<commit_before>// -----------------------------------------------------------------------
// pion-common: a collection of common libraries used by the Pion Platform
// -----------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_PIONUNITTESTDEFS_HEADER__
#define __PION_PIONUNITTESTDEFS_HEADER__

#include <iostream>
#include <fstream>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <pion/PionLogger.hpp>

#ifdef _MSC_VER
	#include <direct.h>
	#define CHANGE_DIRECTORY _chdir
	#define GET_DIRECTORY(a,b) _getcwd(a,b)
#else
	#include <unistd.h>
	#define CHANGE_DIRECTORY chdir
	#define GET_DIRECTORY(a,b) getcwd(a,b)
#endif

#define DIRECTORY_MAX_SIZE 1000

struct PionUnitTest {
	// This is passed to xmlSetGenericErrorFunc() to make libxml do nothing when an error
	// occurs, rather than its default behavior of writing a message to stderr.
	static void doNothing(void* ctx, const char* msg, ...) {
	}

	static void skip_comment_lines(std::ifstream& fs) {
		int c;
		while (!fs.eof() && fs.peek() == '#') {
			while (! fs.eof()) {
				c = fs.get();
				if (c == '\n') {
					if (fs.peek() == '\r')
						fs.get();
					break;
				} else if (c == '\r') {
					if (fs.peek() == '\n')
						fs.get();
					break;
				}
			}
		}
	}

	static char* trim(char* str) {
		for (unsigned len = strlen(str) - 1; len > 0; len--) {
			if (str[len] == '\n' || str[len] == '\r')
				str[len] = '\0';
			else
				break;
		}
		return str;
	}

	// Check for file match, use std::list for sorting the files, which will allow
	// random order matching...
	static bool check_files_match(const std::string& fileA, const std::string& fileB) {
		// open files
		std::ifstream a_file(fileA.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(a_file.is_open());

		std::ifstream b_file(fileB.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(b_file.is_open());

		// skip lines that start with #
		skip_comment_lines(a_file);
		skip_comment_lines(b_file);

		// read and compare data in files
		static const unsigned int BUF_SIZE = 4096;
		char buf[BUF_SIZE];
		std::list<std::string> a_lines, b_lines;

		while (a_file.getline(buf, BUF_SIZE)) {
			a_lines.push_back(trim(buf));
			if (! b_file.getline(buf, BUF_SIZE))
				return false;
			b_lines.push_back(trim(buf));
		}
		if (b_file.getline(buf, BUF_SIZE))
			return false;
		if (a_file.gcount() != b_file.gcount())
			return false;
		a_lines.sort();
		b_lines.sort();

		if (a_lines != b_lines)
			return false;

		a_file.close();
		b_file.close();

		// files match
		return true;
	}
};

// PionUnitTestsConfig is intended for use as a global fixture.  By including the
// following line in one source code file of a unit test project, the constructor will
// run once before the first test and the destructor will run once after the last test:

// BOOST_GLOBAL_FIXTURE(PionUnitTestsConfig);

struct PionUnitTestsConfig {
	PionUnitTestsConfig() {
		std::cout << "global setup for all pion unit tests\n";

		// argc and argv do not include parameters handled by the boost unit test framework, such as --log_level.
		int argc = boost::unit_test::framework::master_test_suite().argc;
		char** argv = boost::unit_test::framework::master_test_suite().argv;

		bool verbose = false;
		if (argc > 1) {
			if (argv[1][0] == '-' && argv[1][1] == 'v') {
				verbose = true;
			}
		}
		PION_LOG_CONFIG_BASIC;
		pion::PionLogger log_ptr = PION_GET_LOGGER("pion");
		if (verbose) {
			PION_LOG_SETLEVEL_WARN(log_ptr);
		} else {
			std::cout << "Use '-v' to enable logging of errors and warnings from pion.\n";
			PION_LOG_SETLEVEL_FATAL(log_ptr);
		}
	}
	~PionUnitTestsConfig() {
		std::cout << "global teardown for all pion unit tests\n";
	}
};

/*
Using BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE and
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE has two additional benefits relative to
using BOOST_FIXTURE_TEST_SUITE and BOOST_AUTO_TEST_CASE:
1) it allows a test to be run with more than one fixture, and
2) it makes the current fixture part of the test name, e.g.
   checkPropertyX<myFixture_F>

For an example of 1), see HTTPMessageTests.cpp.

There are probably simpler ways to achieve 2), but since it comes for free,
it makes sense to use it.  The benefit of this is that the test names don't
have to include redundant information about the fixture, e.g.
checkMyFixtureHasPropertyX.  (In this example, checkPropertyX<myFixture_F> is
not obviously better than checkMyFixtureHasPropertyX, but in many cases the
test names become too long and/or hard to parse, or the fixture just isn't
part of the name, making some error reports ambiguous.)

(BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE is based on BOOST_AUTO_TEST_CASE_TEMPLATE,
in unit_test_suite.hpp.)

Minimal example demonstrating usage of BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE:

class ObjectToTest_F { // suffix _F is used for fixtures
public:
	ObjectToTest_F() {
		m_value = 2;
	}
	int m_value;
	int getValue() { return m_value; }
};

// This illustrates the most common case, where just one fixture will be used,
// so the list only has one fixture in it.
// ObjectToTest_S is the name of the test suite.
BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE(ObjectToTest_S,
									   boost::mpl::list<ObjectToTest_F>)

// One method for testing the fixture...
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwo) {
	BOOST_CHECK_EQUAL(F::m_value, 2);
	BOOST_CHECK_EQUAL(F::getValue(), 2);
}

// Another method for testing the fixture...
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwoAgain) {
	BOOST_CHECK_EQUAL(this->m_value, 2);
	BOOST_CHECK_EQUAL(this->getValue(), 2);
}

// The simplest, but, alas, non conformant to the C++ standard, method for testing the fixture.
// This will compile with MSVC (unless language extensions are disabled (/Za)).
// It won't compile with gcc unless -fpermissive is used.
// See http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html.
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwoNonConformant) {
	BOOST_CHECK_EQUAL(m_value, 2);
	BOOST_CHECK_EQUAL(getValue(), 2);
}

BOOST_AUTO_TEST_SUITE_END()
*/

#define BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE(suite_name, fixture_types) \
BOOST_AUTO_TEST_SUITE(suite_name)                                         \
typedef fixture_types BOOST_AUTO_TEST_CASE_FIXTURE_TYPES;                 \
/**/

#define BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(test_name)		\
template<typename F>											\
struct test_name : public F										\
{ void test_method(); };										\
																\
struct BOOST_AUTO_TC_INVOKER( test_name ) {						\
	template<typename TestType>									\
	static void run( boost::type<TestType>* = 0 )				\
	{															\
		test_name<TestType> t;									\
		t.test_method();										\
	}															\
};																\
																\
BOOST_AUTO_TU_REGISTRAR( test_name )(							\
	boost::unit_test::ut_detail::template_test_case_gen<		\
		BOOST_AUTO_TC_INVOKER( test_name ),						\
		BOOST_AUTO_TEST_CASE_FIXTURE_TYPES >(					\
			BOOST_STRINGIZE( test_name ) ) );					\
																\
template<typename F>											\
void test_name<F>::test_method()								\
/**/

#endif
<commit_msg>Moved ReactionEngineAlreadyRunningTests_F::check_files_exact_match() to PionUnitTest, and added parameter 'ignore_line_endings'.<commit_after>// -----------------------------------------------------------------------
// pion-common: a collection of common libraries used by the Pion Platform
// -----------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_PIONUNITTESTDEFS_HEADER__
#define __PION_PIONUNITTESTDEFS_HEADER__

#include <iostream>
#include <fstream>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <pion/PionLogger.hpp>

#ifdef _MSC_VER
	#include <direct.h>
	#define CHANGE_DIRECTORY _chdir
	#define GET_DIRECTORY(a,b) _getcwd(a,b)
#else
	#include <unistd.h>
	#define CHANGE_DIRECTORY chdir
	#define GET_DIRECTORY(a,b) getcwd(a,b)
#endif

#define DIRECTORY_MAX_SIZE 1000

struct PionUnitTest {
	// This is passed to xmlSetGenericErrorFunc() to make libxml do nothing when an error
	// occurs, rather than its default behavior of writing a message to stderr.
	static void doNothing(void* ctx, const char* msg, ...) {
	}

	static void skip_comment_lines(std::ifstream& fs) {
		int c;
		while (!fs.eof() && fs.peek() == '#') {
			while (! fs.eof()) {
				c = fs.get();
				if (c == '\n') {
					if (fs.peek() == '\r')
						fs.get();
					break;
				} else if (c == '\r') {
					if (fs.peek() == '\n')
						fs.get();
					break;
				}
			}
		}
	}

	static char* trim(char* str) {
		for (unsigned len = strlen(str) - 1; len > 0; len--) {
			if (str[len] == '\n' || str[len] == '\r')
				str[len] = '\0';
			else
				break;
		}
		return str;
	}

	// Check for file match, use std::list for sorting the files, which will allow
	// random order matching...
	static bool check_files_match(const std::string& fileA, const std::string& fileB) {
		// open files
		std::ifstream a_file(fileA.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(a_file.is_open());

		std::ifstream b_file(fileB.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(b_file.is_open());

		// skip lines that start with #
		skip_comment_lines(a_file);
		skip_comment_lines(b_file);

		// read and compare data in files
		static const unsigned int BUF_SIZE = 4096;
		char buf[BUF_SIZE];
		std::list<std::string> a_lines, b_lines;

		while (a_file.getline(buf, BUF_SIZE)) {
			a_lines.push_back(trim(buf));
			if (! b_file.getline(buf, BUF_SIZE))
				return false;
			b_lines.push_back(trim(buf));
		}
		if (b_file.getline(buf, BUF_SIZE))
			return false;
		if (a_file.gcount() != b_file.gcount())
			return false;
		a_lines.sort();
		b_lines.sort();

		if (a_lines != b_lines)
			return false;

		a_file.close();
		b_file.close();

		// files match
		return true;
	}

	static bool check_files_exact_match(const std::string& fileA, const std::string& fileB, bool ignore_line_endings = false) {
		// open files
		std::ifstream a_file(fileA.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(a_file.is_open());

		std::ifstream b_file(fileB.c_str(), std::ios::in | std::ios::binary);
		BOOST_REQUIRE(b_file.is_open());

		// read and compare data in files
		static const unsigned int BUF_SIZE = 4096;
		char a_buf[BUF_SIZE];
		char b_buf[BUF_SIZE];

		if (ignore_line_endings) {
			while (a_file.getline(a_buf, BUF_SIZE)) {
				if (! b_file.getline(b_buf, BUF_SIZE))
					return false;
				PionUnitTest::trim(a_buf);
				PionUnitTest::trim(b_buf);
				if (strlen(a_buf) != strlen(b_buf))
					return false;
				if (memcmp(a_buf, b_buf, strlen(a_buf)) != 0)
					return false;
			}
			if (b_file.getline(b_buf, BUF_SIZE))
				return false;
		} else {
			while (a_file.read(a_buf, BUF_SIZE)) {
				if (! b_file.read(b_buf, BUF_SIZE))
					return false;
				if (memcmp(a_buf, b_buf, BUF_SIZE) != 0)
					return false;
			}
			if (b_file.read(b_buf, BUF_SIZE))
				return false;
		}
		if (a_file.gcount() != b_file.gcount())
			return false;
		if (memcmp(a_buf, b_buf, a_file.gcount()) != 0)
			return false;

		a_file.close();
		b_file.close();

		// files match
		return true;
	}
};

// PionUnitTestsConfig is intended for use as a global fixture.  By including the
// following line in one source code file of a unit test project, the constructor will
// run once before the first test and the destructor will run once after the last test:

// BOOST_GLOBAL_FIXTURE(PionUnitTestsConfig);

struct PionUnitTestsConfig {
	PionUnitTestsConfig() {
		std::cout << "global setup for all pion unit tests\n";

		// argc and argv do not include parameters handled by the boost unit test framework, such as --log_level.
		int argc = boost::unit_test::framework::master_test_suite().argc;
		char** argv = boost::unit_test::framework::master_test_suite().argv;

		bool verbose = false;
		if (argc > 1) {
			if (argv[1][0] == '-' && argv[1][1] == 'v') {
				verbose = true;
			}
		}
		PION_LOG_CONFIG_BASIC;
		pion::PionLogger log_ptr = PION_GET_LOGGER("pion");
		if (verbose) {
			PION_LOG_SETLEVEL_WARN(log_ptr);
		} else {
			std::cout << "Use '-v' to enable logging of errors and warnings from pion.\n";
			PION_LOG_SETLEVEL_FATAL(log_ptr);
		}
	}
	~PionUnitTestsConfig() {
		std::cout << "global teardown for all pion unit tests\n";
	}
};

/*
Using BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE and
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE has two additional benefits relative to
using BOOST_FIXTURE_TEST_SUITE and BOOST_AUTO_TEST_CASE:
1) it allows a test to be run with more than one fixture, and
2) it makes the current fixture part of the test name, e.g.
   checkPropertyX<myFixture_F>

For an example of 1), see HTTPMessageTests.cpp.

There are probably simpler ways to achieve 2), but since it comes for free,
it makes sense to use it.  The benefit of this is that the test names don't
have to include redundant information about the fixture, e.g.
checkMyFixtureHasPropertyX.  (In this example, checkPropertyX<myFixture_F> is
not obviously better than checkMyFixtureHasPropertyX, but in many cases the
test names become too long and/or hard to parse, or the fixture just isn't
part of the name, making some error reports ambiguous.)

(BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE is based on BOOST_AUTO_TEST_CASE_TEMPLATE,
in unit_test_suite.hpp.)

Minimal example demonstrating usage of BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE:

class ObjectToTest_F { // suffix _F is used for fixtures
public:
	ObjectToTest_F() {
		m_value = 2;
	}
	int m_value;
	int getValue() { return m_value; }
};

// This illustrates the most common case, where just one fixture will be used,
// so the list only has one fixture in it.
// ObjectToTest_S is the name of the test suite.
BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE(ObjectToTest_S,
									   boost::mpl::list<ObjectToTest_F>)

// One method for testing the fixture...
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwo) {
	BOOST_CHECK_EQUAL(F::m_value, 2);
	BOOST_CHECK_EQUAL(F::getValue(), 2);
}

// Another method for testing the fixture...
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwoAgain) {
	BOOST_CHECK_EQUAL(this->m_value, 2);
	BOOST_CHECK_EQUAL(this->getValue(), 2);
}

// The simplest, but, alas, non conformant to the C++ standard, method for testing the fixture.
// This will compile with MSVC (unless language extensions are disabled (/Za)).
// It won't compile with gcc unless -fpermissive is used.
// See http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html.
BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(checkValueEqualsTwoNonConformant) {
	BOOST_CHECK_EQUAL(m_value, 2);
	BOOST_CHECK_EQUAL(getValue(), 2);
}

BOOST_AUTO_TEST_SUITE_END()
*/

#define BOOST_AUTO_TEST_SUITE_FIXTURE_TEMPLATE(suite_name, fixture_types) \
BOOST_AUTO_TEST_SUITE(suite_name)                                         \
typedef fixture_types BOOST_AUTO_TEST_CASE_FIXTURE_TYPES;                 \
/**/

#define BOOST_AUTO_TEST_CASE_FIXTURE_TEMPLATE(test_name)		\
template<typename F>											\
struct test_name : public F										\
{ void test_method(); };										\
																\
struct BOOST_AUTO_TC_INVOKER( test_name ) {						\
	template<typename TestType>									\
	static void run( boost::type<TestType>* = 0 )				\
	{															\
		test_name<TestType> t;									\
		t.test_method();										\
	}															\
};																\
																\
BOOST_AUTO_TU_REGISTRAR( test_name )(							\
	boost::unit_test::ut_detail::template_test_case_gen<		\
		BOOST_AUTO_TC_INVOKER( test_name ),						\
		BOOST_AUTO_TEST_CASE_FIXTURE_TYPES >(					\
			BOOST_STRINGIZE( test_name ) ) );					\
																\
template<typename F>											\
void test_name<F>::test_method()								\
/**/

#endif
<|endoftext|>


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

#include <seeder/bitcoin.h>

#include <hash.h>
#include <netbase.h>
#include <seeder/db.h>
#include <serialize.h>
#include <uint256.h>

#include <algorithm>

// The network magic to use.
CMessageHeader::MessageMagic netMagic = {{0xe3, 0xe1, 0xf3, 0xe8}};

#define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL

static const uint32_t allones(-1);

void CSeederNode::BeginMessage(const char *pszCommand) {
    if (nHeaderStart != allones) {
        AbortMessage();
    }
    nHeaderStart = vSend.size();
    vSend << CMessageHeader(netMagic, pszCommand, 0);
    nMessageStart = vSend.size();
    // tfm::format(std::cout, "%s: SEND %s\n", ToString(you),
    // pszCommand);
}

void CSeederNode::AbortMessage() {
    if (nHeaderStart == allones) {
        return;
    }
    vSend.resize(nHeaderStart);
    nHeaderStart = allones;
    nMessageStart = allones;
}

void CSeederNode::EndMessage() {
    if (nHeaderStart == allones) {
        return;
    }
    uint32_t nSize = vSend.size() - nMessageStart;
    memcpy((char *)&vSend[nHeaderStart] +
               offsetof(CMessageHeader, nMessageSize),
           &nSize, sizeof(nSize));
    if (vSend.GetVersion() >= 209) {
        uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
        unsigned int nChecksum = 0;
        memcpy(&nChecksum, &hash, sizeof(nChecksum));
        assert(nMessageStart - nHeaderStart >=
               offsetof(CMessageHeader, pchChecksum) + sizeof(nChecksum));
        memcpy((char *)&vSend[nHeaderStart] +
                   offsetof(CMessageHeader, pchChecksum),
               &nChecksum, sizeof(nChecksum));
    }
    nHeaderStart = allones;
    nMessageStart = allones;
}

void CSeederNode::Send() {
    if (sock == INVALID_SOCKET) {
        return;
    }
    if (vSend.empty()) {
        return;
    }
    int nBytes = send(sock, &vSend[0], vSend.size(), 0);
    if (nBytes > 0) {
        vSend.erase(vSend.begin(), vSend.begin() + nBytes);
    } else {
        close(sock);
        sock = INVALID_SOCKET;
    }
}

void CSeederNode::PushVersion() {
    int64_t nTime = time(nullptr);
    uint64_t nLocalNonce = BITCOIN_SEED_NONCE;
    int64_t nLocalServices = 0;
    CService myService;
    CAddress me(myService, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH));
    BeginMessage("version");
    int nBestHeight = GetRequireHeight();
    std::string ver = "/bitcoin-cash-seeder:0.15/";
    vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me
          << nLocalNonce << ver << nBestHeight;
    EndMessage();
}

PeerMessagingState CSeederNode::ProcessMessage(std::string strCommand,
                                               CDataStream &recv) {
    // tfm::format(std::cout, "%s: RECV %s\n", ToString(you),
    // strCommand);
    if (strCommand == "version") {
        int64_t nTime;
        CAddress addrMe;
        CAddress addrFrom;
        uint64_t nNonce = 1;
        uint64_t nServiceInt;
        recv >> nVersion >> nServiceInt >> nTime >> addrMe;
        you.nServices = ServiceFlags(nServiceInt);
        recv >> addrFrom >> nNonce;
        recv >> strSubVer;
        recv >> nStartingHeight;

        BeginMessage("verack");
        EndMessage();
        vSend.SetVersion(std::min(nVersion, PROTOCOL_VERSION));
        return PeerMessagingState::AwaitingMessages;
    }

    if (strCommand == "verack") {
        vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION));
        // tfm::format(std::cout, "\n%s: version %i\n", ToString(you),
        // nVersion);
        if (vAddr) {
            BeginMessage("getaddr");
            EndMessage();
            doneAfter = time(nullptr) + GetTimeout();
        } else {
            doneAfter = time(nullptr) + 1;
        }
        return PeerMessagingState::AwaitingMessages;
    }

    if (strCommand == "addr" && vAddr) {
        std::vector<CAddress> vAddrNew;
        recv >> vAddrNew;
        // tfm::format(std::cout, "%s: got %i addresses\n",
        // ToString(you),
        //        (int)vAddrNew.size());
        int64_t now = time(nullptr);
        std::vector<CAddress>::iterator it = vAddrNew.begin();
        if (vAddrNew.size() > 1) {
            if (doneAfter == 0 || doneAfter > now + 1) {
                doneAfter = now + 1;
            }
        }
        while (it != vAddrNew.end()) {
            CAddress &addr = *it;
            // tfm::format(std::cout, "%s: got address %s\n",
            // ToString(you),
            //        addr.ToString(), (int)(vAddr->size()));
            it++;
            if (addr.nTime <= 100000000 || addr.nTime > now + 600) {
                addr.nTime = now - 5 * 86400;
            }
            if (addr.nTime > now - 604800) {
                vAddr->push_back(addr);
            }
            // tfm::format(std::cout, "%s: added address %s (#%i)\n",
            // ToString(you),
            //        addr.ToString(), (int)(vAddr->size()));
            if (vAddr->size() > ADDR_SOFT_CAP) {
                doneAfter = 1;
                return PeerMessagingState::Finished;
            }
        }
        return PeerMessagingState::AwaitingMessages;
    }

    return PeerMessagingState::AwaitingMessages;
}

bool CSeederNode::ProcessMessages() {
    if (vRecv.empty()) {
        return false;
    }

    do {
        CDataStream::iterator pstart = std::search(
            vRecv.begin(), vRecv.end(), BEGIN(netMagic), END(netMagic));
        uint32_t nHeaderSize =
            GetSerializeSize(CMessageHeader(netMagic), vRecv.GetVersion());
        if (vRecv.end() - pstart < nHeaderSize) {
            if (vRecv.size() > nHeaderSize) {
                vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
            }
            break;
        }
        vRecv.erase(vRecv.begin(), pstart);
        std::vector<char> vHeaderSave(vRecv.begin(),
                                      vRecv.begin() + nHeaderSize);
        CMessageHeader hdr(netMagic);
        vRecv >> hdr;
        if (!hdr.IsValidWithoutConfig(netMagic)) {
            // tfm::format(std::cout, "%s: BAD (invalid header)\n",
            // ToString(you));
            ban = 100000;
            return true;
        }
        std::string strCommand = hdr.GetCommand();
        unsigned int nMessageSize = hdr.nMessageSize;
        if (nMessageSize > MAX_SIZE) {
            // tfm::format(std::cout, "%s: BAD (message too large)\n",
            // ToString(you));
            ban = 100000;
            return true;
        }
        if (nMessageSize > vRecv.size()) {
            vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
            break;
        }
        if (vRecv.GetVersion() >= 209) {
            uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
            if (memcmp(hash.begin(), hdr.pchChecksum,
                       CMessageHeader::CHECKSUM_SIZE) != 0) {
                continue;
            }
        }
        CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize,
                         vRecv.GetType(), vRecv.GetVersion());
        vRecv.ignore(nMessageSize);
        if (ProcessMessage(strCommand, vMsg) == PeerMessagingState::Finished) {
            return true;
        }
        // tfm::format(std::cout, "%s: done processing %s\n",
        // ToString(you),
        //        strCommand);
    } while (1);
    return false;
}

CSeederNode::CSeederNode(const CService &ip, std::vector<CAddress> *vAddrIn)
    : sock(INVALID_SOCKET), vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0),
      nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0),
      doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)) {
    if (time(nullptr) > 1329696000) {
        vSend.SetVersion(209);
        vRecv.SetVersion(209);
    }
}

bool CSeederNode::Run() {
    // FIXME: This logic is duplicated with CConnman::ConnectNode for no
    // good reason.
    bool connected = false;
    proxyType proxy;

    if (you.IsValid()) {
        bool proxyConnectionFailed = false;

        if (GetProxy(you.GetNetwork(), proxy)) {
            sock = CreateSocket(proxy.proxy);
            if (sock == INVALID_SOCKET) {
                return false;
            }
            connected = ConnectThroughProxy(
                proxy, you.ToStringIP(), you.GetPort(), sock, nConnectTimeout,
                &proxyConnectionFailed);
        } else {
            // no proxy needed (none set for target network)
            sock = CreateSocket(you);
            if (sock == INVALID_SOCKET) {
                return false;
            }
            // no proxy needed (none set for target network)
            connected =
                ConnectSocketDirectly(you, sock, nConnectTimeout, false);
        }
    }

    if (!connected) {
        // tfm::format(std::cout, "Cannot connect to %s\n",
        // ToString(you));
        CloseSocket(sock);
        return false;
    }

    PushVersion();
    Send();

    bool res = true;
    int64_t now;
    while (now = time(nullptr), ban == 0 &&
                                    (doneAfter == 0 || doneAfter > now) &&
                                    sock != INVALID_SOCKET) {
        char pchBuf[0x10000];
        fd_set fdsetRecv;
        fd_set fdsetError;
        FD_ZERO(&fdsetRecv);
        FD_ZERO(&fdsetError);
        FD_SET(sock, &fdsetRecv);
        FD_SET(sock, &fdsetError);
        struct timeval wa;
        if (doneAfter) {
            wa.tv_sec = doneAfter - now;
            wa.tv_usec = 0;
        } else {
            wa.tv_sec = GetTimeout();
            wa.tv_usec = 0;
        }
        int ret = select(sock + 1, &fdsetRecv, nullptr, &fdsetError, &wa);
        if (ret != 1) {
            if (!doneAfter) {
                res = false;
            }
            break;
        }
        int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0);
        int nPos = vRecv.size();
        if (nBytes > 0) {
            vRecv.resize(nPos + nBytes);
            memcpy(&vRecv[nPos], pchBuf, nBytes);
        } else if (nBytes == 0) {
            // tfm::format(std::cout, "%s: BAD (connection closed
            // prematurely)\n",
            //        ToString(you));
            res = false;
            break;
        } else {
            // tfm::format(std::cout, "%s: BAD (connection error)\n",
            // ToString(you));
            res = false;
            break;
        }
        ProcessMessages();
        Send();
    }
    if (sock == INVALID_SOCKET) {
        res = false;
    }
    close(sock);
    sock = INVALID_SOCKET;
    return (ban == 0) && res;
}
<commit_msg>Replace message type literals with protocol.h constants<commit_after>// Copyright (c) 2017-2019 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <seeder/bitcoin.h>

#include <hash.h>
#include <netbase.h>
#include <seeder/db.h>
#include <serialize.h>
#include <uint256.h>

#include <algorithm>

// The network magic to use.
CMessageHeader::MessageMagic netMagic = {{0xe3, 0xe1, 0xf3, 0xe8}};

#define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL

static const uint32_t allones(-1);

void CSeederNode::BeginMessage(const char *pszCommand) {
    if (nHeaderStart != allones) {
        AbortMessage();
    }
    nHeaderStart = vSend.size();
    vSend << CMessageHeader(netMagic, pszCommand, 0);
    nMessageStart = vSend.size();
    // tfm::format(std::cout, "%s: SEND %s\n", ToString(you),
    // pszCommand);
}

void CSeederNode::AbortMessage() {
    if (nHeaderStart == allones) {
        return;
    }
    vSend.resize(nHeaderStart);
    nHeaderStart = allones;
    nMessageStart = allones;
}

void CSeederNode::EndMessage() {
    if (nHeaderStart == allones) {
        return;
    }
    uint32_t nSize = vSend.size() - nMessageStart;
    memcpy((char *)&vSend[nHeaderStart] +
               offsetof(CMessageHeader, nMessageSize),
           &nSize, sizeof(nSize));
    if (vSend.GetVersion() >= 209) {
        uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
        unsigned int nChecksum = 0;
        memcpy(&nChecksum, &hash, sizeof(nChecksum));
        assert(nMessageStart - nHeaderStart >=
               offsetof(CMessageHeader, pchChecksum) + sizeof(nChecksum));
        memcpy((char *)&vSend[nHeaderStart] +
                   offsetof(CMessageHeader, pchChecksum),
               &nChecksum, sizeof(nChecksum));
    }
    nHeaderStart = allones;
    nMessageStart = allones;
}

void CSeederNode::Send() {
    if (sock == INVALID_SOCKET) {
        return;
    }
    if (vSend.empty()) {
        return;
    }
    int nBytes = send(sock, &vSend[0], vSend.size(), 0);
    if (nBytes > 0) {
        vSend.erase(vSend.begin(), vSend.begin() + nBytes);
    } else {
        close(sock);
        sock = INVALID_SOCKET;
    }
}

void CSeederNode::PushVersion() {
    int64_t nTime = time(nullptr);
    uint64_t nLocalNonce = BITCOIN_SEED_NONCE;
    int64_t nLocalServices = 0;
    CService myService;
    CAddress me(myService, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH));
    BeginMessage(NetMsgType::VERSION);
    int nBestHeight = GetRequireHeight();
    std::string ver = "/bitcoin-cash-seeder:0.15/";
    vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me
          << nLocalNonce << ver << nBestHeight;
    EndMessage();
}

PeerMessagingState CSeederNode::ProcessMessage(std::string strCommand,
                                               CDataStream &recv) {
    // tfm::format(std::cout, "%s: RECV %s\n", ToString(you),
    // strCommand);
    if (strCommand == NetMsgType::VERSION) {
        int64_t nTime;
        CAddress addrMe;
        CAddress addrFrom;
        uint64_t nNonce = 1;
        uint64_t nServiceInt;
        recv >> nVersion >> nServiceInt >> nTime >> addrMe;
        you.nServices = ServiceFlags(nServiceInt);
        recv >> addrFrom >> nNonce;
        recv >> strSubVer;
        recv >> nStartingHeight;

        BeginMessage(NetMsgType::VERACK);
        EndMessage();
        vSend.SetVersion(std::min(nVersion, PROTOCOL_VERSION));
        return PeerMessagingState::AwaitingMessages;
    }

    if (strCommand == NetMsgType::VERACK) {
        vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION));
        // tfm::format(std::cout, "\n%s: version %i\n", ToString(you),
        // nVersion);
        if (vAddr) {
            BeginMessage(NetMsgType::GETADDR);
            EndMessage();
            doneAfter = time(nullptr) + GetTimeout();
        } else {
            doneAfter = time(nullptr) + 1;
        }
        return PeerMessagingState::AwaitingMessages;
    }

    if (strCommand == NetMsgType::ADDR && vAddr) {
        std::vector<CAddress> vAddrNew;
        recv >> vAddrNew;
        // tfm::format(std::cout, "%s: got %i addresses\n",
        // ToString(you),
        //        (int)vAddrNew.size());
        int64_t now = time(nullptr);
        std::vector<CAddress>::iterator it = vAddrNew.begin();
        if (vAddrNew.size() > 1) {
            if (doneAfter == 0 || doneAfter > now + 1) {
                doneAfter = now + 1;
            }
        }
        while (it != vAddrNew.end()) {
            CAddress &addr = *it;
            // tfm::format(std::cout, "%s: got address %s\n",
            // ToString(you),
            //        addr.ToString(), (int)(vAddr->size()));
            it++;
            if (addr.nTime <= 100000000 || addr.nTime > now + 600) {
                addr.nTime = now - 5 * 86400;
            }
            if (addr.nTime > now - 604800) {
                vAddr->push_back(addr);
            }
            // tfm::format(std::cout, "%s: added address %s (#%i)\n",
            // ToString(you),
            //        addr.ToString(), (int)(vAddr->size()));
            if (vAddr->size() > ADDR_SOFT_CAP) {
                doneAfter = 1;
                return PeerMessagingState::Finished;
            }
        }
        return PeerMessagingState::AwaitingMessages;
    }

    return PeerMessagingState::AwaitingMessages;
}

bool CSeederNode::ProcessMessages() {
    if (vRecv.empty()) {
        return false;
    }

    do {
        CDataStream::iterator pstart = std::search(
            vRecv.begin(), vRecv.end(), BEGIN(netMagic), END(netMagic));
        uint32_t nHeaderSize =
            GetSerializeSize(CMessageHeader(netMagic), vRecv.GetVersion());
        if (vRecv.end() - pstart < nHeaderSize) {
            if (vRecv.size() > nHeaderSize) {
                vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
            }
            break;
        }
        vRecv.erase(vRecv.begin(), pstart);
        std::vector<char> vHeaderSave(vRecv.begin(),
                                      vRecv.begin() + nHeaderSize);
        CMessageHeader hdr(netMagic);
        vRecv >> hdr;
        if (!hdr.IsValidWithoutConfig(netMagic)) {
            // tfm::format(std::cout, "%s: BAD (invalid header)\n",
            // ToString(you));
            ban = 100000;
            return true;
        }
        std::string strCommand = hdr.GetCommand();
        unsigned int nMessageSize = hdr.nMessageSize;
        if (nMessageSize > MAX_SIZE) {
            // tfm::format(std::cout, "%s: BAD (message too large)\n",
            // ToString(you));
            ban = 100000;
            return true;
        }
        if (nMessageSize > vRecv.size()) {
            vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
            break;
        }
        if (vRecv.GetVersion() >= 209) {
            uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
            if (memcmp(hash.begin(), hdr.pchChecksum,
                       CMessageHeader::CHECKSUM_SIZE) != 0) {
                continue;
            }
        }
        CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize,
                         vRecv.GetType(), vRecv.GetVersion());
        vRecv.ignore(nMessageSize);
        if (ProcessMessage(strCommand, vMsg) == PeerMessagingState::Finished) {
            return true;
        }
        // tfm::format(std::cout, "%s: done processing %s\n",
        // ToString(you),
        //        strCommand);
    } while (1);
    return false;
}

CSeederNode::CSeederNode(const CService &ip, std::vector<CAddress> *vAddrIn)
    : sock(INVALID_SOCKET), vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0),
      nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0),
      doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH)) {
    if (time(nullptr) > 1329696000) {
        vSend.SetVersion(209);
        vRecv.SetVersion(209);
    }
}

bool CSeederNode::Run() {
    // FIXME: This logic is duplicated with CConnman::ConnectNode for no
    // good reason.
    bool connected = false;
    proxyType proxy;

    if (you.IsValid()) {
        bool proxyConnectionFailed = false;

        if (GetProxy(you.GetNetwork(), proxy)) {
            sock = CreateSocket(proxy.proxy);
            if (sock == INVALID_SOCKET) {
                return false;
            }
            connected = ConnectThroughProxy(
                proxy, you.ToStringIP(), you.GetPort(), sock, nConnectTimeout,
                &proxyConnectionFailed);
        } else {
            // no proxy needed (none set for target network)
            sock = CreateSocket(you);
            if (sock == INVALID_SOCKET) {
                return false;
            }
            // no proxy needed (none set for target network)
            connected =
                ConnectSocketDirectly(you, sock, nConnectTimeout, false);
        }
    }

    if (!connected) {
        // tfm::format(std::cout, "Cannot connect to %s\n",
        // ToString(you));
        CloseSocket(sock);
        return false;
    }

    PushVersion();
    Send();

    bool res = true;
    int64_t now;
    while (now = time(nullptr), ban == 0 &&
                                    (doneAfter == 0 || doneAfter > now) &&
                                    sock != INVALID_SOCKET) {
        char pchBuf[0x10000];
        fd_set fdsetRecv;
        fd_set fdsetError;
        FD_ZERO(&fdsetRecv);
        FD_ZERO(&fdsetError);
        FD_SET(sock, &fdsetRecv);
        FD_SET(sock, &fdsetError);
        struct timeval wa;
        if (doneAfter) {
            wa.tv_sec = doneAfter - now;
            wa.tv_usec = 0;
        } else {
            wa.tv_sec = GetTimeout();
            wa.tv_usec = 0;
        }
        int ret = select(sock + 1, &fdsetRecv, nullptr, &fdsetError, &wa);
        if (ret != 1) {
            if (!doneAfter) {
                res = false;
            }
            break;
        }
        int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0);
        int nPos = vRecv.size();
        if (nBytes > 0) {
            vRecv.resize(nPos + nBytes);
            memcpy(&vRecv[nPos], pchBuf, nBytes);
        } else if (nBytes == 0) {
            // tfm::format(std::cout, "%s: BAD (connection closed
            // prematurely)\n",
            //        ToString(you));
            res = false;
            break;
        } else {
            // tfm::format(std::cout, "%s: BAD (connection error)\n",
            // ToString(you));
            res = false;
            break;
        }
        ProcessMessages();
        Send();
    }
    if (sock == INVALID_SOCKET) {
        res = false;
    }
    close(sock);
    sock = INVALID_SOCKET;
    return (ban == 0) && res;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Crytek
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_replay.h"
#include "gl_driver.h"
#include "gl_resources.h"

#include <dlfcn.h>

typedef Bool (*PFNGLXMAKECURRENTPROC)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display *dpy, GLXContext ctx);
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display *dpy, GLXDrawable drawable);

PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfigProc = NULL;
PFNGLXCREATEPBUFFERPROC glXCreatePbufferProc = NULL;
PFNGLXDESTROYPBUFFERPROC glXDestroyPbufferProc = NULL;
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsProc = NULL;
PFNGLXGETPROCADDRESSPROC glXGetFuncProc = NULL;
PFNGLXMAKECONTEXTCURRENTPROC glXMakeContextCurrentProc = NULL;
PFNGLXQUERYDRAWABLEPROC glXQueryDrawableProc = NULL;
PFNGLXDESTROYCONTEXTPROC glXDestroyCtxProc = NULL;
PFNGLXSWAPBUFFERSPROC glXSwapProc = NULL;

void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx)
{
	static GLWindowingData *prev = NULL;

	if(glXMakeContextCurrentProc && ctx && ctx != prev)
	{
		prev = ctx;
		glXMakeContextCurrentProc(ctx->dpy, ctx->wnd, ctx->wnd, ctx->ctx);
	}
}

void GLReplay::SwapBuffers(GLWindowingData *ctx)
{
	glXSwapProc(ctx->dpy, ctx->wnd);
}

void GLReplay::CloseReplayContext()
{
	if(glXDestroyCtxProc)
	{
		glXDestroyCtxProc(m_ReplayCtx.dpy, m_ReplayCtx.ctx);
	}
}

uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth)
{
	void **displayAndDrawable = (void **)wn;

	Display *dpy = NULL;
	GLXDrawable wnd = 0;

	if(wn)
	{
		dpy = (Display *)displayAndDrawable[0];
		wnd = (GLXDrawable)displayAndDrawable[1];
	}
	else
	{
		dpy = XOpenDisplay(NULL);

		if(dpy == NULL)
			return 0;

		// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
		static int visAttribs[] = { 0 };
		int numCfgs = 0;
		GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

		if(fbcfg == NULL)
		{
			XCloseDisplay(dpy);
			RDCERR("Couldn't choose default framebuffer config");
			return 0;
		}

		// don't care about pbuffer properties for same reason as backbuffer
		int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

		wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

		XFree(fbcfg);
	}

	static int visAttribs[] = {
		GLX_X_RENDERABLE, True,
		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
		GLX_RED_SIZE, 8,
		GLX_GREEN_SIZE, 8,
		GLX_BLUE_SIZE, 8,
		GLX_ALPHA_SIZE, 8,
		GLX_DOUBLEBUFFER, True,
		0
	};
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return 0;
	}

	XFree(fbcfg);

	OutputWindow win;
	win.dpy = dpy;
	win.ctx = ctx;
	win.wnd = wnd;

	glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width);
	glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height);

	MakeCurrentReplayContext(&win);

	InitOutputWindow(win);
	CreateOutputWindowBackbuffer(win);

	uint64_t ret = m_OutputWindowID++;

	m_OutputWindows[ret] = win;

	return 0;
}

void GLReplay::DestroyOutputWindow(uint64_t id)
{
	auto it = m_OutputWindows.find(id);
	if(id == 0 || it == m_OutputWindows.end())
		return;

	OutputWindow &outw = it->second;

	glXDestroyCtxProc(outw.dpy, outw.ctx);

	m_OutputWindows.erase(it);
}

void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return;

	OutputWindow &outw = m_OutputWindows[id];

	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_WIDTH, (unsigned int *)&w);
	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_HEIGHT, (unsigned int *)&h);
}

bool GLReplay::IsOutputWindowVisible(uint64_t id)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return false;

	GLNOTIMP("Optimisation missing - output window always returning true");

	return true;
}

const GLHookSet &GetRealFunctions();

ReplayCreateStatus GL_CreateReplayDevice(const wchar_t *logfile, IReplayDriver **driver)
{
	RDCDEBUG("Creating an OpenGL replay device");

	if(glXCreateContextAttribsProc == NULL)
	{
		glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress");
		glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext");
		glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers");
		glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig");
		glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbufferProc");
		glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer");
		glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable");

		if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL ||
			 glXSwapProc == NULL || glXChooseFBConfigProc == NULL ||
			 glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL ||
			 glXQueryDrawableProc == NULL)
		{
			RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers");
			return eReplayCreate_APIInitFailed;
		}

		glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB");
		glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent");

		if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL)
		{
			RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent");
			return eReplayCreate_APIInitFailed;
		}
	}

	GLInitParams initParams;
	RDCDriver driverType = RDC_OpenGL;
	wstring driverName = L"OpenGL";
	if(logfile)
		RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams);

	if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION)
	{
		RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion);
		return eReplayCreate_APIIncompatibleVersion;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	Display *dpy = XOpenDisplay(NULL);

	if(dpy == NULL)
	{
		RDCERR("Couldn't open default X display");
		return eReplayCreate_APIInitFailed;
	}

	// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
	static int visAttribs[] = { 0 };
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return eReplayCreate_APIHardwareUnsupported;
	}

	// don't care about pbuffer properties for same reason as backbuffer
	int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

	GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

	XFree(fbcfg);

	Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx);

	if(!res)
	{
		glXDestroyPbufferProc(dpy, pbuffer);
		glXDestroyCtxProc(dpy, ctx);
		XCloseDisplay(dpy);
		RDCERR("Couldn't make pbuffer & context current");
		return eReplayCreate_APIInitFailed;
	}

	WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealFunctions());
	gl->Initialise(initParams);

	RDCLOG("Created device.");
	GLReplay *replay = gl->GetReplay();
	replay->SetProxy(logfile == NULL);
	GLWindowingData data;
	data.dpy = dpy;
	data.ctx = ctx;
	data.wnd = pbuffer;
	replay->SetReplayData(data);

	*driver = (IReplayDriver *)replay;
	return eReplayCreate_Success;
}
<commit_msg>Fix dlsym() call to fetch right function name<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Crytek
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_replay.h"
#include "gl_driver.h"
#include "gl_resources.h"

#include <dlfcn.h>

typedef Bool (*PFNGLXMAKECURRENTPROC)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display *dpy, GLXContext ctx);
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display *dpy, GLXDrawable drawable);

PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfigProc = NULL;
PFNGLXCREATEPBUFFERPROC glXCreatePbufferProc = NULL;
PFNGLXDESTROYPBUFFERPROC glXDestroyPbufferProc = NULL;
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsProc = NULL;
PFNGLXGETPROCADDRESSPROC glXGetFuncProc = NULL;
PFNGLXMAKECONTEXTCURRENTPROC glXMakeContextCurrentProc = NULL;
PFNGLXQUERYDRAWABLEPROC glXQueryDrawableProc = NULL;
PFNGLXDESTROYCONTEXTPROC glXDestroyCtxProc = NULL;
PFNGLXSWAPBUFFERSPROC glXSwapProc = NULL;

void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx)
{
	static GLWindowingData *prev = NULL;

	if(glXMakeContextCurrentProc && ctx && ctx != prev)
	{
		prev = ctx;
		glXMakeContextCurrentProc(ctx->dpy, ctx->wnd, ctx->wnd, ctx->ctx);
	}
}

void GLReplay::SwapBuffers(GLWindowingData *ctx)
{
	glXSwapProc(ctx->dpy, ctx->wnd);
}

void GLReplay::CloseReplayContext()
{
	if(glXDestroyCtxProc)
	{
		glXDestroyCtxProc(m_ReplayCtx.dpy, m_ReplayCtx.ctx);
	}
}

uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth)
{
	void **displayAndDrawable = (void **)wn;

	Display *dpy = NULL;
	GLXDrawable wnd = 0;

	if(wn)
	{
		dpy = (Display *)displayAndDrawable[0];
		wnd = (GLXDrawable)displayAndDrawable[1];
	}
	else
	{
		dpy = XOpenDisplay(NULL);

		if(dpy == NULL)
			return 0;

		// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
		static int visAttribs[] = { 0 };
		int numCfgs = 0;
		GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

		if(fbcfg == NULL)
		{
			XCloseDisplay(dpy);
			RDCERR("Couldn't choose default framebuffer config");
			return 0;
		}

		// don't care about pbuffer properties for same reason as backbuffer
		int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

		wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

		XFree(fbcfg);
	}

	static int visAttribs[] = {
		GLX_X_RENDERABLE, True,
		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
		GLX_RED_SIZE, 8,
		GLX_GREEN_SIZE, 8,
		GLX_BLUE_SIZE, 8,
		GLX_ALPHA_SIZE, 8,
		GLX_DOUBLEBUFFER, True,
		0
	};
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return 0;
	}

	XFree(fbcfg);

	OutputWindow win;
	win.dpy = dpy;
	win.ctx = ctx;
	win.wnd = wnd;

	glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width);
	glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height);

	MakeCurrentReplayContext(&win);

	InitOutputWindow(win);
	CreateOutputWindowBackbuffer(win);

	uint64_t ret = m_OutputWindowID++;

	m_OutputWindows[ret] = win;

	return 0;
}

void GLReplay::DestroyOutputWindow(uint64_t id)
{
	auto it = m_OutputWindows.find(id);
	if(id == 0 || it == m_OutputWindows.end())
		return;

	OutputWindow &outw = it->second;

	glXDestroyCtxProc(outw.dpy, outw.ctx);

	m_OutputWindows.erase(it);
}

void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return;

	OutputWindow &outw = m_OutputWindows[id];

	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_WIDTH, (unsigned int *)&w);
	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_HEIGHT, (unsigned int *)&h);
}

bool GLReplay::IsOutputWindowVisible(uint64_t id)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return false;

	GLNOTIMP("Optimisation missing - output window always returning true");

	return true;
}

const GLHookSet &GetRealFunctions();

ReplayCreateStatus GL_CreateReplayDevice(const wchar_t *logfile, IReplayDriver **driver)
{
	RDCDEBUG("Creating an OpenGL replay device");

	if(glXCreateContextAttribsProc == NULL)
	{
		glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress");
		glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext");
		glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers");
		glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig");
		glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbuffer");
		glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer");
		glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable");

		if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL ||
			 glXSwapProc == NULL || glXChooseFBConfigProc == NULL ||
			 glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL ||
			 glXQueryDrawableProc == NULL)
		{
			RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers");
			return eReplayCreate_APIInitFailed;
		}

		glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB");
		glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent");

		if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL)
		{
			RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent");
			return eReplayCreate_APIInitFailed;
		}
	}

	GLInitParams initParams;
	RDCDriver driverType = RDC_OpenGL;
	wstring driverName = L"OpenGL";
	if(logfile)
		RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams);

	if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION)
	{
		RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion);
		return eReplayCreate_APIIncompatibleVersion;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	Display *dpy = XOpenDisplay(NULL);

	if(dpy == NULL)
	{
		RDCERR("Couldn't open default X display");
		return eReplayCreate_APIInitFailed;
	}

	// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
	static int visAttribs[] = { 0 };
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return eReplayCreate_APIHardwareUnsupported;
	}

	// don't care about pbuffer properties for same reason as backbuffer
	int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

	GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

	XFree(fbcfg);

	Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx);

	if(!res)
	{
		glXDestroyPbufferProc(dpy, pbuffer);
		glXDestroyCtxProc(dpy, ctx);
		XCloseDisplay(dpy);
		RDCERR("Couldn't make pbuffer & context current");
		return eReplayCreate_APIInitFailed;
	}

	WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealFunctions());
	gl->Initialise(initParams);

	RDCLOG("Created device.");
	GLReplay *replay = gl->GetReplay();
	replay->SetProxy(logfile == NULL);
	GLWindowingData data;
	data.dpy = dpy;
	data.ctx = ctx;
	data.wnd = pbuffer;
	replay->SetReplayData(data);

	*driver = (IReplayDriver *)replay;
	return eReplayCreate_Success;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Crytek
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_replay.h"
#include "gl_driver.h"
#include "gl_resources.h"

#include <dlfcn.h>

typedef Bool (*PFNGLXMAKECURRENTPROC)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display *dpy, GLXContext ctx);
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display *dpy, GLXDrawable drawable);

PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfigProc = NULL;
PFNGLXCREATEPBUFFERPROC glXCreatePbufferProc = NULL;
PFNGLXDESTROYPBUFFERPROC glXDestroyPbufferProc = NULL;
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsProc = NULL;
PFNGLXGETPROCADDRESSPROC glXGetFuncProc = NULL;
PFNGLXMAKECONTEXTCURRENTPROC glXMakeContextCurrentProc = NULL;
PFNGLXQUERYDRAWABLEPROC glXQueryDrawableProc = NULL;
PFNGLXDESTROYCONTEXTPROC glXDestroyCtxProc = NULL;
PFNGLXSWAPBUFFERSPROC glXSwapProc = NULL;

void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx)
{
	static GLWindowingData *prev = NULL;

	if(glXMakeContextCurrentProc && ctx && ctx != prev)
	{
		prev = ctx;
		glXMakeContextCurrentProc(ctx->dpy, ctx->wnd, ctx->wnd, ctx->ctx);
	}
}

void GLReplay::SwapBuffers(GLWindowingData *ctx)
{
	glXSwapProc(ctx->dpy, ctx->wnd);
}

void GLReplay::CloseReplayContext()
{
	if(glXDestroyCtxProc)
	{
		glXDestroyCtxProc(m_ReplayCtx.dpy, m_ReplayCtx.ctx);
	}
}

uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth)
{
	void **displayAndDrawable = (void **)wn;

	Display *dpy = NULL;
	GLXDrawable wnd = 0;

	if(wn)
	{
		dpy = (Display *)displayAndDrawable[0];
		wnd = (GLXDrawable)displayAndDrawable[1];
	}
	else
	{
		dpy = XOpenDisplay(NULL);

		if(dpy == NULL)
			return 0;

		// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
		static int visAttribs[] = { 0 };
		int numCfgs = 0;
		GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

		if(fbcfg == NULL)
		{
			XCloseDisplay(dpy);
			RDCERR("Couldn't choose default framebuffer config");
			return 0;
		}

		// don't care about pbuffer properties for same reason as backbuffer
		int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

		wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

		XFree(fbcfg);
	}

	static int visAttribs[] = {
		GLX_X_RENDERABLE, True,
		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
		GLX_RED_SIZE, 8,
		GLX_GREEN_SIZE, 8,
		GLX_BLUE_SIZE, 8,
		GLX_ALPHA_SIZE, 8,
		GLX_DOUBLEBUFFER, True,
		0
	};
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return 0;
	}

	XFree(fbcfg);

	OutputWindow win;
	win.dpy = dpy;
	win.ctx = ctx;
	win.wnd = wnd;

	glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width);
	glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height);

	MakeCurrentReplayContext(&win);

	InitOutputWindow(win);
	CreateOutputWindowBackbuffer(win);

	uint64_t ret = m_OutputWindowID++;

	m_OutputWindows[ret] = win;

	return 0;
}

void GLReplay::DestroyOutputWindow(uint64_t id)
{
	auto it = m_OutputWindows.find(id);
	if(id == 0 || it == m_OutputWindows.end())
		return;

	OutputWindow &outw = it->second;

	glXDestroyCtxProc(outw.dpy, outw.ctx);

	m_OutputWindows.erase(it);
}

void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return;

	OutputWindow &outw = m_OutputWindows[id];

	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_WIDTH, (unsigned int *)&w);
	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_HEIGHT, (unsigned int *)&h);
}

bool GLReplay::IsOutputWindowVisible(uint64_t id)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return false;

	GLNOTIMP("Optimisation missing - output window always returning true");

	return true;
}

const GLHookSet &GetRealFunctions();

ReplayCreateStatus GL_CreateReplayDevice(const wchar_t *logfile, IReplayDriver **driver)
{
	RDCDEBUG("Creating an OpenGL replay device");

	if(glXCreateContextAttribsProc == NULL)
	{
		glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress");
		glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext");
		glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers");
		glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig");
		glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbuffer");
		glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer");
		glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable");

		if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL ||
			 glXSwapProc == NULL || glXChooseFBConfigProc == NULL ||
			 glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL ||
			 glXQueryDrawableProc == NULL)
		{
			RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers");
			return eReplayCreate_APIInitFailed;
		}

		glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB");
		glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent");

		if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL)
		{
			RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent");
			return eReplayCreate_APIInitFailed;
		}
	}

	GLInitParams initParams;
	RDCDriver driverType = RDC_OpenGL;
	wstring driverName = L"OpenGL";
	if(logfile)
		RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams);

	if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION)
	{
		RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion);
		return eReplayCreate_APIIncompatibleVersion;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	Display *dpy = XOpenDisplay(NULL);

	if(dpy == NULL)
	{
		RDCERR("Couldn't open default X display");
		return eReplayCreate_APIInitFailed;
	}

	// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
	static int visAttribs[] = { 0 };
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return eReplayCreate_APIHardwareUnsupported;
	}

	// don't care about pbuffer properties for same reason as backbuffer
	int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

	GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

	XFree(fbcfg);

	Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx);

	if(!res)
	{
		glXDestroyPbufferProc(dpy, pbuffer);
		glXDestroyCtxProc(dpy, ctx);
		XCloseDisplay(dpy);
		RDCERR("Couldn't make pbuffer & context current");
		return eReplayCreate_APIInitFailed;
	}

	WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealFunctions());
	gl->Initialise(initParams);

	RDCLOG("Created device.");
	GLReplay *replay = gl->GetReplay();
	replay->SetProxy(logfile == NULL);
	GLWindowingData data;
	data.dpy = dpy;
	data.ctx = ctx;
	data.wnd = pbuffer;
	replay->SetReplayData(data);

	*driver = (IReplayDriver *)replay;
	return eReplayCreate_Success;
}
<commit_msg>Return ID of output window<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Crytek
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_replay.h"
#include "gl_driver.h"
#include "gl_resources.h"

#include <dlfcn.h>

typedef Bool (*PFNGLXMAKECURRENTPROC)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display *dpy, GLXContext ctx);
typedef void (*PFNGLXSWAPBUFFERSPROC)(Display *dpy, GLXDrawable drawable);

PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfigProc = NULL;
PFNGLXCREATEPBUFFERPROC glXCreatePbufferProc = NULL;
PFNGLXDESTROYPBUFFERPROC glXDestroyPbufferProc = NULL;
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsProc = NULL;
PFNGLXGETPROCADDRESSPROC glXGetFuncProc = NULL;
PFNGLXMAKECONTEXTCURRENTPROC glXMakeContextCurrentProc = NULL;
PFNGLXQUERYDRAWABLEPROC glXQueryDrawableProc = NULL;
PFNGLXDESTROYCONTEXTPROC glXDestroyCtxProc = NULL;
PFNGLXSWAPBUFFERSPROC glXSwapProc = NULL;

void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx)
{
	static GLWindowingData *prev = NULL;

	if(glXMakeContextCurrentProc && ctx && ctx != prev)
	{
		prev = ctx;
		glXMakeContextCurrentProc(ctx->dpy, ctx->wnd, ctx->wnd, ctx->ctx);
	}
}

void GLReplay::SwapBuffers(GLWindowingData *ctx)
{
	glXSwapProc(ctx->dpy, ctx->wnd);
}

void GLReplay::CloseReplayContext()
{
	if(glXDestroyCtxProc)
	{
		glXDestroyCtxProc(m_ReplayCtx.dpy, m_ReplayCtx.ctx);
	}
}

uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth)
{
	void **displayAndDrawable = (void **)wn;

	Display *dpy = NULL;
	GLXDrawable wnd = 0;

	if(wn)
	{
		dpy = (Display *)displayAndDrawable[0];
		wnd = (GLXDrawable)displayAndDrawable[1];
	}
	else
	{
		dpy = XOpenDisplay(NULL);

		if(dpy == NULL)
			return 0;

		// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
		static int visAttribs[] = { 0 };
		int numCfgs = 0;
		GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

		if(fbcfg == NULL)
		{
			XCloseDisplay(dpy);
			RDCERR("Couldn't choose default framebuffer config");
			return 0;
		}

		// don't care about pbuffer properties for same reason as backbuffer
		int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

		wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

		XFree(fbcfg);
	}

	static int visAttribs[] = {
		GLX_X_RENDERABLE, True,
		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
		GLX_RED_SIZE, 8,
		GLX_GREEN_SIZE, 8,
		GLX_BLUE_SIZE, 8,
		GLX_ALPHA_SIZE, 8,
		GLX_DOUBLEBUFFER, True,
		0
	};
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return 0;
	}

	XFree(fbcfg);

	OutputWindow win;
	win.dpy = dpy;
	win.ctx = ctx;
	win.wnd = wnd;

	glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width);
	glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height);

	MakeCurrentReplayContext(&win);

	InitOutputWindow(win);
	CreateOutputWindowBackbuffer(win);

	uint64_t ret = m_OutputWindowID++;

	m_OutputWindows[ret] = win;

	return ret;
}

void GLReplay::DestroyOutputWindow(uint64_t id)
{
	auto it = m_OutputWindows.find(id);
	if(id == 0 || it == m_OutputWindows.end())
		return;

	OutputWindow &outw = it->second;

	glXDestroyCtxProc(outw.dpy, outw.ctx);

	m_OutputWindows.erase(it);
}

void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return;

	OutputWindow &outw = m_OutputWindows[id];

	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_WIDTH, (unsigned int *)&w);
	glXQueryDrawableProc(outw.dpy, outw.wnd, GLX_HEIGHT, (unsigned int *)&h);
}

bool GLReplay::IsOutputWindowVisible(uint64_t id)
{
	if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
		return false;

	GLNOTIMP("Optimisation missing - output window always returning true");

	return true;
}

const GLHookSet &GetRealFunctions();

ReplayCreateStatus GL_CreateReplayDevice(const wchar_t *logfile, IReplayDriver **driver)
{
	RDCDEBUG("Creating an OpenGL replay device");

	if(glXCreateContextAttribsProc == NULL)
	{
		glXGetFuncProc = (PFNGLXGETPROCADDRESSPROC)dlsym(RTLD_NEXT, "glXGetProcAddress");
		glXDestroyCtxProc = (PFNGLXDESTROYCONTEXTPROC)dlsym(RTLD_NEXT, "glXDestroyContext");
		glXSwapProc = (PFNGLXSWAPBUFFERSPROC)dlsym(RTLD_NEXT, "glXSwapBuffers");
		glXChooseFBConfigProc = (PFNGLXCHOOSEFBCONFIGPROC)dlsym(RTLD_NEXT, "glXChooseFBConfig");
		glXCreatePbufferProc = (PFNGLXCREATEPBUFFERPROC)dlsym(RTLD_NEXT, "glXCreatePbuffer");
		glXDestroyPbufferProc = (PFNGLXDESTROYPBUFFERPROC)dlsym(RTLD_NEXT, "glXDestroyPbuffer");
		glXQueryDrawableProc = (PFNGLXQUERYDRAWABLEPROC)dlsym(RTLD_NEXT, "glXQueryDrawable");

		if(glXGetFuncProc == NULL || glXDestroyCtxProc == NULL ||
			 glXSwapProc == NULL || glXChooseFBConfigProc == NULL ||
			 glXCreatePbufferProc == NULL || glXDestroyPbufferProc == NULL ||
			 glXQueryDrawableProc == NULL)
		{
			RDCERR("Couldn't find required entry points, glXGetProcAddress glXDestroyContext glXSwapBuffers");
			return eReplayCreate_APIInitFailed;
		}

		glXCreateContextAttribsProc = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetFuncProc((const GLubyte*)"glXCreateContextAttribsARB");
		glXMakeContextCurrentProc = (PFNGLXMAKECONTEXTCURRENTPROC)glXGetFuncProc((const GLubyte*)"glXMakeContextCurrent");

		if(glXCreateContextAttribsProc == NULL || glXMakeContextCurrentProc == NULL)
		{
			RDCERR("Couldn't get glx function addresses, glXCreateContextAttribsARB glXMakeContextCurrent");
			return eReplayCreate_APIInitFailed;
		}
	}

	GLInitParams initParams;
	RDCDriver driverType = RDC_OpenGL;
	wstring driverName = L"OpenGL";
	if(logfile)
		RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams);

	if(initParams.SerialiseVersion != GLInitParams::GL_SERIALISE_VERSION)
	{
		RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GLInitParams::GL_SERIALISE_VERSION, initParams.SerialiseVersion);
		return eReplayCreate_APIIncompatibleVersion;
	}

	int attribs[64] = {0};
	int i=0;

	attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
	attribs[i++] = 4;
	attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
	attribs[i++] = 3;
	attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
	attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB;

	Display *dpy = XOpenDisplay(NULL);

	if(dpy == NULL)
	{
		RDCERR("Couldn't open default X display");
		return eReplayCreate_APIInitFailed;
	}

	// don't need to care about the fb config as we won't be using the default framebuffer (backbuffer)
	static int visAttribs[] = { 0 };
	int numCfgs = 0;
	GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs);

	if(fbcfg == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't choose default framebuffer config");
		return eReplayCreate_APIInitFailed;
	}

	GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], 0, true, attribs);

	if(ctx == NULL)
	{
		XCloseDisplay(dpy);
		RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability");
		return eReplayCreate_APIHardwareUnsupported;
	}

	// don't care about pbuffer properties for same reason as backbuffer
	int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 };

	GLXPbuffer pbuffer = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs);

	XFree(fbcfg);

	Bool res = glXMakeContextCurrentProc(dpy, pbuffer, pbuffer, ctx);

	if(!res)
	{
		glXDestroyPbufferProc(dpy, pbuffer);
		glXDestroyCtxProc(dpy, ctx);
		XCloseDisplay(dpy);
		RDCERR("Couldn't make pbuffer & context current");
		return eReplayCreate_APIInitFailed;
	}

	WrappedOpenGL *gl = new WrappedOpenGL(logfile, GetRealFunctions());
	gl->Initialise(initParams);

	RDCLOG("Created device.");
	GLReplay *replay = gl->GetReplay();
	replay->SetProxy(logfile == NULL);
	GLWindowingData data;
	data.dpy = dpy;
	data.ctx = ctx;
	data.wnd = pbuffer;
	replay->SetReplayData(data);

	*driver = (IReplayDriver *)replay;
	return eReplayCreate_Success;
}
<|endoftext|>


<commit_before>/**
 * Appcelerator Kroll - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */
#include "utils.h"
#if defined(KROLL_HOST_EXPORT) || defined(KROLL_API_EXPORT) || defined(_KROLL_H_)
#include <Poco/URI.h>
#endif
namespace UTILS_NS
{
	namespace
	{
		const char HEX2DEC[256] =
		{
			/*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
			/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,

			/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

			/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

			/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
		};

		// Only alphanum is safe.
		const char SAFE[256] =
		{
			/*      0 1 2 3  4 5 6 7  8 9 A B  C D E F */
			/* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,1,0,
			/* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,

			/* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
			/* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,1,
			/* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
			/* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,

			/* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,

			/* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
		};
	}

	std::string URLUtils::EncodeURIComponent(std::string src)
	{
		const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
		const unsigned char *pSrc = (const unsigned char *)src.c_str();
		const int SRC_LEN = (int) src.length();
		unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
		unsigned char * pEnd = pStart;
		const unsigned char * const SRC_END = pSrc + SRC_LEN;

		for (; pSrc < SRC_END; ++pSrc)
		{
			if (SAFE[*pSrc])
				*pEnd++ = *pSrc;
			else
			{
				// escape this char
				*pEnd++ = '%';
				*pEnd++ = DEC2HEX[*pSrc >> 4];
				*pEnd++ = DEC2HEX[*pSrc & 0x0F];
			}
		}

		std::string sResult((char *)pStart, (char *)pEnd);
		delete [] pStart;

		return sResult;
	}

	std::string URLUtils::DecodeURIComponent(std::string src)
	{
		// Note from RFC1630: "Sequences which start with a percent
		// sign but are not followed by two hexadecimal characters
		// (0-9, A-F) are reserved for future extension"

		const unsigned char * pSrc = (const unsigned char *)src.c_str();
		const int SRC_LEN = (int) src.length();
		const unsigned char * const SRC_END = pSrc + SRC_LEN;
		// last decodable '%'
		const unsigned char * const SRC_LAST_DEC = SRC_END - 2;

		char * const pStart = new char[SRC_LEN];
		char * pEnd = pStart;

		while (pSrc < SRC_LAST_DEC)
		{
			if (*pSrc == '%')
			{
				char dec1, dec2;
				if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
					&& -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
				{
					*pEnd++ = (dec1 << 4) + dec2;
					pSrc += 3;
					continue;
				}
			}

			*pEnd++ = *pSrc++;
		}

		// the last 2- chars
		while (pSrc < SRC_END)
			*pEnd++ = *pSrc++;

		std::string sResult(pStart, pEnd);
		delete [] pStart;

		return sResult;
	}

	std::string URLUtils::FileURLToPath(std::string url)
	{
		size_t fileLength = 7; // file://
		if (url.find("file://") == 0)
		{
			url = url.substr(fileLength);
		}

		if ('/' != KR_PATH_SEP_CHAR)
		{
			for (size_t i = 0; i < url.size(); i++)
			{
				if (url[i] == '/')
				{
						url[i] = KR_PATH_SEP_CHAR;
				}
			}
		}
		return url;
	}

	std::string URLUtils::PathToFileURL(std::string path)
	{
		if ('\\' == KR_PATH_SEP_CHAR)
		{
			for (size_t i = 0; i < path.size(); i++)
			{
				if (path[i] == '\\')
				{
					path[i] = '/';
				}
			}
		}

		std::string url("file:/");
		std::vector<std::string> pieces;
		std::string delim = "/";
		FileUtils::Tokenize(path, pieces, delim);
		for (size_t i = 0; i < pieces.size(); i++)
		{
#if OS_WIN32
			// Don't encode the C:
			if (i != 0)
#endif
			{
				pieces[i] = EncodeURIComponent(pieces[i]);
			}
			url.append("/");
			url.append(pieces[i]);
		}
		return url;
	}

#if defined(KROLL_HOST_EXPORT) || defined(KROLL_API_EXPORT) || defined(_KROLL_H_)
	std::string URLUtils::NormalizeURL(std::string& url)
	{
		Poco::URI inURI = Poco::URI(url);
		if (inURI.getScheme() != "app")
		{
			return url;
		}
		else
		{
			return NormalizeAppURL(url);
		}
	}

	std::string URLUtils::URLToPath(std::string& url)
	{
		Poco::URI inURI = Poco::URI(url);
		try
		{
			if (inURI.getScheme() == "ti")
			{
				return TiURLToPath(url);
			}
			else if (inURI.getScheme() == "app")
			{
				return AppURLToPath(url);
			}
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", url.c_str(), ss->c_str());
		}
		return url;
	}

	std::string URLUtils::TiURLToPath(std::string& tiURL)
	{
		try
		{
			Poco::URI inURI = Poco::URI(tiURL);

			if (inURI.getScheme() != "ti")
			{
				return tiURL;
			}

			std::string host = inURI.getHost();
			SharedApplication app = Host::GetInstance()->GetApplication();
			std::string path = app->GetComponentPath(host);

			if (path.empty())
			{
				throw ValueException::FromString("Could not find component "+host);
			}

			std::vector<std::string> segments;
			inURI.getPathSegments(segments);

			for (size_t i = 0; i < segments.size(); i++)
			{
				path = FileUtils::Join(path.c_str(), segments[i].c_str(), NULL);
			}
			return path;
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", tiURL.c_str(), ss->c_str());
		}
		catch (...)
		{
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path", tiURL.c_str());
		}
		return tiURL;
	}

	std::string URLUtils::NormalizeAppURL(std::string& url)
	{
		size_t appLength = 6; // app://
		std::string id = Host::GetInstance()->GetApplication()->id;
		size_t idLength = id.size();
		std::string idPart = url.substr(appLength, idLength);

		if (idPart == id)
		{
			return url;
		}
		else
		{
			return std::string("app://") + id + "/" + url.substr(appLength);
		}
	}

	std::string URLUtils::AppURLToPath(std::string& inURL)
	{
		try
		{
			Poco::URI inURI = Poco::URI(inURL);
			if (inURI.getScheme() != "app")
			{
				return inURL;
			}

			std::string appURL = NormalizeAppURL(inURL);
			inURI = Poco::URI(appURL);

			SharedApplication app = Host::GetInstance()->GetApplication();
			std::string path = app->GetResourcesPath();

			std::vector<std::string> segments;
			inURI.getPathSegments(segments);
			for (size_t i = 0; i < segments.size(); i++)
			{
				path = FileUtils::Join(path.c_str(), segments[i].c_str(), NULL);
			}
			return path;
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", inURL.c_str(), ss->c_str());
		}
		catch (...)
		{
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path", inURL.c_str());
		}

		return inURL;
	}
#endif
}
<commit_msg>URLUtils fix for non-Win32.<commit_after>/**
 * Appcelerator Kroll - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */
#include "utils.h"
#if defined(KROLL_HOST_EXPORT) || defined(KROLL_API_EXPORT) || defined(_KROLL_H_)
#include <Poco/URI.h>
#endif
namespace UTILS_NS
{
	namespace
	{
		const char HEX2DEC[256] =
		{
			/*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
			/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,

			/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

			/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

			/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
			/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
		};

		// Only alphanum is safe.
		const char SAFE[256] =
		{
			/*      0 1 2 3  4 5 6 7  8 9 A B  C D E F */
			/* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,1,0,
			/* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,

			/* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
			/* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,1,
			/* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
			/* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,

			/* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,

			/* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
			/* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
		};
	}

	std::string URLUtils::EncodeURIComponent(std::string src)
	{
		const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
		const unsigned char *pSrc = (const unsigned char *)src.c_str();
		const int SRC_LEN = (int) src.length();
		unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
		unsigned char * pEnd = pStart;
		const unsigned char * const SRC_END = pSrc + SRC_LEN;

		for (; pSrc < SRC_END; ++pSrc)
		{
			if (SAFE[*pSrc])
				*pEnd++ = *pSrc;
			else
			{
				// escape this char
				*pEnd++ = '%';
				*pEnd++ = DEC2HEX[*pSrc >> 4];
				*pEnd++ = DEC2HEX[*pSrc & 0x0F];
			}
		}

		std::string sResult((char *)pStart, (char *)pEnd);
		delete [] pStart;

		return sResult;
	}

	std::string URLUtils::DecodeURIComponent(std::string src)
	{
		// Note from RFC1630: "Sequences which start with a percent
		// sign but are not followed by two hexadecimal characters
		// (0-9, A-F) are reserved for future extension"

		const unsigned char * pSrc = (const unsigned char *)src.c_str();
		const int SRC_LEN = (int) src.length();
		const unsigned char * const SRC_END = pSrc + SRC_LEN;
		// last decodable '%'
		const unsigned char * const SRC_LAST_DEC = SRC_END - 2;

		char * const pStart = new char[SRC_LEN];
		char * pEnd = pStart;

		while (pSrc < SRC_LAST_DEC)
		{
			if (*pSrc == '%')
			{
				char dec1, dec2;
				if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
					&& -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
				{
					*pEnd++ = (dec1 << 4) + dec2;
					pSrc += 3;
					continue;
				}
			}

			*pEnd++ = *pSrc++;
		}

		// the last 2- chars
		while (pSrc < SRC_END)
			*pEnd++ = *pSrc++;

		std::string sResult(pStart, pEnd);
		delete [] pStart;

		return sResult;
	}

	std::string URLUtils::FileURLToPath(std::string url)
	{
		size_t fileLength = 7; // file://
		if (url.find("file://") == 0)
		{
			url = url.substr(fileLength);
		}

		if ('/' != KR_PATH_SEP_CHAR)
		{
			for (size_t i = 0; i < url.size(); i++)
			{
				if (url[i] == '/')
				{
						url[i] = KR_PATH_SEP_CHAR;
				}
			}
		}
		return url;
	}

	std::string URLUtils::PathToFileURL(std::string path)
	{
		if ('\\' == KR_PATH_SEP_CHAR)
		{
			for (size_t i = 0; i < path.size(); i++)
			{
				if (path[i] == '\\')
				{
					path[i] = '/';
				}
			}
		}

		std::string url("file://");
		std::vector<std::string> pieces;
		std::string delim = "/";
		FileUtils::Tokenize(path, pieces, delim);
		for (size_t i = 0; i < pieces.size(); i++)
		{
#if OS_WIN32
			// Don't encode the C:
			if (i != 0)
#endif
			{
				url.append("/");
				pieces[i] = EncodeURIComponent(pieces[i]);
			}

			url.append(pieces[i]);
		}
		return url;
	}

#if defined(KROLL_HOST_EXPORT) || defined(KROLL_API_EXPORT) || defined(_KROLL_H_)
	std::string URLUtils::NormalizeURL(std::string& url)
	{
		Poco::URI inURI = Poco::URI(url);
		if (inURI.getScheme() != "app")
		{
			return url;
		}
		else
		{
			return NormalizeAppURL(url);
		}
	}

	std::string URLUtils::URLToPath(std::string& url)
	{
		Poco::URI inURI = Poco::URI(url);
		try
		{
			if (inURI.getScheme() == "ti")
			{
				return TiURLToPath(url);
			}
			else if (inURI.getScheme() == "app")
			{
				return AppURLToPath(url);
			}
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", url.c_str(), ss->c_str());
		}
		return url;
	}

	std::string URLUtils::TiURLToPath(std::string& tiURL)
	{
		try
		{
			Poco::URI inURI = Poco::URI(tiURL);

			if (inURI.getScheme() != "ti")
			{
				return tiURL;
			}

			std::string host = inURI.getHost();
			SharedApplication app = Host::GetInstance()->GetApplication();
			std::string path = app->GetComponentPath(host);

			if (path.empty())
			{
				throw ValueException::FromString("Could not find component "+host);
			}

			std::vector<std::string> segments;
			inURI.getPathSegments(segments);

			for (size_t i = 0; i < segments.size(); i++)
			{
				path = FileUtils::Join(path.c_str(), segments[i].c_str(), NULL);
			}
			return path;
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", tiURL.c_str(), ss->c_str());
		}
		catch (...)
		{
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path", tiURL.c_str());
		}
		return tiURL;
	}

	std::string URLUtils::NormalizeAppURL(std::string& url)
	{
		size_t appLength = 6; // app://
		std::string id = Host::GetInstance()->GetApplication()->id;
		size_t idLength = id.size();
		std::string idPart = url.substr(appLength, idLength);

		if (idPart == id)
		{
			return url;
		}
		else
		{
			return std::string("app://") + id + "/" + url.substr(appLength);
		}
	}

	std::string URLUtils::AppURLToPath(std::string& inURL)
	{
		try
		{
			Poco::URI inURI = Poco::URI(inURL);
			if (inURI.getScheme() != "app")
			{
				return inURL;
			}

			std::string appURL = NormalizeAppURL(inURL);
			inURI = Poco::URI(appURL);

			SharedApplication app = Host::GetInstance()->GetApplication();
			std::string path = app->GetResourcesPath();

			std::vector<std::string> segments;
			inURI.getPathSegments(segments);
			for (size_t i = 0; i < segments.size(); i++)
			{
				path = FileUtils::Join(path.c_str(), segments[i].c_str(), NULL);
			}
			return path;
		}
		catch (ValueException& e)
		{
			SharedString ss = e.DisplayString();
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path: %s", inURL.c_str(), ss->c_str());
		}
		catch (...)
		{
			Logger* log = Logger::Get("URLUtils");
			log->Error("Could not convert %s to a path", inURL.c_str());
		}

		return inURL;
	}
#endif
}
<|endoftext|>