<commit_before>/*

Copyright (c) 2007, Arvid Norberg
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 the author 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 OWNER 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.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

			boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
			if (i->is_v4())
			{
				s->open(udp::v4(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
				s->set_option(outbound_interface(i->to_v4()), ec);
				if (ec) continue;
			}
			else
			{
				s->open(udp::v6(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
//				s->set_option(outbound_interface(i->to_v6()), ec);
//				if (ec) continue;
			}
			s->set_option(hops(255), ec);
			if (ec) continue;
			s->set_option(enable_loopback(loopback), ec);
			if (ec) continue;
			m_sockets.push_back(socket_entry(s));
			socket_entry& se = m_sockets.back();
			s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
				, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
		}
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << std::endl;
#endif
			if (e) ec = e;
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		m_on_receive.clear();

		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			i->socket->close();
		}
	}
}

<commit_msg>fixed potential call to empty boost.function<commit_after>/*

Copyright (c) 2007, Arvid Norberg
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 the author 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 OWNER 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.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

			boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
			if (i->is_v4())
			{
				s->open(udp::v4(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
				s->set_option(outbound_interface(i->to_v4()), ec);
				if (ec) continue;
			}
			else
			{
				s->open(udp::v6(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
//				s->set_option(outbound_interface(i->to_v6()), ec);
//				if (ec) continue;
			}
			s->set_option(hops(255), ec);
			if (ec) continue;
			s->set_option(enable_loopback(loopback), ec);
			if (ec) continue;
			m_sockets.push_back(socket_entry(s));
			socket_entry& se = m_sockets.back();
			s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
				, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
		}
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << std::endl;
#endif
			if (e) ec = e;
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		m_on_receive.clear();

		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			i->socket->close();
		}
	}
}

<|endoftext|>


<commit_before>#include "project.hpp"

#include <bacs/system/process.hpp>

#include <yandex/contest/system/unistd/Operations.hpp>

#include <bunsan/filesystem/fstream.hpp>
#include <bunsan/utility/archiver.hpp>
#include <bunsan/utility/resolver.hpp>

#include <boost/assert.hpp>
#include <boost/filesystem/operations.hpp>

namespace bacs{namespace system{namespace builders
{
    static const boost::filesystem::path project_path =
        "/builders_project";

    static const boost::filesystem::path source_path = "source";
    static const boost::filesystem::path source_archive_path = "source_archive";

    static bool extract(
        const bacs::process::Source &source,
        const boost::filesystem::path &root,
        const unistd::access::Id &owner_id,
        std::string &error)
    {
        using namespace bunsan::utility;
        using boost::filesystem::recursive_directory_iterator;

        if (!source.has_archiver())
            BOOST_THROW_EXCEPTION(
                incompatible_builder_error() <<
                incompatible_builder_error::message(
                    "Directory tree source is required"));

        boost::property_tree::ptree config;
        if (source.archiver().has_format())
            config.put("format", source.archiver().format());
        resolver rs;
        const archiver_ptr ar =
            archiver::instance(source.archiver().type(), rs);
        ar->setup(config);

        try
        {
            bunsan::filesystem::ofstream fout(root / source_archive_path);
            BUNSAN_FILESYSTEM_FSTREAM_WRAP_BEGIN(fout)
            {
                fout << source.data();
            }
            BUNSAN_FILESYSTEM_FSTREAM_WRAP_END(fout)
            fout.close();

            ar->unpack(
                root / source_archive_path,
                root / source_path
            );

            for (recursive_directory_iterator i(root / source_path),
                                              end;
                 i != end;
                 ++i)
            {
                unistd::lchown(i->path(), owner_id);
            }
        }
        catch (std::exception &e)
        {
            error = e.what();
            return false;
        }
        return true;
    }

    executable_ptr project::build(
        const ContainerPointer &container,
        const unistd::access::Id &owner_id,
        const bacs::process::Source &source,
        const bacs::process::ResourceLimits &resource_limits,
        bacs::process::BuildResult &result)
    {
        const boost::filesystem::path solutions =
            container->filesystem().keepInRoot(project_path);
        boost::filesystem::create_directories(solutions);
        bunsan::tempfile tmpdir =
            bunsan::tempfile::directory_in_directory(solutions);
        container->filesystem().setOwnerId(
            project_path / tmpdir.path().filename(), owner_id);
        BOOST_VERIFY(boost::filesystem::create_directory(
            tmpdir.path() / source_path
        ));
        std::string error;
        if (!extract(source, tmpdir.path(), owner_id, error))
        {
            result.set_status(bacs::process::BuildResult::FAILED);
            result.set_output(error);
            return executable_ptr();
        }
        return build_extracted(
            container,
            owner_id,
            std::move(tmpdir),
            project_path / tmpdir.path().filename() / source_path,
            resource_limits,
            result
        );
    }

    project_executable::project_executable(
        const ContainerPointer &container,
        bunsan::tempfile &&tmpdir,
        const boost::filesystem::path &executable):
            m_container(container),
            m_tmpdir(std::move(tmpdir)),
            m_executable(executable) {}

    ProcessPointer project_executable::create(
        const ProcessGroupPointer &process_group,
        const ProcessArguments &arguments)
    {
        const ProcessPointer process =
            process_group->createProcess(executable());
        process->setArguments(process->executable(), arguments);
        return process;
    }

    ContainerPointer project_executable::container()
    {
        return m_container;
    }

    boost::filesystem::path project_executable::dir() const
    {
        return project_path / m_tmpdir.path().filename();
    }

    boost::filesystem::path project_executable::source() const
    {
        return dir() / source_path;
    }

    boost::filesystem::path project_executable::executable() const
    {
        return dir() / m_executable;
    }
}}}
<commit_msg>builders::project::build() fix.<commit_after>#include "project.hpp"

#include <bacs/system/process.hpp>

#include <yandex/contest/system/unistd/Operations.hpp>

#include <bunsan/filesystem/fstream.hpp>
#include <bunsan/utility/archiver.hpp>
#include <bunsan/utility/resolver.hpp>

#include <boost/assert.hpp>
#include <boost/filesystem/operations.hpp>

namespace bacs{namespace system{namespace builders
{
    static const boost::filesystem::path project_path =
        "/builders_project";

    static const boost::filesystem::path source_path = "source";
    static const boost::filesystem::path source_archive_path = "source_archive";

    using boost::filesystem::recursive_directory_iterator;

    static bool extract(
        const bacs::process::Source &source,
        const boost::filesystem::path &root,
        std::string &error)
    {
        using namespace bunsan::utility;

        if (!source.has_archiver())
            BOOST_THROW_EXCEPTION(
                incompatible_builder_error() <<
                incompatible_builder_error::message(
                    "Directory tree source is required"));

        boost::property_tree::ptree config;
        if (source.archiver().has_format())
            config.put("format", source.archiver().format());
        resolver rs;
        const archiver_ptr ar =
            archiver::instance(source.archiver().type(), rs);
        ar->setup(config);

        try
        {
            bunsan::filesystem::ofstream fout(root / source_archive_path);
            BUNSAN_FILESYSTEM_FSTREAM_WRAP_BEGIN(fout)
            {
                fout << source.data();
            }
            BUNSAN_FILESYSTEM_FSTREAM_WRAP_END(fout)
            fout.close();

            ar->unpack(
                root / source_archive_path,
                root / source_path
            );
        }
        catch (std::exception &e)
        {
            error = e.what();
            return false;
        }
        return true;
    }

    static void pre_build(
        const ContainerPointer &container,
        const unistd::access::Id &owner_id,
        const boost::filesystem::path &root)
    {
        for (recursive_directory_iterator i(root / source_path),
                                          end;
             i != end;
             ++i)
        {
            unistd::lchown(i->path(), owner_id);
        }
        BOOST_VERIFY(boost::filesystem::create_directory(
            container->filesystem().keepInRoot("/tmp")));
        container->filesystem().setMode("/tmp", 01777);
    }

    static void post_build(
        const ContainerPointer &container,
        const unistd::access::Id &/*owner_id*/,
        const boost::filesystem::path &root)
    {
        for (recursive_directory_iterator i(root / source_path),
                                          end;
             i != end;
             ++i)
        {
            unistd::lchown(i->path(), unistd::access::Id{0, 0});
            boost::filesystem::permissions(
                *i,
                boost::filesystem::remove_perms &
                boost::filesystem::owner_write &
                boost::filesystem::group_write &
                boost::filesystem::others_write
            );
        }
        boost::filesystem::remove_all(
            container->filesystem().keepInRoot("/tmp"));
    }

    executable_ptr project::build(
        const ContainerPointer &container,
        const unistd::access::Id &owner_id,
        const bacs::process::Source &source,
        const bacs::process::ResourceLimits &resource_limits,
        bacs::process::BuildResult &result)
    {
        const boost::filesystem::path solutions =
            container->filesystem().keepInRoot(project_path);
        boost::filesystem::create_directories(solutions);
        bunsan::tempfile tmpdir =
            bunsan::tempfile::directory_in_directory(solutions);
        container->filesystem().setOwnerId(
            project_path / tmpdir.path().filename(), owner_id);
        BOOST_VERIFY(boost::filesystem::create_directory(
            tmpdir.path() / source_path
        ));

        std::string error;
        if (!extract(source, tmpdir.path(), error))
        {
            result.set_status(bacs::process::BuildResult::FAILED);
            result.set_output(error);
            return executable_ptr();
        }

        pre_build(container, owner_id, tmpdir.path());
        const executable_ptr exe = build_extracted(
            container,
            owner_id,
            std::move(tmpdir),
            project_path / tmpdir.path().filename() / source_path,
            resource_limits,
            result
        );
        post_build(container, owner_id, tmpdir.path());
        return exe;
    }

    project_executable::project_executable(
        const ContainerPointer &container,
        bunsan::tempfile &&tmpdir,
        const boost::filesystem::path &executable):
            m_container(container),
            m_tmpdir(std::move(tmpdir)),
            m_executable(executable) {}

    ProcessPointer project_executable::create(
        const ProcessGroupPointer &process_group,
        const ProcessArguments &arguments)
    {
        const ProcessPointer process =
            process_group->createProcess(executable());
        process->setArguments(process->executable(), arguments);
        return process;
    }

    ContainerPointer project_executable::container()
    {
        return m_container;
    }

    boost::filesystem::path project_executable::dir() const
    {
        return project_path / m_tmpdir.path().filename();
    }

    boost::filesystem::path project_executable::source() const
    {
        return dir() / source_path;
    }

    boost::filesystem::path project_executable::executable() const
    {
        return dir() / m_executable;
    }
}}}
<|endoftext|>


<commit_before>/*
 * Copyright 2008, 2009 Google Inc.
 * Copyright 2007 Nintendo Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cplusplus.h"

class Cxx : public CPlusPlus
{
public:
    Cxx(FILE* file, const char* stringTypeName = "char*", bool useExceptions = true) :
        CPlusPlus(file, stringTypeName, useExceptions)
    {
    }

    virtual void at(const StructType* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n%s", node->getJavadoc().c_str(), indentString.c_str());
        }
        write("struct %s", node->getName().c_str());
        if (!node->isLeaf())
        {
            write("\n%s{\n", indentString.c_str());
            indent();
            printChildren(node);
            unindent();
            write("%s}", indentString.c_str());
        }
    }

    virtual void at(const ExceptDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n%s", node->getJavadoc().c_str(), indentString.c_str());
        }
        write("struct %s", node->getName().c_str());
        write("\n%s{\n", indentString.c_str());
        indent();
        printChildren(node);
        unindent();
        write("%s}", indentString.c_str());
    }

    virtual void at(const Interface* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }
        write("class %s", node->getName().c_str());
        if (node->getExtends())
        {
            write(" : ");
            prefix = "public ";
            node->getExtends()->accept(this);
            prefix = "";
        }
        if (!node->isLeaf())
        {
            writeln("");
            writeln("{");
            writeln("public:");
            indent();

            // printChildren(node);
            int count = 0;
            for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
            {
                if ((*i)->isSequence(node) || (*i)->isNative(node))
                {
                    continue;
                }

                optionalStage = 0;
                do
                {
#ifdef USE_FUNCTION_CALLBACK
                    callbackStage = 0;
                    do
                    {
                        if (0 < count)
                        {
                            write(";\n");
                        }

                        optionalCount = 0;
                        callbackCount = 0;
                        ++count;
                        writetab();
                        (*i)->accept(this);
                        ++callbackStage;
                    } while (callbackStage < (1u << callbackCount));
#else  // USE_FUNCTION_CALLBACK
                    if (0 < count)
                    {
                        write(";\n");
                    }
                    optionalCount = 0;
                    ++count;
                    writetab();
                    (*i)->accept(this);
#endif  // USE_FUNCTION_CALLBACK
                    ++optionalStage;
                } while (optionalStage <= optionalCount);
            }
            if (0 < count)
            {
                write(";\n");
            }

            writeln("static const char* iid()");
            writeln("{");
            indent();
                writeln("static const char* name = \"%s\";",
                        node->getQualifiedName().c_str());
                writeln("return name;");
            unindent();
            writeln("}");

            if (Interface* constructor = node->getConstructor())
            {
                // Process constructors.
                constructorMode = true;
                for (NodeList::iterator i = constructor->begin();
                     i != constructor->end();
                     ++i)
                {
                    writetab();
                    (*i)->accept(this);
                }
                constructorMode = false;
                writeln("static Constructor* getConstructor()");
                writeln("{");
                indent();
                    writeln("return constructor;");
                unindent();
                writeln("}");
                writeln("static void setConstructor(Constructor* ctor)");
                writeln("{");
                indent();
                    writeln("constructor = ctor;");
                unindent();
                writeln("}");
                unindent();
                writeln("private:");
                indent();
                writeln("static Constructor* constructor;");
            }

            unindent();
            writetab();
            write("}");
        }

        if (node->getConstructor())
        {
            write(";\n\n");
            writetab();
            write("%s::Constructor* %s::constructor __attribute__((weak))",
                  node->getName().c_str(), node->getName().c_str());
        }
    }

    virtual void at(const NativeType* node)
    {
        if (node->getName() == "void_pointer")
        {
            write("void*");
        }
        else
        {
            write("%s", node->getName().c_str());
        }
    }

    virtual void at(const BinaryExpr* node)
    {
        node->getLeft()->accept(this);
        write(" %s ", node->getName().c_str());
        node->getRight()->accept(this);
    }

    virtual void at(const UnaryExpr* node)
    {
        write("%s", node->getName().c_str());
        NodeList::iterator elem = node->begin();
        (*elem)->accept(this);
    }

    virtual void at(const GroupingExpression* node)
    {
        write("(");
        NodeList::iterator elem = node->begin();
        (*elem)->accept(this);
        write(")");
    }

    virtual void at(const Literal* node)
    {
        if (node->getName() == "TRUE")
        {
            write("true");
        }
        else if (node->getName() == "FALSE")
        {
            write("false");
        }
        else
        {
            write("%s", node->getName().c_str());
        }
    }

    virtual void at(const Member* node)
    {
        if (node->isTypedef())
        {
            write("typedef ");
        }
        if (node->isInterface(node->getParent()))
        {
            node->getSpec()->accept(this);
            write(" %s", node->getName().c_str());
        }
        else
        {
            node->getSpec()->accept(this);
            write(" %s", node->getName().c_str());
        }
    }

    virtual void at(const ArrayDcl* node)
    {
        assert(!node->isLeaf());

        at(static_cast<const Member*>(node));
        for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
        {
            write("[");
            (*i)->accept(this);
            write("]");
        }
    }

    virtual void at(const ConstDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }
        write("static const ");
        at(static_cast<const Member*>(node));
        write(" = ");
        node->getExp()->accept(this);
    }

    virtual void at(const Attribute* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }

        // getter
        CPlusPlus::getter(node);
        write(" = 0");

        if (!node->isReadonly() || node->isPutForwards() || node->isReplaceable())
        {
            // setter
            write(";\n");
            writetab();
            CPlusPlus::setter(node);
            write(" = 0");
        }
    }

    virtual void at(const OpDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }

        CPlusPlus::at(node);

        if (!constructorMode)
        {
            write(" = 0");
        }
        else
        {
            writeln("");
            writeln("{");
            indent();
                writeln("if (constructor)");
                indent();
                    writetab();
                    write("constructor->createInstance(");
                    for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
                    {
                        if (i != node->begin())
                        {
                            write(", ");
                        }
                        write("%s", (*i)->getName().c_str());
                    }
                    write(");");
                    writeln("");
                unindent();
            unindent();
            writeln("}");
        }
    }
};

class Import : public Visitor
{
    FILE* file;
    bool printed;

public:
    Import(FILE* file) :
        file(file),
        printed(false)
    {
    }

    virtual void at(const Node* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        visitChildren(node);
    }

    virtual void at(const Include* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        if (node->isSystem())
        {
            fprintf(file, "#include <%s>\n", getOutputFilename(node->getName().c_str(), "h").c_str());
        }
        else
        {
            fprintf(file, "#include \"%s\"\n", getOutputFilename(node->getName().c_str(), "h").c_str());
        }
        printed = true;
    }

    bool hasPrinted() const
    {
        return printed;
    }
};

class Predeclaration : public Visitor
{
    FILE* file;

public:
    Predeclaration(FILE* file) :
        file(file)
    {
    }

    virtual void at(const Node* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        visitChildren(node);
    }

    virtual void at(const Module* node)
    {
        if (!node->hasPredeclarations())
        {
            return;
        }
        if (0 < node->getName().size())
        {
            fprintf(file, "namespace %s\n", node->getName().c_str());
            fprintf(file, "{\n");
                visitChildren(node);
            fprintf(file, "}\n\n");
        }
        else
        {
            visitChildren(node);
        }
    }

    virtual void at(const Interface* node)
    {
        if (1 < node->getRank() || !node->isLeaf())
        {
            return;
        }
        fprintf(file, "    class %s;\n", node->getName().c_str());
    }
};

void printCxx(const std::string& filename, const char* stringTypeName, bool useExceptions)
{
    printf("# %s\n", filename.c_str());

    FILE* file = fopen(filename.c_str(), "w");
    if (!file)
    {
        return;
    }

    std::string included = getIncludedName(filename);
    fprintf(file, "// Generated by esidl %s.\n\n", VERSION);
    fprintf(file, "#ifndef %s\n", included.c_str());
    fprintf(file, "#define %s\n\n", included.c_str());

    Import import(file);
    getSpecification()->accept(&import);
    if (import.hasPrinted())
    {
        fprintf(file, "\n");
    }

    if (!Node::getFlatNamespace())
    {
        Predeclaration predeclaration(file);
        getSpecification()->accept(&predeclaration);
    }

    Cxx cxx(file, stringTypeName, useExceptions);
    getSpecification()->accept(&cxx);

    fprintf(file, "#endif  // %s\n", included.c_str());

    fclose(file);
}
<commit_msg>(Cxx::at(const Interface*)) : Generate 'name' as a const pointer.<commit_after>/*
 * Copyright 2008, 2009 Google Inc.
 * Copyright 2007 Nintendo Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cplusplus.h"

class Cxx : public CPlusPlus
{
public:
    Cxx(FILE* file, const char* stringTypeName = "char*", bool useExceptions = true) :
        CPlusPlus(file, stringTypeName, useExceptions)
    {
    }

    virtual void at(const StructType* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n%s", node->getJavadoc().c_str(), indentString.c_str());
        }
        write("struct %s", node->getName().c_str());
        if (!node->isLeaf())
        {
            write("\n%s{\n", indentString.c_str());
            indent();
            printChildren(node);
            unindent();
            write("%s}", indentString.c_str());
        }
    }

    virtual void at(const ExceptDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n%s", node->getJavadoc().c_str(), indentString.c_str());
        }
        write("struct %s", node->getName().c_str());
        write("\n%s{\n", indentString.c_str());
        indent();
        printChildren(node);
        unindent();
        write("%s}", indentString.c_str());
    }

    virtual void at(const Interface* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }
        write("class %s", node->getName().c_str());
        if (node->getExtends())
        {
            write(" : ");
            prefix = "public ";
            node->getExtends()->accept(this);
            prefix = "";
        }
        if (!node->isLeaf())
        {
            writeln("");
            writeln("{");
            writeln("public:");
            indent();

            // printChildren(node);
            int count = 0;
            for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
            {
                if ((*i)->isSequence(node) || (*i)->isNative(node))
                {
                    continue;
                }

                optionalStage = 0;
                do
                {
#ifdef USE_FUNCTION_CALLBACK
                    callbackStage = 0;
                    do
                    {
                        if (0 < count)
                        {
                            write(";\n");
                        }

                        optionalCount = 0;
                        callbackCount = 0;
                        ++count;
                        writetab();
                        (*i)->accept(this);
                        ++callbackStage;
                    } while (callbackStage < (1u << callbackCount));
#else  // USE_FUNCTION_CALLBACK
                    if (0 < count)
                    {
                        write(";\n");
                    }
                    optionalCount = 0;
                    ++count;
                    writetab();
                    (*i)->accept(this);
#endif  // USE_FUNCTION_CALLBACK
                    ++optionalStage;
                } while (optionalStage <= optionalCount);
            }
            if (0 < count)
            {
                write(";\n");
            }

            writeln("static const char* iid()");
            writeln("{");
            indent();
                writeln("static const char* const name = \"%s\";",
                        node->getQualifiedName().c_str());
                writeln("return name;");
            unindent();
            writeln("}");

            if (Interface* constructor = node->getConstructor())
            {
                // Process constructors.
                constructorMode = true;
                for (NodeList::iterator i = constructor->begin();
                     i != constructor->end();
                     ++i)
                {
                    writetab();
                    (*i)->accept(this);
                }
                constructorMode = false;
                writeln("static Constructor* getConstructor()");
                writeln("{");
                indent();
                    writeln("return constructor;");
                unindent();
                writeln("}");
                writeln("static void setConstructor(Constructor* ctor)");
                writeln("{");
                indent();
                    writeln("constructor = ctor;");
                unindent();
                writeln("}");
                unindent();
                writeln("private:");
                indent();
                writeln("static Constructor* constructor;");
            }

            unindent();
            writetab();
            write("}");
        }

        if (node->getConstructor())
        {
            write(";\n\n");
            writetab();
            write("%s::Constructor* %s::constructor __attribute__((weak))",
                  node->getName().c_str(), node->getName().c_str());
        }
    }

    virtual void at(const NativeType* node)
    {
        if (node->getName() == "void_pointer")
        {
            write("void*");
        }
        else
        {
            write("%s", node->getName().c_str());
        }
    }

    virtual void at(const BinaryExpr* node)
    {
        node->getLeft()->accept(this);
        write(" %s ", node->getName().c_str());
        node->getRight()->accept(this);
    }

    virtual void at(const UnaryExpr* node)
    {
        write("%s", node->getName().c_str());
        NodeList::iterator elem = node->begin();
        (*elem)->accept(this);
    }

    virtual void at(const GroupingExpression* node)
    {
        write("(");
        NodeList::iterator elem = node->begin();
        (*elem)->accept(this);
        write(")");
    }

    virtual void at(const Literal* node)
    {
        if (node->getName() == "TRUE")
        {
            write("true");
        }
        else if (node->getName() == "FALSE")
        {
            write("false");
        }
        else
        {
            write("%s", node->getName().c_str());
        }
    }

    virtual void at(const Member* node)
    {
        if (node->isTypedef())
        {
            write("typedef ");
        }
        if (node->isInterface(node->getParent()))
        {
            node->getSpec()->accept(this);
            write(" %s", node->getName().c_str());
        }
        else
        {
            node->getSpec()->accept(this);
            write(" %s", node->getName().c_str());
        }
    }

    virtual void at(const ArrayDcl* node)
    {
        assert(!node->isLeaf());

        at(static_cast<const Member*>(node));
        for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
        {
            write("[");
            (*i)->accept(this);
            write("]");
        }
    }

    virtual void at(const ConstDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }
        write("static const ");
        at(static_cast<const Member*>(node));
        write(" = ");
        node->getExp()->accept(this);
    }

    virtual void at(const Attribute* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }

        // getter
        CPlusPlus::getter(node);
        write(" = 0");

        if (!node->isReadonly() || node->isPutForwards() || node->isReplaceable())
        {
            // setter
            write(";\n");
            writetab();
            CPlusPlus::setter(node);
            write(" = 0");
        }
    }

    virtual void at(const OpDcl* node)
    {
        if (node->getJavadoc().size())
        {
            write("%s\n", node->getJavadoc().c_str());
            writetab();
        }

        CPlusPlus::at(node);

        if (!constructorMode)
        {
            write(" = 0");
        }
        else
        {
            writeln("");
            writeln("{");
            indent();
                writeln("if (constructor)");
                indent();
                    writetab();
                    write("constructor->createInstance(");
                    for (NodeList::iterator i = node->begin(); i != node->end(); ++i)
                    {
                        if (i != node->begin())
                        {
                            write(", ");
                        }
                        write("%s", (*i)->getName().c_str());
                    }
                    write(");");
                    writeln("");
                unindent();
            unindent();
            writeln("}");
        }
    }
};

class Import : public Visitor
{
    FILE* file;
    bool printed;

public:
    Import(FILE* file) :
        file(file),
        printed(false)
    {
    }

    virtual void at(const Node* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        visitChildren(node);
    }

    virtual void at(const Include* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        if (node->isSystem())
        {
            fprintf(file, "#include <%s>\n", getOutputFilename(node->getName().c_str(), "h").c_str());
        }
        else
        {
            fprintf(file, "#include \"%s\"\n", getOutputFilename(node->getName().c_str(), "h").c_str());
        }
        printed = true;
    }

    bool hasPrinted() const
    {
        return printed;
    }
};

class Predeclaration : public Visitor
{
    FILE* file;

public:
    Predeclaration(FILE* file) :
        file(file)
    {
    }

    virtual void at(const Node* node)
    {
        if (1 < node->getRank())
        {
            return;
        }
        visitChildren(node);
    }

    virtual void at(const Module* node)
    {
        if (!node->hasPredeclarations())
        {
            return;
        }
        if (0 < node->getName().size())
        {
            fprintf(file, "namespace %s\n", node->getName().c_str());
            fprintf(file, "{\n");
                visitChildren(node);
            fprintf(file, "}\n\n");
        }
        else
        {
            visitChildren(node);
        }
    }

    virtual void at(const Interface* node)
    {
        if (1 < node->getRank() || !node->isLeaf())
        {
            return;
        }
        fprintf(file, "    class %s;\n", node->getName().c_str());
    }
};

void printCxx(const std::string& filename, const char* stringTypeName, bool useExceptions)
{
    printf("# %s\n", filename.c_str());

    FILE* file = fopen(filename.c_str(), "w");
    if (!file)
    {
        return;
    }

    std::string included = getIncludedName(filename);
    fprintf(file, "// Generated by esidl %s.\n\n", VERSION);
    fprintf(file, "#ifndef %s\n", included.c_str());
    fprintf(file, "#define %s\n\n", included.c_str());

    Import import(file);
    getSpecification()->accept(&import);
    if (import.hasPrinted())
    {
        fprintf(file, "\n");
    }

    if (!Node::getFlatNamespace())
    {
        Predeclaration predeclaration(file);
        getSpecification()->accept(&predeclaration);
    }

    Cxx cxx(file, stringTypeName, useExceptions);
    getSpecification()->accept(&cxx);

    fprintf(file, "#endif  // %s\n", included.c_str());

    fclose(file);
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993-2011 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "FlagHistory.h"

/* system headers */
#include <vector>
#include <string>

#define MAX_FLAG_HISTORY (10)

void FlagHistory::clear() {
  flagHistory.clear();
}

std::string FlagHistory::getStr() {
  std::string flagList;
  std::vector<FlagType*>::iterator fhIt = flagHistory.begin();

  while (fhIt != flagHistory.end()) {
    flagList += " (";
    FlagType * fDesc = (FlagType*)(*fhIt);
    if (fDesc->endurance == FlagNormal) {
      flagList += '*';
      flagList += fDesc->flagName.c_str()[0];
    } else
      flagList += fDesc->flagAbbv;
    flagList += ")";
    ++fhIt;
  }
  return flagList;
}

void FlagHistory::add(FlagType* type) {
  if (flagHistory.size() >= MAX_FLAG_HISTORY)
    flagHistory.erase(flagHistory.begin());
  flagHistory.push_back(type);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>(performance) Prefer prefix ++/-- operators for non-primitive types.<commit_after>/* bzflag
 * Copyright (c) 1993-2011 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "FlagHistory.h"

/* system headers */
#include <vector>
#include <string>

#define MAX_FLAG_HISTORY (10)

void FlagHistory::clear() {
  flagHistory.clear();
}

std::string FlagHistory::getStr() {
  std::string flagList;
  std::vector<FlagType*>::iterator fhIt = flagHistory.begin();

  while (fhIt != flagHistory.end()) {
    flagList += " (";
    FlagType * fDesc = (FlagType*)(*fhIt);
    if (fDesc->endurance == FlagNormal) {
      flagList += '*';
      flagList += fDesc->flagName.c_str()[0];
    } else
      flagList += fDesc->flagAbbv;
    flagList += ")";
    fhIt++;
  }
  return flagList;
}

void FlagHistory::add(FlagType* type) {
  if (flagHistory.size() >= MAX_FLAG_HISTORY)
    flagHistory.erase(flagHistory.begin());
  flagHistory.push_back(type);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#ifndef KEYBOARDMANAGER_HPP
#define KEYBOARDMANAGER_HPP

#include <map>
#include <string>
#include <functional>

#include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp>

namespace swift
{
	class KeyboardManager
	{
		public:
			KeyboardManager() {};
			~KeyboardManager() {};

			void newBinding(const std::string& n, sf::Keyboard::Key k, std::function<void()> f = [](){return true;}, bool onPress = false)
			{
				bindings.emplace(std::make_pair(n, KeyBinding(k, f, onPress)));
			}

			bool operator()(sf::Event& e)
			{
				for(auto &k : bindings)
				{
					if(k.second(e))
						return k.second.call();
				}

				return false;
			}

		private:
			class KeyBinding
			{
				public:
					explicit KeyBinding(sf::Keyboard::Key k, std::function<void()> f, bool p)
					{
						key = k;
						onPress = p;
						func = f;
					}

					~KeyBinding()
					{
					}

					sf::Keyboard::Key getKey() const
					{
						return key;
					}

					bool operator()(sf::Event& e)
					{
						if(e.key.code == key)
						{
							if(onPress)
							{
								if(e.type == sf::Event::KeyPressed)
								{
									return true;
								}
							}
							else
							{
								if(e.type == sf::Event::KeyReleased)
								{
									return true;
								}
							}
						}
						return false;
					}

					bool call()
					{
						if(!func)
							return false;

						func();

						return true;
					}

				private:
					sf::Keyboard::Key key;

					std::function<void()> func;

					bool onPress;	// if true, means if key is pressed, if false, means if key is released
			};

			std::map<std::string, KeyBinding> bindings;
	};
}

#endif // KEYBOARDMANAGER_HPP
<commit_msg>Added call function.<commit_after>#ifndef KEYBOARDMANAGER_HPP
#define KEYBOARDMANAGER_HPP

#include <map>
#include <string>
#include <functional>

#include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp>

namespace swift
{
	class KeyboardManager
	{
		public:
			KeyboardManager() {};
			~KeyboardManager() {};

			void newBinding(const std::string& n, sf::Keyboard::Key k, std::function<void()> f = [](){return true;}, bool onPress = false)
			{
				bindings.emplace(std::make_pair(n, KeyBinding(k, f, onPress)));
			}

			void call(const std::string& k)
			{
				bindings.at(k).call();
			}

			bool operator()(sf::Event& e)
			{
				for(auto &k : bindings)
				{
					if(k.second(e))
						return k.second.call();
				}

				return false;
			}

		private:
			class KeyBinding
			{
				public:
					explicit KeyBinding(sf::Keyboard::Key k, std::function<void()> f, bool p)
					{
						key = k;
						onPress = p;
						func = f;
					}

					~KeyBinding()
					{
					}

					sf::Keyboard::Key getKey() const
					{
						return key;
					}

					bool operator()(sf::Event& e)
					{
						return ((e.type == sf::Event::KeyPressed && onPress) || (e.type == sf::Event::KeyReleased && !onPress)) && e.key.code == key;
					}

					bool call()
					{
						if(!func)
							return false;

						func();

						return true;
					}

				private:
					sf::Keyboard::Key key;

					std::function<void()> func;

					bool onPress;	// if true, means if key is pressed, if false, means if key is released
			};

			std::map<std::string, KeyBinding> bindings;
	};
}

#endif // KEYBOARDMANAGER_HPP
<|endoftext|>


<commit_before>/* bzflag
* Copyright (c) 1993 - 2005 Tim Riker
*
* This package is free software;  you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <string>
#include <vector>
#include "bzfio.h"
#include "common.h"
#include "TextUtils.h"
#include <vector>

#include "commands.h"
#include "bzfsAPI.h"

typedef struct
{
	std::string plugin;

#ifdef _WIN32
	HINSTANCE	handle;
#else
	void*		handle;
#endif
}trPluginRecord;

std::vector<trPluginRecord>	vPluginList;

void unload1Plugin ( int iPluginID );

#ifdef _WIN32
#include <windows.h>

int getPluginVersion ( HINSTANCE hLib )
{
	int (*lpProc)(void);
	lpProc = (int (__cdecl *)(void))GetProcAddress(hLib, "bz_GetVersion");
	if (lpProc)
		return lpProc();
	return 0;
}

void loadPlugin ( std::string plugin, std::string config )
{
	int (*lpProc)(const char*);

	HINSTANCE	hLib = LoadLibrary(plugin.c_str());
	if (hLib)
	{
		if (getPluginVersion(hLib) < BZ_API_VERSION)
		{
			DEBUG1("Plugin:%s found but expects an older API version (%d), upgrade it\n",plugin.c_str(),getPluginVersion(hLib));
			FreeLibrary(hLib);
		}
		else
		{
			lpProc = (int (__cdecl *)(const char*))GetProcAddress(hLib, "bz_Load");
			if (lpProc)
			{
				int ret =lpProc(config.c_str());
				DEBUG1("Plugin:%s loaded\n",plugin.c_str());

				trPluginRecord pluginRecord;
				pluginRecord.handle = hLib;
				pluginRecord.plugin = plugin;
				vPluginList.push_back(pluginRecord);
			}
			else
			{
				DEBUG1("Plugin:%s found but does not contain bz_Load method\n",plugin.c_str());
				FreeLibrary(hLib);
			}
		}
	}
	else
		DEBUG1("Plugin:%s not found\n",plugin.c_str());
}

void unload1Plugin ( int iPluginID )
{
	int (*lpProc)(void);

	trPluginRecord &plugin = vPluginList[iPluginID];

	lpProc = (int (__cdecl *)(void))GetProcAddress(plugin.handle, "bz_Unload");
	if (lpProc)
		int ret =lpProc();
	else
		DEBUG1("Plugin does not contain bz_UnLoad method\n");

	FreeLibrary(plugin.handle);
	plugin.handle = NULL;
}
#else

#include <dlfcn.h>
std::vector<void*>	vLibHandles;

int getPluginVersion ( void* hLib )
{
	int (*lpProc)(void);
	*(void**) &lpProc = dlsym(hLib,"bz_GetVersion");
	if (lpProc)
		return (*lpProc)();
	return 0;
}

void loadPlugin ( std::string plugin, std::string config )
{
	int (*lpProc)(const char*);

	void*	hLib = dlopen(plugin.c_str(),RTLD_LAZY);
	if (hLib)
	{
		if (getPluginVersion(hLib) < BZ_API_VERSION)
		{
			DEBUG1("Plugin:%s found but expects an older API version (%d), upgrade it\n",plugin.c_str(),getPluginVersion(hLib));
			dlclose(hLib);
		}
		else
		{
			*(void**) &lpProc = dlsym(hLib,"bz_Load");
			if (lpProc)
			{
				(*lpProc)(config.c_str());
				DEBUG1("Plugin:%s loaded\n",plugin.c_str());
				trPluginRecord pluginRecord;
				pluginRecord.handle = hLib;
				pluginRecord.plugin = plugin;
				vPluginList.push_back(pluginRecord);
			}
			else
			{
				DEBUG1("Plugin:%s found but does not contain bz_Load method, error %s\n",plugin.c_str(),dlerror());
				dlclose(hLib);
			}
		}
	}
	else
		DEBUG1("Plugin:%s not found, error %s\n",plugin.c_str(), dlerror());
}

void unload1Plugin ( int iPluginID )
{
	int (*lpProc)(void);
	trPluginRecord &plugin = vPluginList[iPluginID];

	*(void**) &lpProc = dlsym(plugin.handle, "bz_Unload");
	if (lpProc)
		(*lpProc)();
	else
		DEBUG1("Plugin does not contain bz_UnLoad method, error %s\n",dlerror());

	dlclose(plugin.handle);
	plugin.handle = NULL;
}
#endif

void unloadPlugin ( std::string plugin )
{
	// unload the first one of the name we find
	for (unsigned int i = 0; i < vPluginList.size();i++)
	{
		if ( vPluginList[i].plugin == plugin )
		{
			unload1Plugin(i);
			vPluginList.erase(vPluginList.begin()+i);
			return;
		}
	}
}

void unloadPlugins ( void )
{
	for (unsigned int i = 0; i < vPluginList.size();i++)
		unload1Plugin(i);
	vPluginList.clear();

	removeCustomSlashCommand("loadplugin");
	removeCustomSlashCommand("unloadplugin");
	removeCustomSlashCommand("listplugins");
}

std::vector<std::string> getPluginList ( void )
{
	std::vector<std::string> plugins;
	for (unsigned int i = 0; i < vPluginList.size();i++)
		plugins.push_back(vPluginList[i].plugin);

	return plugins;
}

void parseServerCommand(const char *message, int dstPlayerId);

class DynamicPluginCommands : public CustomSlashCommandHandler
{
public:
	virtual ~DynamicPluginCommands(){};
	virtual bool handle ( int playerID, std::string command, std::string message )
	{
		bz_PlayerRecord	record;

		bz_getPlayerByIndex(playerID,&record);

		if ( !record.admin )
		{
			bz_sendTextMessage(BZ_SERVER,playerID,"Permision denied: ADMIN");
			return true;
		}

		if ( !message.size() )
		{
			bz_sendTextMessage(BZ_SERVER,playerID,"Error: Command must have a plugin name");
			return true;
		}

		if ( TextUtils::tolower(command) == "loadplugin" )
		{
			std::vector<std::string> params = TextUtils::tokenize(message,std::string(","));

			std::string config;
			if ( params.size() >1)
				config = params[1];

			loadPlugin(params[0],config);

			bz_sendTextMessage(BZ_SERVER,playerID,"Plug-in loaded");
			return true;
		}

		if ( TextUtils::tolower(command) == "unloadplugin" )
		{
			unloadPlugin(message);

			bz_sendTextMessage(BZ_SERVER,playerID,"Plug-in unloaded");
			return true;
		}

		if ( TextUtils::tolower(command) == "listplugins" )
		{
			std::vector<std::string>	plugins = getPluginList();

			if (!plugins.size())
				bz_sendTextMessage(BZ_SERVER,playerID,"No Plug-ins loaded;");
			else
			{
				bz_sendTextMessage(BZ_SERVER,playerID,"Plug-isn loaded;");

				for ( unsigned int i = 0; i < plugins.size(); i++)
					bz_sendTextMessage(BZ_SERVER,playerID,plugins[i].c_str());
			}
			return true;
		}
		return true;
	}
};

DynamicPluginCommands	command;

void initPlugins ( void )
{
	registerCustomSlashCommand("loadplugin",&command);
	registerCustomSlashCommand("unloadplugin",&command);
	registerCustomSlashCommand("listplugins",&command);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Unused vars warning<commit_after>/* bzflag
* Copyright (c) 1993 - 2005 Tim Riker
*
* This package is free software;  you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <string>
#include <vector>
#include "bzfio.h"
#include "common.h"
#include "TextUtils.h"
#include <vector>

#include "commands.h"
#include "bzfsAPI.h"

typedef struct
{
	std::string plugin;

#ifdef _WIN32
	HINSTANCE	handle;
#else
	void*		handle;
#endif
}trPluginRecord;

std::vector<trPluginRecord>	vPluginList;

void unload1Plugin ( int iPluginID );

#ifdef _WIN32
#include <windows.h>

int getPluginVersion ( HINSTANCE hLib )
{
	int (*lpProc)(void);
	lpProc = (int (__cdecl *)(void))GetProcAddress(hLib, "bz_GetVersion");
	if (lpProc)
		return lpProc();
	return 0;
}

void loadPlugin ( std::string plugin, std::string config )
{
	int (*lpProc)(const char*);

	HINSTANCE	hLib = LoadLibrary(plugin.c_str());
	if (hLib)
	{
		if (getPluginVersion(hLib) < BZ_API_VERSION)
		{
			DEBUG1("Plugin:%s found but expects an older API version (%d), upgrade it\n",plugin.c_str(),getPluginVersion(hLib));
			FreeLibrary(hLib);
		}
		else
		{
			lpProc = (int (__cdecl *)(const char*))GetProcAddress(hLib, "bz_Load");
			if (lpProc)
			{
				lpProc(config.c_str());
				DEBUG1("Plugin:%s loaded\n",plugin.c_str());

				trPluginRecord pluginRecord;
				pluginRecord.handle = hLib;
				pluginRecord.plugin = plugin;
				vPluginList.push_back(pluginRecord);
			}
			else
			{
				DEBUG1("Plugin:%s found but does not contain bz_Load method\n",plugin.c_str());
				FreeLibrary(hLib);
			}
		}
	}
	else
		DEBUG1("Plugin:%s not found\n",plugin.c_str());
}

void unload1Plugin ( int iPluginID )
{
	int (*lpProc)(void);

	trPluginRecord &plugin = vPluginList[iPluginID];

	lpProc = (int (__cdecl *)(void))GetProcAddress(plugin.handle, "bz_Unload");
	if (lpProc)
		lpProc();
	else
		DEBUG1("Plugin does not contain bz_UnLoad method\n");

	FreeLibrary(plugin.handle);
	plugin.handle = NULL;
}
#else

#include <dlfcn.h>
std::vector<void*>	vLibHandles;

int getPluginVersion ( void* hLib )
{
	int (*lpProc)(void);
	*(void**) &lpProc = dlsym(hLib,"bz_GetVersion");
	if (lpProc)
		return (*lpProc)();
	return 0;
}

void loadPlugin ( std::string plugin, std::string config )
{
	int (*lpProc)(const char*);

	void*	hLib = dlopen(plugin.c_str(),RTLD_LAZY);
	if (hLib)
	{
		if (getPluginVersion(hLib) < BZ_API_VERSION)
		{
			DEBUG1("Plugin:%s found but expects an older API version (%d), upgrade it\n",plugin.c_str(),getPluginVersion(hLib));
			dlclose(hLib);
		}
		else
		{
			*(void**) &lpProc = dlsym(hLib,"bz_Load");
			if (lpProc)
			{
				(*lpProc)(config.c_str());
				DEBUG1("Plugin:%s loaded\n",plugin.c_str());
				trPluginRecord pluginRecord;
				pluginRecord.handle = hLib;
				pluginRecord.plugin = plugin;
				vPluginList.push_back(pluginRecord);
			}
			else
			{
				DEBUG1("Plugin:%s found but does not contain bz_Load method, error %s\n",plugin.c_str(),dlerror());
				dlclose(hLib);
			}
		}
	}
	else
		DEBUG1("Plugin:%s not found, error %s\n",plugin.c_str(), dlerror());
}

void unload1Plugin ( int iPluginID )
{
	int (*lpProc)(void);
	trPluginRecord &plugin = vPluginList[iPluginID];

	*(void**) &lpProc = dlsym(plugin.handle, "bz_Unload");
	if (lpProc)
		(*lpProc)();
	else
		DEBUG1("Plugin does not contain bz_UnLoad method, error %s\n",dlerror());

	dlclose(plugin.handle);
	plugin.handle = NULL;
}
#endif

void unloadPlugin ( std::string plugin )
{
	// unload the first one of the name we find
	for (unsigned int i = 0; i < vPluginList.size();i++)
	{
		if ( vPluginList[i].plugin == plugin )
		{
			unload1Plugin(i);
			vPluginList.erase(vPluginList.begin()+i);
			return;
		}
	}
}

void unloadPlugins ( void )
{
	for (unsigned int i = 0; i < vPluginList.size();i++)
		unload1Plugin(i);
	vPluginList.clear();

	removeCustomSlashCommand("loadplugin");
	removeCustomSlashCommand("unloadplugin");
	removeCustomSlashCommand("listplugins");
}

std::vector<std::string> getPluginList ( void )
{
	std::vector<std::string> plugins;
	for (unsigned int i = 0; i < vPluginList.size();i++)
		plugins.push_back(vPluginList[i].plugin);

	return plugins;
}

void parseServerCommand(const char *message, int dstPlayerId);

class DynamicPluginCommands : public CustomSlashCommandHandler
{
public:
	virtual ~DynamicPluginCommands(){};
	virtual bool handle ( int playerID, std::string command, std::string message )
	{
		bz_PlayerRecord	record;

		bz_getPlayerByIndex(playerID,&record);

		if ( !record.admin )
		{
			bz_sendTextMessage(BZ_SERVER,playerID,"Permision denied: ADMIN");
			return true;
		}

		if ( !message.size() )
		{
			bz_sendTextMessage(BZ_SERVER,playerID,"Error: Command must have a plugin name");
			return true;
		}

		if ( TextUtils::tolower(command) == "loadplugin" )
		{
			std::vector<std::string> params = TextUtils::tokenize(message,std::string(","));

			std::string config;
			if ( params.size() >1)
				config = params[1];

			loadPlugin(params[0],config);

			bz_sendTextMessage(BZ_SERVER,playerID,"Plug-in loaded");
			return true;
		}

		if ( TextUtils::tolower(command) == "unloadplugin" )
		{
			unloadPlugin(message);

			bz_sendTextMessage(BZ_SERVER,playerID,"Plug-in unloaded");
			return true;
		}

		if ( TextUtils::tolower(command) == "listplugins" )
		{
			std::vector<std::string>	plugins = getPluginList();

			if (!plugins.size())
				bz_sendTextMessage(BZ_SERVER,playerID,"No Plug-ins loaded;");
			else
			{
				bz_sendTextMessage(BZ_SERVER,playerID,"Plug-isn loaded;");

				for ( unsigned int i = 0; i < plugins.size(); i++)
					bz_sendTextMessage(BZ_SERVER,playerID,plugins[i].c_str());
			}
			return true;
		}
		return true;
	}
};

DynamicPluginCommands	command;

void initPlugins ( void )
{
	registerCustomSlashCommand("loadplugin",&command);
	registerCustomSlashCommand("unloadplugin",&command);
	registerCustomSlashCommand("listplugins",&command);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2020 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "config.h"

#include "third_party/cpptoml/include/cpptoml.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/string.h"
#include "xenia/base/string_buffer.h"

std::shared_ptr<cpptoml::table> ParseFile(
    const std::filesystem::path& filename) {
  std::ifstream file(filename);
  if (!file.is_open()) {
    throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
                                   " could not be opened for parsing");
  }
  cpptoml::parser p(file);
  return p.parse();
}

CmdVar(config, "", "Specifies the target config to load.");

DEFINE_uint32(
    defaults_date, 0,
    "Do not modify - internal version of the default values in the config, for "
    "seamless updates if default value of any option is changed.",
    "Config");

namespace config {
std::string config_name = "xenia.config.toml";
std::filesystem::path config_folder;
std::filesystem::path config_path;
std::string game_config_suffix = ".config.toml";

std::shared_ptr<cpptoml::table> ParseConfig(
    const std::filesystem::path& config_path) {
  try {
    return ParseFile(config_path);
  } catch (cpptoml::parse_exception e) {
    xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
                               xe::path_to_utf8(config_path), e.what()));
    return nullptr;
  }
}

void ReadConfig(const std::filesystem::path& file_path,
                bool update_if_no_version_stored) {
  if (!cvar::ConfigVars) {
    return;
  }
  const auto config = ParseConfig(file_path);
  // Loading an actual global config file that exists - if there's no
  // defaults_date in it, it's very old (before updating was added at all, thus
  // all defaults need to be updated).
  auto defaults_date_cvar =
      dynamic_cast<cvar::ConfigVar<uint32_t>*>(cv::cv_defaults_date);
  assert_not_null(defaults_date_cvar);
  defaults_date_cvar->SetConfigValue(0);
  for (auto& it : *cvar::ConfigVars) {
    auto config_var = static_cast<cvar::IConfigVar*>(it.second);
    auto config_key = config_var->category() + "." + config_var->name();
    if (config->contains_qualified(config_key)) {
      config_var->LoadConfigValue(config->get_qualified(config_key));
    }
  }
  uint32_t config_defaults_date = defaults_date_cvar->GetTypedConfigValue();
  if (update_if_no_version_stored || config_defaults_date) {
    cvar::IConfigVarUpdate::ApplyUpdates(config_defaults_date);
  }
  XELOGI("Loaded config: {}", xe::path_to_utf8(file_path));
}

void ReadGameConfig(const std::filesystem::path& file_path) {
  if (!cvar::ConfigVars) {
    return;
  }
  const auto config = ParseConfig(file_path);
  for (auto& it : *cvar::ConfigVars) {
    auto config_var = static_cast<cvar::IConfigVar*>(it.second);
    auto config_key = config_var->category() + "." + config_var->name();
    if (config->contains_qualified(config_key)) {
      config_var->LoadGameConfigValue(config->get_qualified(config_key));
    }
  }
  XELOGI("Loaded game config: {}", xe::path_to_utf8(file_path));
}

void SaveConfig() {
  // All cvar defaults have been updated on loading - store the current date.
  auto defaults_date_cvar =
      dynamic_cast<cvar::ConfigVar<uint32_t>*>(cv::cv_defaults_date);
  assert_not_null(defaults_date_cvar);
  defaults_date_cvar->SetConfigValue(
      cvar::IConfigVarUpdate::GetLastUpdateDate());

  std::vector<cvar::IConfigVar*> vars;
  if (cvar::ConfigVars) {
    for (const auto& s : *cvar::ConfigVars) {
      vars.push_back(s.second);
    }
  }
  std::sort(vars.begin(), vars.end(), [](auto a, auto b) {
    if (a->category() < b->category()) return true;
    if (a->category() > b->category()) return false;
    if (a->name() < b->name()) return true;
    return false;
  });

  // we use our own write logic because cpptoml doesn't
  // allow us to specify comments :(
  std::string last_category;
  bool last_multiline_description = false;
  xe::StringBuffer sb;
  for (auto config_var : vars) {
    if (config_var->is_transient()) {
      continue;
    }

    if (last_category != config_var->category()) {
      if (!last_category.empty()) {
        sb.Append('\n', 2);
      }
      last_category = config_var->category();
      last_multiline_description = false;
      sb.AppendFormat("[{}]\n", last_category);
    } else if (last_multiline_description) {
      last_multiline_description = false;
      sb.Append('\n');
    }

    auto value = config_var->config_value();
    size_t line_count;
    if (xe::utf8::find_any_of(value, "\n") == std::string_view::npos) {
      auto line = fmt::format("{} = {}", config_var->name(),
                              config_var->config_value());
      sb.Append(line);
      line_count = xe::utf8::count(line);
    } else {
      auto lines = xe::utf8::split(value, "\n");
      auto first = lines.cbegin();
      sb.AppendFormat("{} = {}\n", config_var->name(), *first);
      auto last = std::prev(lines.cend());
      for (auto it = std::next(first); it != last; ++it) {
        sb.Append(*it);
        sb.Append('\n');
      }
      sb.Append(*last);
      line_count = xe::utf8::count(*last);
    }

    const size_t value_alignment = 50;
    const auto& description = config_var->description();
    if (!description.empty()) {
      if (line_count < value_alignment) {
        sb.Append(' ', value_alignment - line_count);
      }
      if (xe::utf8::find_any_of(description, "\n") == std::string_view::npos) {
        sb.AppendFormat("\t# {}\n", config_var->description());
      } else {
        auto lines = xe::utf8::split(description, "\n");
        auto first = lines.cbegin();
        sb.Append("\t# ");
        sb.Append(*first);
        sb.Append('\n');
        for (auto it = std::next(first); it != lines.cend(); ++it) {
          sb.Append(' ', value_alignment);
          sb.Append("\t# ");
          sb.Append(*it);
          sb.Append('\n');
        }
        last_multiline_description = true;
      }
    }
  }

  // save the config file
  xe::filesystem::CreateParentFolder(config_path);

  auto handle = xe::filesystem::OpenFile(config_path, "wb");
  if (!handle) {
    XELOGE("Failed to open '{}' for writing.", xe::path_to_utf8(config_path));
  } else {
    fwrite(sb.buffer(), 1, sb.length(), handle);
    fclose(handle);
  }
}

void SetupConfig(const std::filesystem::path& config_folder) {
  config::config_folder = config_folder;
  // check if the user specified a specific config to load
  if (!cvars::config.empty()) {
    config_path = xe::to_path(cvars::config);
    if (std::filesystem::exists(config_path)) {
      // An external config file may contain only explicit overrides - in this
      // case, it will likely not contain the defaults version; don't update
      // from the version 0 in this case. Or, it may be a full config - in this
      // case, if it's recent enough (created at least in 2021), it will contain
      // the version number - updates the defaults in it.
      ReadConfig(config_path, false);
      return;
    }
  }
  // if the user specified a --config argument, but the file doesn't exist,
  // let's also load the default config
  if (!config_folder.empty()) {
    config_path = config_folder / config_name;
    if (std::filesystem::exists(config_path)) {
      ReadConfig(config_path, true);
    }
    // Re-save the loaded config to present the most up-to-date list of
    // parameters to the user, if new options were added, descriptions were
    // updated, or default values were changed.
    SaveConfig();
  }
}

void LoadGameConfig(const std::string_view title_id) {
  const auto game_config_folder = config_folder / "config";
  const auto game_config_path =
      game_config_folder / (std::string(title_id) + game_config_suffix);
  if (std::filesystem::exists(game_config_path)) {
    ReadGameConfig(game_config_path);
  }
}

}  // namespace config
<commit_msg>[Core] Fix reading UTF-8 BOM config files<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2020 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "config.h"

#include "third_party/cpptoml/include/cpptoml.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/string.h"
#include "xenia/base/string_buffer.h"

std::shared_ptr<cpptoml::table> ParseFile(
    const std::filesystem::path& filename) {
  std::ifstream file(filename);
  if (!file.is_open()) {
    throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
                                   " could not be opened for parsing");
  }
  // since cpptoml can't parse files with a UTF-8 BOM we need to skip them
  char bom[3];
  file.read(bom, sizeof(bom));
  if (file.fail() || bom[0] != '\xEF' || bom[1] != '\xBB' || bom[2] != '\xBF') {
    file.clear();
    file.seekg(0);
  }

  cpptoml::parser p(file);
  return p.parse();
}

CmdVar(config, "", "Specifies the target config to load.");

DEFINE_uint32(
    defaults_date, 0,
    "Do not modify - internal version of the default values in the config, for "
    "seamless updates if default value of any option is changed.",
    "Config");

namespace config {
std::string config_name = "xenia.config.toml";
std::filesystem::path config_folder;
std::filesystem::path config_path;
std::string game_config_suffix = ".config.toml";

std::shared_ptr<cpptoml::table> ParseConfig(
    const std::filesystem::path& config_path) {
  try {
    return ParseFile(config_path);
  } catch (cpptoml::parse_exception e) {
    xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
                               xe::path_to_utf8(config_path), e.what()));
    return nullptr;
  }
}

void ReadConfig(const std::filesystem::path& file_path,
                bool update_if_no_version_stored) {
  if (!cvar::ConfigVars) {
    return;
  }
  const auto config = ParseConfig(file_path);
  // Loading an actual global config file that exists - if there's no
  // defaults_date in it, it's very old (before updating was added at all, thus
  // all defaults need to be updated).
  auto defaults_date_cvar =
      dynamic_cast<cvar::ConfigVar<uint32_t>*>(cv::cv_defaults_date);
  assert_not_null(defaults_date_cvar);
  defaults_date_cvar->SetConfigValue(0);
  for (auto& it : *cvar::ConfigVars) {
    auto config_var = static_cast<cvar::IConfigVar*>(it.second);
    auto config_key = config_var->category() + "." + config_var->name();
    if (config->contains_qualified(config_key)) {
      config_var->LoadConfigValue(config->get_qualified(config_key));
    }
  }
  uint32_t config_defaults_date = defaults_date_cvar->GetTypedConfigValue();
  if (update_if_no_version_stored || config_defaults_date) {
    cvar::IConfigVarUpdate::ApplyUpdates(config_defaults_date);
  }
  XELOGI("Loaded config: {}", xe::path_to_utf8(file_path));
}

void ReadGameConfig(const std::filesystem::path& file_path) {
  if (!cvar::ConfigVars) {
    return;
  }
  const auto config = ParseConfig(file_path);
  for (auto& it : *cvar::ConfigVars) {
    auto config_var = static_cast<cvar::IConfigVar*>(it.second);
    auto config_key = config_var->category() + "." + config_var->name();
    if (config->contains_qualified(config_key)) {
      config_var->LoadGameConfigValue(config->get_qualified(config_key));
    }
  }
  XELOGI("Loaded game config: {}", xe::path_to_utf8(file_path));
}

void SaveConfig() {
  // All cvar defaults have been updated on loading - store the current date.
  auto defaults_date_cvar =
      dynamic_cast<cvar::ConfigVar<uint32_t>*>(cv::cv_defaults_date);
  assert_not_null(defaults_date_cvar);
  defaults_date_cvar->SetConfigValue(
      cvar::IConfigVarUpdate::GetLastUpdateDate());

  std::vector<cvar::IConfigVar*> vars;
  if (cvar::ConfigVars) {
    for (const auto& s : *cvar::ConfigVars) {
      vars.push_back(s.second);
    }
  }
  std::sort(vars.begin(), vars.end(), [](auto a, auto b) {
    if (a->category() < b->category()) return true;
    if (a->category() > b->category()) return false;
    if (a->name() < b->name()) return true;
    return false;
  });

  // we use our own write logic because cpptoml doesn't
  // allow us to specify comments :(
  std::string last_category;
  bool last_multiline_description = false;
  xe::StringBuffer sb;
  for (auto config_var : vars) {
    if (config_var->is_transient()) {
      continue;
    }

    if (last_category != config_var->category()) {
      if (!last_category.empty()) {
        sb.Append('\n', 2);
      }
      last_category = config_var->category();
      last_multiline_description = false;
      sb.AppendFormat("[{}]\n", last_category);
    } else if (last_multiline_description) {
      last_multiline_description = false;
      sb.Append('\n');
    }

    auto value = config_var->config_value();
    size_t line_count;
    if (xe::utf8::find_any_of(value, "\n") == std::string_view::npos) {
      auto line = fmt::format("{} = {}", config_var->name(),
                              config_var->config_value());
      sb.Append(line);
      line_count = xe::utf8::count(line);
    } else {
      auto lines = xe::utf8::split(value, "\n");
      auto first = lines.cbegin();
      sb.AppendFormat("{} = {}\n", config_var->name(), *first);
      auto last = std::prev(lines.cend());
      for (auto it = std::next(first); it != last; ++it) {
        sb.Append(*it);
        sb.Append('\n');
      }
      sb.Append(*last);
      line_count = xe::utf8::count(*last);
    }

    const size_t value_alignment = 50;
    const auto& description = config_var->description();
    if (!description.empty()) {
      if (line_count < value_alignment) {
        sb.Append(' ', value_alignment - line_count);
      }
      if (xe::utf8::find_any_of(description, "\n") == std::string_view::npos) {
        sb.AppendFormat("\t# {}\n", config_var->description());
      } else {
        auto lines = xe::utf8::split(description, "\n");
        auto first = lines.cbegin();
        sb.Append("\t# ");
        sb.Append(*first);
        sb.Append('\n');
        for (auto it = std::next(first); it != lines.cend(); ++it) {
          sb.Append(' ', value_alignment);
          sb.Append("\t# ");
          sb.Append(*it);
          sb.Append('\n');
        }
        last_multiline_description = true;
      }
    }
  }

  // save the config file
  xe::filesystem::CreateParentFolder(config_path);

  auto handle = xe::filesystem::OpenFile(config_path, "wb");
  if (!handle) {
    XELOGE("Failed to open '{}' for writing.", xe::path_to_utf8(config_path));
  } else {
    fwrite(sb.buffer(), 1, sb.length(), handle);
    fclose(handle);
  }
}

void SetupConfig(const std::filesystem::path& config_folder) {
  config::config_folder = config_folder;
  // check if the user specified a specific config to load
  if (!cvars::config.empty()) {
    config_path = xe::to_path(cvars::config);
    if (std::filesystem::exists(config_path)) {
      // An external config file may contain only explicit overrides - in this
      // case, it will likely not contain the defaults version; don't update
      // from the version 0 in this case. Or, it may be a full config - in this
      // case, if it's recent enough (created at least in 2021), it will contain
      // the version number - updates the defaults in it.
      ReadConfig(config_path, false);
      return;
    }
  }
  // if the user specified a --config argument, but the file doesn't exist,
  // let's also load the default config
  if (!config_folder.empty()) {
    config_path = config_folder / config_name;
    if (std::filesystem::exists(config_path)) {
      ReadConfig(config_path, true);
    }
    // Re-save the loaded config to present the most up-to-date list of
    // parameters to the user, if new options were added, descriptions were
    // updated, or default values were changed.
    SaveConfig();
  }
}

void LoadGameConfig(const std::string_view title_id) {
  const auto game_config_folder = config_folder / "config";
  const auto game_config_path =
      game_config_folder / (std::string(title_id) + game_config_suffix);
  if (std::filesystem::exists(game_config_path)) {
    ReadGameConfig(game_config_path);
  }
}

}  // namespace config
<|endoftext|>