<commit_before>#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <google/protobuf/message.h>
#include <boost/asio.hpp>
#include "base/header.hpp"
#include "base/atimer.hpp"
#include "base/scope_guard.hpp"
#include "base/serialize_util.hpp"
#include "base/thread_pool.hpp"
#include "base/table/threadsafe_list.hpp"
#include "base/table/threadsafe_unordered_map.hpp"
#include "base/czlog.hpp"

using namespace czrpc::base;
using namespace czrpc::base::table;

namespace czrpc
{
namespace client
{
class client_base
{
public:
    client_base() : work_(ios_), socket_(ios_), is_connected_(false) {}
    virtual ~client_base()
    {
        stop();
    }

    client_base& connect(const endpoint& ep)
    {
        boost::asio::ip::tcp::resolver resolver(ios_);
        boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), ep.ip, std::to_string(ep.port));
        endpoint_iter_ = resolver.resolve(query);
        return *this;
    }

    client_base& timeout(std::size_t timeout_milli)
    {
        timeout_milli_ = timeout_milli;
        return *this;
    }

    virtual void run()
    {
        start_ios_thread();
    }

    virtual void stop()
    {
        is_stoped_ = true;
        disconnect();
        stop_ios_thread();
    }

    void set_connect_success_notify(const std::function<void()> func)
    {
        connect_success_notify_ = func;
    }

    void async_write(const request_content& content)
    {
        request_header header;
        header.protocol_len = content.protocol.size();
        header.message_name_len = content.message_name.size();
        header.body_len = content.body.size();

        auto buffer = get_buffer(request_data{ header, content });
        async_write_impl(buffer);
    }

protected:
    void write(const request_content& content)
    {
        request_header header;
        header.protocol_len = content.protocol.size();
        header.message_name_len = content.message_name.size();
        header.body_len = content.body.size();

        auto buffer = get_buffer(request_data{ header, content });
        write_impl(buffer);
    }

    response_content read()
    {
        read_head();
        memcpy(&rsp_head_, rsp_head_buf_, sizeof(rsp_head_buf_));
        return std::move(read_content());
    }

    void disconnect()
    {
        is_connected_ = false;
        if (socket_.is_open())
        {
            boost::system::error_code ignore_ec;
            socket_.shutdown(boost::asio::socket_base::shutdown_both, ignore_ec);
            socket_.close(ignore_ec);
        }
    }

    boost::asio::ip::tcp::socket& get_socket()
    {
        return socket_;
    }

    bool try_connect()
    {
        if (!is_connected_)
        {
            std::lock_guard<std::mutex> lock(conn_mutex_);
            if (!is_connected_)
            {
                connect();
                is_connected_ = true;
                return true;
            }
        }
        return false;
    }

    response_content make_response_content()
    {
        response_content content;
        memcpy(&content.call_id, &rsp_content_[0], sizeof(content.call_id));
        memcpy(&content.code, &rsp_content_[sizeof(content.call_id)], sizeof(content.code));
        content.message_name.assign(&rsp_content_[sizeof(content.call_id) + sizeof(content.code)], rsp_head_.message_name_len);
        content.body.assign(&rsp_content_[sizeof(content.call_id) + sizeof(content.code) + rsp_head_.message_name_len], rsp_head_.body_len);
        return std::move(content);
    }

private:
    std::shared_ptr<std::string> get_buffer(const request_data& data)
    {
        auto buffer = std::make_shared<std::string>();
        buffer->append(reinterpret_cast<const char*>(&data.header), sizeof(data.header));
        buffer->append(reinterpret_cast<const char*>(&data.content.call_id), sizeof(data.content.call_id));
        buffer->append(reinterpret_cast<const char*>(&data.content.flag), sizeof(data.content.flag));
        buffer->append(data.content.protocol);
        buffer->append(data.content.message_name);
        buffer->append(data.content.body);
        return buffer;
    }

    void connect()
    {
        auto begin_time = std::chrono::high_resolution_clock::now();
        while (true)
        {
            try
            {
                boost::asio::connect(socket_, endpoint_iter_);
                break;
            }
            catch (std::exception& e)
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(20));
                auto end_time = std::chrono::high_resolution_clock::now();
                auto elapsed_time = end_time - begin_time;
                if (std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() > static_cast<long>(timeout_milli_))
                {
                    throw std::runtime_error(e.what());
                }
            }
        }
    }

    void write_impl(const std::shared_ptr<std::string>& buffer)
    {
        boost::system::error_code ec;
        boost::asio::write(socket_, boost::asio::buffer(*buffer), ec);
        if (ec)
        {
            is_connected_ = false;
            throw std::runtime_error(ec.message());
        }
    }

    void async_write_impl(const std::shared_ptr<std::string>& buffer)
    {
        ios_.post([this, buffer]
        {
            bool is_empty = send_queue_.empty();
            send_queue_.emplace_back(buffer);
            if (is_empty)
            {
                async_write_impl();
            }
        });
    }

    void async_write_impl()
    {
        boost::asio::async_write(socket_, boost::asio::buffer(*send_queue_.front()),
                                 [this](boost::system::error_code ec, std::size_t)
        {
            if (!ec)
            {
                send_queue_.pop_front();
                if (!send_queue_.empty())
                {
                    async_write_impl();
                }
            }
            else
            {
                is_connected_ = false;
                send_queue_.clear();
                log_warn() << ec.message();
            }
        });
    }

    void read_head()
    {
        boost::system::error_code ec;
        boost::asio::read(socket_, boost::asio::buffer(rsp_head_buf_), ec);
        if (ec)
        {
            disconnect();
            throw std::runtime_error(ec.message());
        }
    }

    response_content read_content()
    {
        rsp_content_.clear();
        rsp_content_.resize(sizeof(unsigned int) + sizeof(rpc_error_code) + rsp_head_.message_name_len + rsp_head_.body_len);
        boost::system::error_code ec;
        boost::asio::read(socket_, boost::asio::buffer(rsp_content_), ec);
        if (ec)
        {
            disconnect();
            throw std::runtime_error(ec.message());
        }

        return std::move(make_response_content());
    }

    void start_ios_thread()
    {
        if (thread_ == nullptr)
        {
            thread_ = std::make_unique<std::thread>([this]{ ios_.run(); });
        }
    }

    void stop_ios_thread()
    {
        ios_.stop();
        if (thread_ != nullptr)
        {
            if (thread_->joinable())
            {
                thread_->join();
            }
        }
    }

protected:
    client_type client_type_;
    std::size_t timeout_milli_ = 0;
    char rsp_head_buf_[response_header_len];
    response_header rsp_head_;
    std::vector<char> rsp_content_;
    boost::asio::ip::tcp::resolver::iterator endpoint_iter_;
    std::function<void()> connect_success_notify_ = nullptr;
    std::atomic<bool> is_stoped_{ false };

private:
    boost::asio::io_service ios_;
    boost::asio::io_service::work work_;
    boost::asio::ip::tcp::socket socket_;
    std::unique_ptr<std::thread> thread_ = nullptr;

    std::atomic<bool> is_connected_ ;
    std::mutex conn_mutex_;

    threadsafe_list<std::shared_ptr<std::string>> send_queue_;
};

}
}
<commit_msg>update rpc client<commit_after>#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <google/protobuf/message.h>
#include <boost/asio.hpp>
#include "base/header.hpp"
#include "base/atimer.hpp"
#include "base/scope_guard.hpp"
#include "base/serialize_util.hpp"
#include "base/thread_pool.hpp"
#include "base/table/threadsafe_list.hpp"
#include "base/table/threadsafe_unordered_map.hpp"
#include "base/czlog.hpp"

using namespace czrpc::base;
using namespace czrpc::base::table;

namespace czrpc
{
namespace client
{
class client_base
{
public:
    client_base() : work_(ios_), socket_(ios_), is_connected_(false) {}
    virtual ~client_base()
    {
        stop();
    }

    client_base& connect(const endpoint& ep)
    {
        boost::asio::ip::tcp::resolver resolver(ios_);
        boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), ep.ip, std::to_string(ep.port));
        endpoint_iter_ = resolver.resolve(query);
        return *this;
    }

    client_base& timeout(std::size_t timeout_milli)
    {
        timeout_milli_ = timeout_milli;
        return *this;
    }

    virtual void run()
    {
        start_ios_thread();
    }

    virtual void stop()
    {
        is_stoped_ = true;
        disconnect();
        stop_ios_thread();
    }

    void set_connect_success_notify(const std::function<void()>& func)
    {
        connect_success_notify_ = func;
    }

    void async_write(const request_content& content)
    {
        request_header header;
        header.protocol_len = content.protocol.size();
        header.message_name_len = content.message_name.size();
        header.body_len = content.body.size();

        auto buffer = get_buffer(request_data{ header, content });
        async_write_impl(buffer);
    }

protected:
    void write(const request_content& content)
    {
        request_header header;
        header.protocol_len = content.protocol.size();
        header.message_name_len = content.message_name.size();
        header.body_len = content.body.size();

        auto buffer = get_buffer(request_data{ header, content });
        write_impl(buffer);
    }

    response_content read()
    {
        read_head();
        memcpy(&rsp_head_, rsp_head_buf_, sizeof(rsp_head_buf_));
        return std::move(read_content());
    }

    void disconnect()
    {
        is_connected_ = false;
        if (socket_.is_open())
        {
            boost::system::error_code ignore_ec;
            socket_.shutdown(boost::asio::socket_base::shutdown_both, ignore_ec);
            socket_.close(ignore_ec);
        }
    }

    boost::asio::ip::tcp::socket& get_socket()
    {
        return socket_;
    }

    bool try_connect()
    {
        if (!is_connected_)
        {
            std::lock_guard<std::mutex> lock(conn_mutex_);
            if (!is_connected_)
            {
                connect();
                is_connected_ = true;
                return true;
            }
        }
        return false;
    }

    response_content make_response_content()
    {
        response_content content;
        memcpy(&content.call_id, &rsp_content_[0], sizeof(content.call_id));
        memcpy(&content.code, &rsp_content_[sizeof(content.call_id)], sizeof(content.code));
        content.message_name.assign(&rsp_content_[sizeof(content.call_id) + sizeof(content.code)], rsp_head_.message_name_len);
        content.body.assign(&rsp_content_[sizeof(content.call_id) + sizeof(content.code) + rsp_head_.message_name_len], rsp_head_.body_len);
        return std::move(content);
    }

private:
    std::shared_ptr<std::string> get_buffer(const request_data& data)
    {
        auto buffer = std::make_shared<std::string>();
        buffer->append(reinterpret_cast<const char*>(&data.header), sizeof(data.header));
        buffer->append(reinterpret_cast<const char*>(&data.content.call_id), sizeof(data.content.call_id));
        buffer->append(reinterpret_cast<const char*>(&data.content.flag), sizeof(data.content.flag));
        buffer->append(data.content.protocol);
        buffer->append(data.content.message_name);
        buffer->append(data.content.body);
        return buffer;
    }

    void connect()
    {
        auto begin_time = std::chrono::high_resolution_clock::now();
        while (true)
        {
            try
            {
                boost::asio::connect(socket_, endpoint_iter_);
                break;
            }
            catch (std::exception& e)
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(20));
                auto end_time = std::chrono::high_resolution_clock::now();
                auto elapsed_time = end_time - begin_time;
                if (std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() > static_cast<long>(timeout_milli_))
                {
                    throw std::runtime_error(e.what());
                }
            }
        }
    }

    void write_impl(const std::shared_ptr<std::string>& buffer)
    {
        boost::system::error_code ec;
        boost::asio::write(socket_, boost::asio::buffer(*buffer), ec);
        if (ec)
        {
            is_connected_ = false;
            throw std::runtime_error(ec.message());
        }
    }

    void async_write_impl(const std::shared_ptr<std::string>& buffer)
    {
        ios_.post([this, buffer]
        {
            bool is_empty = send_queue_.empty();
            send_queue_.emplace_back(buffer);
            if (is_empty)
            {
                async_write_impl();
            }
        });
    }

    void async_write_impl()
    {
        boost::asio::async_write(socket_, boost::asio::buffer(*send_queue_.front()),
                                 [this](boost::system::error_code ec, std::size_t)
        {
            if (!ec)
            {
                send_queue_.pop_front();
                if (!send_queue_.empty())
                {
                    async_write_impl();
                }
            }
            else
            {
                is_connected_ = false;
                send_queue_.clear();
                log_warn() << ec.message();
            }
        });
    }

    void read_head()
    {
        boost::system::error_code ec;
        boost::asio::read(socket_, boost::asio::buffer(rsp_head_buf_), ec);
        if (ec)
        {
            disconnect();
            throw std::runtime_error(ec.message());
        }
    }

    response_content read_content()
    {
        rsp_content_.clear();
        rsp_content_.resize(sizeof(unsigned int) + sizeof(rpc_error_code) + rsp_head_.message_name_len + rsp_head_.body_len);
        boost::system::error_code ec;
        boost::asio::read(socket_, boost::asio::buffer(rsp_content_), ec);
        if (ec)
        {
            disconnect();
            throw std::runtime_error(ec.message());
        }

        return std::move(make_response_content());
    }

    void start_ios_thread()
    {
        if (thread_ == nullptr)
        {
            thread_ = std::make_unique<std::thread>([this]{ ios_.run(); });
        }
    }

    void stop_ios_thread()
    {
        ios_.stop();
        if (thread_ != nullptr)
        {
            if (thread_->joinable())
            {
                thread_->join();
            }
        }
    }

protected:
    client_type client_type_;
    std::size_t timeout_milli_ = 0;
    char rsp_head_buf_[response_header_len];
    response_header rsp_head_;
    std::vector<char> rsp_content_;
    boost::asio::ip::tcp::resolver::iterator endpoint_iter_;
    std::function<void()> connect_success_notify_ = nullptr;
    std::atomic<bool> is_stoped_{ false };

private:
    boost::asio::io_service ios_;
    boost::asio::io_service::work work_;
    boost::asio::ip::tcp::socket socket_;
    std::unique_ptr<std::thread> thread_ = nullptr;

    std::atomic<bool> is_connected_ ;
    std::mutex conn_mutex_;

    threadsafe_list<std::shared_ptr<std::string>> send_queue_;
};

}
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 - 2013 Leap Motion. All rights reserved. Proprietary and confidential.
#include "stdafx.h"
#include "ContextMapTest.h"
#include "Autowired.h"
#include "ContextMap.h"
#include "TestFixtures/ExitRaceThreaded.h"
#include "TestFixtures/SimpleThreaded.h"
#include <string>

using namespace std;

TEST_F(ContextMapTest, VerifySimple) {
  ContextMap<string> mp;

  // Create a new context and add it to the map:
  {
    AutoCreateContext context;

    // Verify the reference count or the rest of the test will fail
    ASSERT_EQ(context.use_count(), 1) << "A newly created context's use count isn't what was expected";

    // Add and ensure the reference count is unmodified
    mp.Add("context_simple", context);
    ASSERT_EQ(context.use_count(), 1) << "The map altered the context use count";

    // We should be able to find this context now:
    std::shared_ptr<CoreContext> found = mp.Find("context_simple");
    EXPECT_TRUE(!!found.get()) << "Failed to find a context that was just inserted into a context map";
  }

  // We shouldn't be able to find it now that it's gone out of scope:
  std::shared_ptr<CoreContext> notFound = mp.Find("context_simple");
  EXPECT_FALSE(!!notFound.get()) << "Context was not evicted as expected when it went out of scope";
}

TEST_F(ContextMapTest, VerifyWithThreads) {
  ContextMap<string> mp;
  std::shared_ptr<SimpleThreaded> threaded;
  std::weak_ptr<CoreContext> weakContext;

  {
    AutoCreateContext context;

    // Obtain a weak pointer of our own, and add to the context:
    weakContext = context;
    mp.Add("context_withthreads", context);

    // Add a thread to hold the context open for awhile:
    threaded = context->Inject<SimpleThreaded>();

    // Start the context
    context->InitiateCoreThreads();
  }

  // Assert that the context still actually exists:
  ASSERT_TRUE(!weakContext.expired()) << "Simple thread exited before it was signalled to exit";

  {
    // Verify that we can still find the context while the thread is alive:
    std::shared_ptr<CoreContext> context = mp.Find("context_withthreads");
    ASSERT_TRUE(!!context.get()) << "Map evicted a context before expected";

    // Relock the weak context, verify that we get back the same pointer:
    auto relocked = weakContext.lock();
    EXPECT_EQ(relocked, context) << "Mapped context pointer was not identical to a previously stored context pointer";

    // Begin context shutdown
    context->SignalShutdown();

    // Signal that the thread can quit:
    threaded->Stop();

    // Wait for the context to exit:
    context->Wait();
  }

  // Release our threaded entity:
  threaded.reset();

  {
    // Verify that the context is gone now that everything in it has stopped running
    std::shared_ptr<CoreContext> ctxt = mp.Find("context_withthreads");
    EXPECT_FALSE(ctxt) << "Context was not properly evicted from the map";
  }
}

TEST_F(ContextMapTest, ConcurrentDestructionTestPathological) {
  for(size_t i = 0; i < 100; i++) {
    // Create our map and a few contexts:
    ContextMap<string> mp;
    AutoCreateContext contexts[4];
    weak_ptr<SimpleThreaded> threads[4];

    // Insert into the map:
    mp.Add("pathological_destruction0", contexts[0]);
    mp.Add("pathological_destruction1", contexts[1]);
    mp.Add("pathological_destruction2", contexts[2]);
    mp.Add("pathological_destruction3", contexts[3]);

    // Add a thread and kick off the context:
    for(size_t i = ARRAYCOUNT(contexts); i--;) {
      threads[i] = contexts[i]->Inject<SimpleThreaded>();
      contexts[i]->InitiateCoreThreads();
    }

    // Immediately tear contexts down:
    for(size_t i = ARRAYCOUNT(contexts); i--;)
      contexts[i]->SignalShutdown();

    // Wait on anything not signalled:
    for(size_t i = 0; i < ARRAYCOUNT(threads); i++) {
      auto cur = threads[i].lock();
      if(cur)
        ASSERT_TRUE(cur->WaitFor(boost::chrono::seconds(1))) << "Spawned thread did not exit in a timely fashion";
    }
  }
}

TEST_F(ContextMapTest, OutOfOrderDeletionTest) {
  try {
    AutoCreateContext controlled;
    ContextMap<size_t> mp;

    // Add the current context to the map:
    mp.Add(1, controlled);

    // Map is destroyed first, then the enclosed context--no exceptions should be thrown
  } catch(...) {
    FAIL() << "Exception thrown while attempting an out-of-order teardown";
  }
}

TEST_F(ContextMapTest, VerifyWithThreadsPathological) {
  ContextMap<size_t> mp;

  // Context collection and exit race threads:
  vector<std::shared_ptr<CoreContext>> contexts;

  // Exit race controller:
  AutoRequired<ExitRaceSignal> signal;

  // Create a number of dependent contexts:
  for(size_t i = 0; i < 100; i++) {
    AutoCreateContext context;
    contexts.push_back(context);

    // Store a shared pointer
    mp.Add(i, context);

    // Start the context
    context->InitiateCoreThreads();
  }

  // Set the signal:
  signal->Signal();

  // Verify that the map empties once our zero-count is hit:
  for(size_t i = 0; i < contexts.size(); i++)
    contexts[i]->Wait();

  // Clear the context collection:
  contexts.clear();
  EXPECT_EQ(0UL, mp.size()) << "Context map did not empty as expected";
}

TEST_F(ContextMapTest, AdjacentCleanupTest) {
  ContextMap<string> mp;
  std::weak_ptr<CoreContext> outerWeak;
  std::weak_ptr<CoreContext> innerWeak;

  // Add two contexts, and let one go out of scope
  AutoCreateContext outer;
  mp.Add("0", outer);
  outerWeak = outer;

  {
    AutoCreateContext inner;
    mp.Add("1", inner);
    innerWeak = inner;

    // Verify that we can find both contexts
    std::shared_ptr<CoreContext> outerSearched = mp.Find("0");
    ASSERT_TRUE(!!outerSearched.get()) << "Outer context just added, but couldn't be found";

    std::shared_ptr<CoreContext> innerSearched = mp.Find("1");
    ASSERT_TRUE(!!innerSearched.get()) << "Inner context just added, but couldn't be found";
  }

  // Inner should be 404 by now
  ASSERT_TRUE(innerWeak.expired()) << "Unexpected outstanding reference to the inner context";

  // Try to find the outer context.  This should evict the inner context.
  mp.Find("0");
  ASSERT_EQ(1UL, mp.size()) << "Proximity eviction didn't function as expected";
}

TEST_F(ContextMapTest, VerifySimpleEnumeration) {
  ContextMap<string> mp;
  AutoCreateContext ctxt1;
  AutoCreateContext ctxt2;
  AutoCreateContext ctxt3;

  mp.Add("context_se_1", ctxt1);
  mp.Add("context_se_2", ctxt2);
  mp.Add("context_se_3", ctxt3);

  std::set<std::string> found;

  size_t count = 0;
  mp.Enumerate(
    [&count, &found] (const string& name, std::shared_ptr<CoreContext>& ctxt) -> bool {
      count++;
      found.insert(name);
      return true;
    }
  );

  EXPECT_EQ(3UL, count) << "Failed to enumerate all expected context pointers";
  EXPECT_EQ(1UL, found.count("context_se_1")) << "Failed to find map element '1'";
  EXPECT_EQ(1UL, found.count("context_se_2")) << "Failed to find map element '2'";
  EXPECT_EQ(1UL, found.count("context_se_3")) << "Failed to find map element '3'";
}

<commit_msg>Simplifying a unit test to make use of Termiante rather than Shutdown/Stop/Wait<commit_after>// Copyright (c) 2010 - 2013 Leap Motion. All rights reserved. Proprietary and confidential.
#include "stdafx.h"
#include "ContextMapTest.h"
#include "Autowired.h"
#include "ContextMap.h"
#include "TestFixtures/ExitRaceThreaded.h"
#include "TestFixtures/SimpleThreaded.h"
#include <string>

using namespace std;

TEST_F(ContextMapTest, VerifySimple) {
  ContextMap<string> mp;

  // Create a new context and add it to the map:
  {
    AutoCreateContext context;

    // Verify the reference count or the rest of the test will fail
    ASSERT_EQ(context.use_count(), 1) << "A newly created context's use count isn't what was expected";

    // Add and ensure the reference count is unmodified
    mp.Add("context_simple", context);
    ASSERT_EQ(context.use_count(), 1) << "The map altered the context use count";

    // We should be able to find this context now:
    std::shared_ptr<CoreContext> found = mp.Find("context_simple");
    EXPECT_TRUE(!!found.get()) << "Failed to find a context that was just inserted into a context map";
  }

  // We shouldn't be able to find it now that it's gone out of scope:
  std::shared_ptr<CoreContext> notFound = mp.Find("context_simple");
  EXPECT_FALSE(!!notFound.get()) << "Context was not evicted as expected when it went out of scope";
}

TEST_F(ContextMapTest, VerifyWithThreads) {
  ContextMap<string> mp;
  std::shared_ptr<SimpleThreaded> threaded;
  std::weak_ptr<CoreContext> weakContext;

  {
    AutoCreateContext context;

    // Obtain a weak pointer of our own, and add to the context:
    weakContext = context;
    mp.Add("context_withthreads", context);

    // Add a thread to hold the context open for awhile:
    threaded = context->Inject<SimpleThreaded>();

    // Start the context
    context->InitiateCoreThreads();
  }

  // Assert that the context still actually exists:
  ASSERT_TRUE(!weakContext.expired()) << "Simple thread exited before it was signalled to exit";

  {
    // Verify that we can still find the context while the thread is alive:
    std::shared_ptr<CoreContext> context = mp.Find("context_withthreads");
    ASSERT_TRUE(!!context.get()) << "Map evicted a context before expected";

    // Relock the weak context, verify that we get back the same pointer:
    auto relocked = weakContext.lock();
    EXPECT_EQ(relocked, context) << "Mapped context pointer was not identical to a previously stored context pointer";

    // Terminate whole context
    context->SignalTerminate();
  }

  // Release our threaded entity:
  threaded.reset();

  {
    // Verify that the context is gone now that everything in it has stopped running
    std::shared_ptr<CoreContext> ctxt = mp.Find("context_withthreads");
    EXPECT_FALSE(ctxt) << "Context was not properly evicted from the map";
  }
}

TEST_F(ContextMapTest, ConcurrentDestructionTestPathological) {
  for(size_t i = 0; i < 100; i++) {
    // Create our map and a few contexts:
    ContextMap<string> mp;
    AutoCreateContext contexts[4];
    weak_ptr<SimpleThreaded> threads[4];

    // Insert into the map:
    mp.Add("pathological_destruction0", contexts[0]);
    mp.Add("pathological_destruction1", contexts[1]);
    mp.Add("pathological_destruction2", contexts[2]);
    mp.Add("pathological_destruction3", contexts[3]);

    // Add a thread and kick off the context:
    for(size_t i = ARRAYCOUNT(contexts); i--;) {
      threads[i] = contexts[i]->Inject<SimpleThreaded>();
      contexts[i]->InitiateCoreThreads();
    }

    // Immediately tear contexts down:
    for(size_t i = ARRAYCOUNT(contexts); i--;)
      contexts[i]->SignalShutdown();

    // Wait on anything not signalled:
    for(size_t i = 0; i < ARRAYCOUNT(threads); i++) {
      auto cur = threads[i].lock();
      if(cur)
        ASSERT_TRUE(cur->WaitFor(boost::chrono::seconds(1))) << "Spawned thread did not exit in a timely fashion";
    }
  }
}

TEST_F(ContextMapTest, OutOfOrderDeletionTest) {
  try {
    AutoCreateContext controlled;
    ContextMap<size_t> mp;

    // Add the current context to the map:
    mp.Add(1, controlled);

    // Map is destroyed first, then the enclosed context--no exceptions should be thrown
  } catch(...) {
    FAIL() << "Exception thrown while attempting an out-of-order teardown";
  }
}

TEST_F(ContextMapTest, VerifyWithThreadsPathological) {
  ContextMap<size_t> mp;

  // Context collection and exit race threads:
  vector<std::shared_ptr<CoreContext>> contexts;

  // Exit race controller:
  AutoRequired<ExitRaceSignal> signal;

  // Create a number of dependent contexts:
  for(size_t i = 0; i < 100; i++) {
    AutoCreateContext context;
    contexts.push_back(context);

    // Store a shared pointer
    mp.Add(i, context);

    // Start the context
    context->InitiateCoreThreads();
  }

  // Set the signal:
  signal->Signal();

  // Verify that the map empties once our zero-count is hit:
  for(size_t i = 0; i < contexts.size(); i++)
    contexts[i]->Wait();

  // Clear the context collection:
  contexts.clear();
  EXPECT_EQ(0UL, mp.size()) << "Context map did not empty as expected";
}

TEST_F(ContextMapTest, AdjacentCleanupTest) {
  ContextMap<string> mp;
  std::weak_ptr<CoreContext> outerWeak;
  std::weak_ptr<CoreContext> innerWeak;

  // Add two contexts, and let one go out of scope
  AutoCreateContext outer;
  mp.Add("0", outer);
  outerWeak = outer;

  {
    AutoCreateContext inner;
    mp.Add("1", inner);
    innerWeak = inner;

    // Verify that we can find both contexts
    std::shared_ptr<CoreContext> outerSearched = mp.Find("0");
    ASSERT_TRUE(!!outerSearched.get()) << "Outer context just added, but couldn't be found";

    std::shared_ptr<CoreContext> innerSearched = mp.Find("1");
    ASSERT_TRUE(!!innerSearched.get()) << "Inner context just added, but couldn't be found";
  }

  // Inner should be 404 by now
  ASSERT_TRUE(innerWeak.expired()) << "Unexpected outstanding reference to the inner context";

  // Try to find the outer context.  This should evict the inner context.
  mp.Find("0");
  ASSERT_EQ(1UL, mp.size()) << "Proximity eviction didn't function as expected";
}

TEST_F(ContextMapTest, VerifySimpleEnumeration) {
  ContextMap<string> mp;
  AutoCreateContext ctxt1;
  AutoCreateContext ctxt2;
  AutoCreateContext ctxt3;

  mp.Add("context_se_1", ctxt1);
  mp.Add("context_se_2", ctxt2);
  mp.Add("context_se_3", ctxt3);

  std::set<std::string> found;

  size_t count = 0;
  mp.Enumerate(
    [&count, &found] (const string& name, std::shared_ptr<CoreContext>& ctxt) -> bool {
      count++;
      found.insert(name);
      return true;
    }
  );

  EXPECT_EQ(3UL, count) << "Failed to enumerate all expected context pointers";
  EXPECT_EQ(1UL, found.count("context_se_1")) << "Failed to find map element '1'";
  EXPECT_EQ(1UL, found.count("context_se_2")) << "Failed to find map element '2'";
  EXPECT_EQ(1UL, found.count("context_se_3")) << "Failed to find map element '3'";
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: RelControliFace.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2007-11-01 15:17:18 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef DBAUI_RELCONTROLIFACE_HXX
#define DBAUI_RELCONTROLIFACE_HXX

#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#include "TableConnectionData.hxx"

namespace dbaui
{
    class IRelationControlInterface
    {
    public:
        virtual ~IRelationControlInterface(){}

        /** getConnectionData returns the current connection data
            @return the current connectiondata
        */
        virtual TTableConnectionData::value_type getConnectionData() const = 0;

        /** setValid set the valid inside, can be used for OK buttons
            @param  _bValid true when the using control allows an update
        */
        virtual void setValid(sal_Bool _bValid) = 0;

        virtual ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection > getConnection() = 0;

        /** notifyConnectionChange is callback which is called when the table selection has changed and a new connection exists
            @param  _pConnectionData    the connection which exists between the new tables
        */
        virtual void notifyConnectionChange() = 0;
    };
}
#endif // DBAUI_RELCONTROLIFACE_HXX
<commit_msg>INTEGRATION: CWS changefileheader (1.4.96); FILE MERGED 2008/03/31 13:27:43 rt 1.4.96.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: RelControliFace.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef DBAUI_RELCONTROLIFACE_HXX
#define DBAUI_RELCONTROLIFACE_HXX

#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#include "TableConnectionData.hxx"

namespace dbaui
{
    class IRelationControlInterface
    {
    public:
        virtual ~IRelationControlInterface(){}

        /** getConnectionData returns the current connection data
            @return the current connectiondata
        */
        virtual TTableConnectionData::value_type getConnectionData() const = 0;

        /** setValid set the valid inside, can be used for OK buttons
            @param  _bValid true when the using control allows an update
        */
        virtual void setValid(sal_Bool _bValid) = 0;

        virtual ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection > getConnection() = 0;

        /** notifyConnectionChange is callback which is called when the table selection has changed and a new connection exists
            @param  _pConnectionData    the connection which exists between the new tables
        */
        virtual void notifyConnectionChange() = 0;
    };
}
#endif // DBAUI_RELCONTROLIFACE_HXX
<|endoftext|>


<commit_before>//--------------------------------------------------------------------------------------
// File: Game.cpp
//
// Developer unit test for basic Direct3D 11 support
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------

#include "pch.h"
#include "Game.h"

#define GAMMA_CORRECT_RENDERING
#define USE_FAST_SEMANTICS

using namespace DirectX;
using namespace DirectX::SimpleMath;

using Microsoft::WRL::ComPtr;

// Constructor.
Game::Game()
{
#if defined(_XBOX_ONE) && defined(_TITLE) && defined(USE_FAST_SEMANTICS)
    m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_D32_FLOAT, 2, true);
#elif defined(GAMMA_CORRECT_RENDERING)
    m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
#else
    m_deviceResources = std::make_unique<DX::DeviceResources>();
#endif

#if !defined(_XBOX_ONE) || !defined(_TITLE)
    m_deviceResources->RegisterDeviceNotify(this);
#endif
}

// Initialize the Direct3D resources required to run.
void Game::Initialize(
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
    HWND window,
#else
    IUnknown* window,
#endif
    int width, int height, DXGI_MODE_ROTATION rotation)
{
    m_gamePad = std::make_unique<GamePad>();
    m_keyboard = std::make_unique<Keyboard>();

#if defined(_XBOX_ONE) && defined(_TITLE)
    UNREFERENCED_PARAMETER(rotation);
    UNREFERENCED_PARAMETER(width);
    UNREFERENCED_PARAMETER(height);
    m_deviceResources->SetWindow(window);
#elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    m_deviceResources->SetWindow(window, width, height, rotation);
    m_keyboard->SetWindow(reinterpret_cast<ABI::Windows::UI::Core::ICoreWindow*>(window));
#else
    UNREFERENCED_PARAMETER(rotation);
    m_deviceResources->SetWindow(window, width, height);
#endif

    m_deviceResources->CreateDeviceResources();
    CreateDeviceDependentResources();

    m_deviceResources->CreateWindowSizeDependentResources();
    CreateWindowSizeDependentResources();

#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    // SimpleMath interop tests for Windows Runtime types
    Rectangle test1(10, 20, 50, 100);

    Windows::Foundation::Rect test2 = test1;
    if (test1.x != long(test2.X)
        && test1.y != long(test2.Y)
        && test1.width != long(test2.Width)
        && test1.height != long(test2.Height))
    {
        OutputDebugStringA("SimpleMath::Rectangle operator test A failed!");
        throw ref new Platform::Exception(E_FAIL);
    }
#endif
}

#pragma region Frame Update
// Executes the basic game loop.
void Game::Tick()
{
    m_timer.Tick([&]()
    {
        Update(m_timer);
    });

    Render();
}

// Updates the world.
void Game::Update(DX::StepTimer const&)
{
    auto pad = m_gamePad->GetState(0);
    auto kb = m_keyboard->GetState();
    if (kb.Escape || (pad.IsConnected() && pad.IsViewPressed()))
    {
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
        PostQuitMessage(0);
#else
        Windows::ApplicationModel::Core::CoreApplication::Exit();
#endif
    }
}
#pragma endregion

#pragma region Frame Render
// Draws the scene.
void Game::Render()
{
    // Don't try to render anything before the first Update.
    if (m_timer.GetFrameCount() == 0)
    {
        return;
    }

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_deviceResources->Prepare();
#endif

    Clear();

    auto context = m_deviceResources->GetD3DDeviceContext();

    m_effect->Apply(context);

    context->OMSetBlendState(m_states->Opaque(), nullptr, 0xFFFFFFFF);
    context->OMSetDepthStencilState(m_states->DepthNone(), 0);
    context->RSSetState(m_states->CullNone());

    context->IASetInputLayout(m_inputLayout.Get());

    m_batch->Begin();

    XMVECTORF32 red, green, blue, dred, dgreen, dblue, yellow, cyan, magenta, gray, dgray;
#ifdef GAMMA_CORRECT_RENDERING
    red.v = XMColorSRGBToRGB(Colors::Red);
    green.v = XMColorSRGBToRGB(Colors::Green);
    blue.v = XMColorSRGBToRGB(Colors::Blue);
    dred.v = XMColorSRGBToRGB(Colors::DarkRed);
    dgreen.v = XMColorSRGBToRGB(Colors::DarkGreen);
    dblue.v = XMColorSRGBToRGB(Colors::DarkBlue);
    yellow.v = XMColorSRGBToRGB(Colors::Yellow);
    cyan.v = XMColorSRGBToRGB(Colors::Cyan);
    magenta.v = XMColorSRGBToRGB(Colors::Magenta);
    gray.v = XMColorSRGBToRGB(Colors::Gray);
    dgray.v = XMColorSRGBToRGB(Colors::DarkGray);
#else
    red.v = Colors::Red;
    green.v = Colors::Green;
    blue.v = Colors::Blue;
    dred.v = Colors::DarkRed;
    dgreen.v = Colors::DarkGreen;
    dblue.v = Colors::DarkBlue;
    yellow.v = Colors::Yellow;
    cyan.v = Colors::Cyan;
    magenta.v = Colors::Magenta;
    gray.v = Colors::Gray;
    dgray.v = Colors::DarkGray;
#endif

    // Point
    {
        VertexPositionColor points[]
        {
            { Vector3(-0.75f, -0.75f, 0.5f), red },
            { Vector3(-0.75f, -0.5f,  0.5f), green },
            { Vector3(-0.75f, -0.25f, 0.5f), blue },
            { Vector3(-0.75f,  0.0f,  0.5f), yellow },
            { Vector3(-0.75f,  0.25f, 0.5f), magenta },
            { Vector3(-0.75f,  0.5f,  0.5f), cyan },
            { Vector3(-0.75f,  0.75f, 0.5f), Colors::White },
        };

        m_batch->Draw(D3D_PRIMITIVE_TOPOLOGY_POINTLIST, points, _countof(points));
    }

    // Lines
    {
        VertexPositionColor lines[] =
        {
            { Vector3(-0.75f, -0.85f, 0.5f), red },{ Vector3(0.75f, -0.85f, 0.5f), dred },
            { Vector3(-0.75f, -0.90f, 0.5f), green },{ Vector3(0.75f, -0.90f, 0.5f), dgreen },
            { Vector3(-0.75f, -0.95f, 0.5f), blue },{ Vector3(0.75f, -0.95f, 0.5f), dblue },
        };

        m_batch->DrawLine(lines[0], lines[1]);
        m_batch->DrawLine(lines[2], lines[3]);
        m_batch->DrawLine(lines[4], lines[5]);
    }

    // Triangle
    {
        VertexPositionColor v1(Vector3(0.f, 0.5f, 0.5f), red);
        VertexPositionColor v2(Vector3(0.5f, -0.5f, 0.5f), green);
        VertexPositionColor v3(Vector3(-0.5f, -0.5f, 0.5f), blue);

        m_batch->DrawTriangle(v1, v2, v3);
    }

    // Quads
    {
        VertexPositionColor quad[] =
        {
            { Vector3(0.75f, 0.75f, 0.5), gray },
            { Vector3(0.95f, 0.75f, 0.5), gray },
            { Vector3(0.95f, -0.75f, 0.5), dgray },
            { Vector3(0.75f, -0.75f, 0.5), dgray },
        };

        m_batch->DrawQuad(quad[0], quad[1], quad[2], quad[3]);
    }

    m_batch->End();

    // Show the new frame.
    m_deviceResources->Present();

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_graphicsMemory->Commit();
#endif
}

// Helper method to clear the back buffers.
void Game::Clear()
{
    // Clear the views.
    auto context = m_deviceResources->GetD3DDeviceContext();
    auto renderTarget = m_deviceResources->GetRenderTargetView();
    auto depthStencil = m_deviceResources->GetDepthStencilView();

    XMVECTORF32 color;
#ifdef GAMMA_CORRECT_RENDERING
    color.v = XMColorSRGBToRGB(Colors::CornflowerBlue);
#else
    color.v = Colors::CornflowerBlue;
#endif
    context->ClearRenderTargetView(renderTarget, color);
    context->ClearDepthStencilView(depthStencil, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    context->OMSetRenderTargets(1, &renderTarget, depthStencil);

    // Set the viewport.
    auto viewport = m_deviceResources->GetScreenViewport();
    context->RSSetViewports(1, &viewport);
}
#pragma endregion

#pragma region Message Handlers
// Message handlers
void Game::OnActivated()
{
}

void Game::OnDeactivated()
{
}

void Game::OnSuspending()
{
}

void Game::OnResuming()
{
    m_timer.ResetElapsedTime();
}

#if !defined(_XBOX_ONE) || !defined(_TITLE)
void Game::OnWindowSizeChanged(int width, int height, DXGI_MODE_ROTATION rotation)
{
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    if (!m_deviceResources->WindowSizeChanged(width, height, rotation))
        return;
#else
    UNREFERENCED_PARAMETER(rotation);
    if (!m_deviceResources->WindowSizeChanged(width, height))
        return;
#endif

    CreateWindowSizeDependentResources();
}
#endif

#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
void Game::ValidateDevice()
{
    m_deviceResources->ValidateDevice();
}
#endif

// Properties
void Game::GetDefaultSize(int& width, int& height) const
{
    width = 1280;
    height = 720;
}
#pragma endregion

#pragma region Direct3D Resources
// These are the resources that depend on the device.
void Game::CreateDeviceDependentResources()
{
    auto device = m_deviceResources->GetD3DDevice();
    auto context = m_deviceResources->GetD3DDeviceContext();

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_graphicsMemory = std::make_unique<GraphicsMemory>(device, m_deviceResources->GetBackBufferCount());
#endif

    m_effect = std::make_unique<BasicEffect>(device);
    m_effect->SetVertexColorEnabled(true);

    void const* shaderByteCode;
    size_t byteCodeLength;

    m_effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);

    DX::ThrowIfFailed(
            device->CreateInputLayout(VertexPositionColor::InputElements,
                VertexPositionColor::InputElementCount,
                shaderByteCode, byteCodeLength,
                m_inputLayout.ReleaseAndGetAddressOf()));

    m_states = std::make_unique<CommonStates>(device);

    m_batch = std::make_unique<PrimitiveBatch<VertexPositionColor>>(context);
}

// Allocate all memory resources that change on a window SizeChanged event.
void Game::CreateWindowSizeDependentResources()
{
    SetDebugObjectName(m_deviceResources->GetDepthStencilView(), "DepthStencil");
}

#if !defined(_XBOX_ONE) || !defined(_TITLE)
void Game::OnDeviceLost()
{
    m_effect.reset();
    m_states.reset();
    m_batch.reset();
    m_inputLayout.Reset();
}

void Game::OnDeviceRestored()
{
    CreateDeviceDependentResources();

    CreateWindowSizeDependentResources();
}
#endif
#pragma endregion<commit_msg>Minor code review<commit_after>//--------------------------------------------------------------------------------------
// File: Game.cpp
//
// Developer unit test for basic Direct3D 11 support
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------

#include "pch.h"
#include "Game.h"

#define GAMMA_CORRECT_RENDERING
#define USE_FAST_SEMANTICS

using namespace DirectX;
using namespace DirectX::SimpleMath;

using Microsoft::WRL::ComPtr;

// Constructor.
Game::Game()
{
#if defined(_XBOX_ONE) && defined(_TITLE) && defined(USE_FAST_SEMANTICS)
    m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_D32_FLOAT, 2, true);
#elif defined(GAMMA_CORRECT_RENDERING)
    m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
#else
    m_deviceResources = std::make_unique<DX::DeviceResources>();
#endif

#if !defined(_XBOX_ONE) || !defined(_TITLE)
    m_deviceResources->RegisterDeviceNotify(this);
#endif
}

// Initialize the Direct3D resources required to run.
void Game::Initialize(
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
    HWND window,
#else
    IUnknown* window,
#endif
    int width, int height, DXGI_MODE_ROTATION rotation)
{
    m_gamePad = std::make_unique<GamePad>();
    m_keyboard = std::make_unique<Keyboard>();

#if defined(_XBOX_ONE) && defined(_TITLE)
    UNREFERENCED_PARAMETER(rotation);
    UNREFERENCED_PARAMETER(width);
    UNREFERENCED_PARAMETER(height);
    m_deviceResources->SetWindow(window);
#elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    m_deviceResources->SetWindow(window, width, height, rotation);
    m_keyboard->SetWindow(reinterpret_cast<ABI::Windows::UI::Core::ICoreWindow*>(window));
#else
    UNREFERENCED_PARAMETER(rotation);
    m_deviceResources->SetWindow(window, width, height);
#endif

    m_deviceResources->CreateDeviceResources();
    CreateDeviceDependentResources();

    m_deviceResources->CreateWindowSizeDependentResources();
    CreateWindowSizeDependentResources();

#if defined(__cplusplus_winrt)
    // SimpleMath interop tests for Windows Runtime types
    Rectangle test1(10, 20, 50, 100);

    Windows::Foundation::Rect test2 = test1;
    if (test1.x != long(test2.X)
        && test1.y != long(test2.Y)
        && test1.width != long(test2.Width)
        && test1.height != long(test2.Height))
    {
        OutputDebugStringA("SimpleMath::Rectangle operator test A failed!");
        throw ref new Platform::Exception(E_FAIL);
    }
#endif
}

#pragma region Frame Update
// Executes the basic game loop.
void Game::Tick()
{
    m_timer.Tick([&]()
    {
        Update(m_timer);
    });

    Render();
}

// Updates the world.
void Game::Update(DX::StepTimer const&)
{
    auto pad = m_gamePad->GetState(0);
    auto kb = m_keyboard->GetState();
    if (kb.Escape || (pad.IsConnected() && pad.IsViewPressed()))
    {
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
        PostQuitMessage(0);
#else
        Windows::ApplicationModel::Core::CoreApplication::Exit();
#endif
    }
}
#pragma endregion

#pragma region Frame Render
// Draws the scene.
void Game::Render()
{
    // Don't try to render anything before the first Update.
    if (m_timer.GetFrameCount() == 0)
    {
        return;
    }

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_deviceResources->Prepare();
#endif

    Clear();

    auto context = m_deviceResources->GetD3DDeviceContext();

    m_effect->Apply(context);

    context->OMSetBlendState(m_states->Opaque(), nullptr, 0xFFFFFFFF);
    context->OMSetDepthStencilState(m_states->DepthNone(), 0);
    context->RSSetState(m_states->CullNone());

    context->IASetInputLayout(m_inputLayout.Get());

    m_batch->Begin();

    XMVECTORF32 red, green, blue, dred, dgreen, dblue, yellow, cyan, magenta, gray, dgray;
#ifdef GAMMA_CORRECT_RENDERING
    red.v = XMColorSRGBToRGB(Colors::Red);
    green.v = XMColorSRGBToRGB(Colors::Green);
    blue.v = XMColorSRGBToRGB(Colors::Blue);
    dred.v = XMColorSRGBToRGB(Colors::DarkRed);
    dgreen.v = XMColorSRGBToRGB(Colors::DarkGreen);
    dblue.v = XMColorSRGBToRGB(Colors::DarkBlue);
    yellow.v = XMColorSRGBToRGB(Colors::Yellow);
    cyan.v = XMColorSRGBToRGB(Colors::Cyan);
    magenta.v = XMColorSRGBToRGB(Colors::Magenta);
    gray.v = XMColorSRGBToRGB(Colors::Gray);
    dgray.v = XMColorSRGBToRGB(Colors::DarkGray);
#else
    red.v = Colors::Red;
    green.v = Colors::Green;
    blue.v = Colors::Blue;
    dred.v = Colors::DarkRed;
    dgreen.v = Colors::DarkGreen;
    dblue.v = Colors::DarkBlue;
    yellow.v = Colors::Yellow;
    cyan.v = Colors::Cyan;
    magenta.v = Colors::Magenta;
    gray.v = Colors::Gray;
    dgray.v = Colors::DarkGray;
#endif

    // Point
    {
        VertexPositionColor points[]
        {
            { Vector3(-0.75f, -0.75f, 0.5f), red },
            { Vector3(-0.75f, -0.5f,  0.5f), green },
            { Vector3(-0.75f, -0.25f, 0.5f), blue },
            { Vector3(-0.75f,  0.0f,  0.5f), yellow },
            { Vector3(-0.75f,  0.25f, 0.5f), magenta },
            { Vector3(-0.75f,  0.5f,  0.5f), cyan },
            { Vector3(-0.75f,  0.75f, 0.5f), Colors::White },
        };

        m_batch->Draw(D3D_PRIMITIVE_TOPOLOGY_POINTLIST, points, _countof(points));
    }

    // Lines
    {
        VertexPositionColor lines[] =
        {
            { Vector3(-0.75f, -0.85f, 0.5f), red },{ Vector3(0.75f, -0.85f, 0.5f), dred },
            { Vector3(-0.75f, -0.90f, 0.5f), green },{ Vector3(0.75f, -0.90f, 0.5f), dgreen },
            { Vector3(-0.75f, -0.95f, 0.5f), blue },{ Vector3(0.75f, -0.95f, 0.5f), dblue },
        };

        m_batch->DrawLine(lines[0], lines[1]);
        m_batch->DrawLine(lines[2], lines[3]);
        m_batch->DrawLine(lines[4], lines[5]);
    }

    // Triangle
    {
        VertexPositionColor v1(Vector3(0.f, 0.5f, 0.5f), red);
        VertexPositionColor v2(Vector3(0.5f, -0.5f, 0.5f), green);
        VertexPositionColor v3(Vector3(-0.5f, -0.5f, 0.5f), blue);

        m_batch->DrawTriangle(v1, v2, v3);
    }

    // Quads
    {
        VertexPositionColor quad[] =
        {
            { Vector3(0.75f, 0.75f, 0.5), gray },
            { Vector3(0.95f, 0.75f, 0.5), gray },
            { Vector3(0.95f, -0.75f, 0.5), dgray },
            { Vector3(0.75f, -0.75f, 0.5), dgray },
        };

        m_batch->DrawQuad(quad[0], quad[1], quad[2], quad[3]);
    }

    m_batch->End();

    // Show the new frame.
    m_deviceResources->Present();

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_graphicsMemory->Commit();
#endif
}

// Helper method to clear the back buffers.
void Game::Clear()
{
    // Clear the views.
    auto context = m_deviceResources->GetD3DDeviceContext();
    auto renderTarget = m_deviceResources->GetRenderTargetView();
    auto depthStencil = m_deviceResources->GetDepthStencilView();

    XMVECTORF32 color;
#ifdef GAMMA_CORRECT_RENDERING
    color.v = XMColorSRGBToRGB(Colors::CornflowerBlue);
#else
    color.v = Colors::CornflowerBlue;
#endif
    context->ClearRenderTargetView(renderTarget, color);
    context->ClearDepthStencilView(depthStencil, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    context->OMSetRenderTargets(1, &renderTarget, depthStencil);

    // Set the viewport.
    auto viewport = m_deviceResources->GetScreenViewport();
    context->RSSetViewports(1, &viewport);
}
#pragma endregion

#pragma region Message Handlers
// Message handlers
void Game::OnActivated()
{
}

void Game::OnDeactivated()
{
}

void Game::OnSuspending()
{
}

void Game::OnResuming()
{
    m_timer.ResetElapsedTime();
}

#if !defined(_XBOX_ONE) || !defined(_TITLE)
void Game::OnWindowSizeChanged(int width, int height, DXGI_MODE_ROTATION rotation)
{
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    if (!m_deviceResources->WindowSizeChanged(width, height, rotation))
        return;
#else
    UNREFERENCED_PARAMETER(rotation);
    if (!m_deviceResources->WindowSizeChanged(width, height))
        return;
#endif

    CreateWindowSizeDependentResources();
}
#endif

#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
void Game::ValidateDevice()
{
    m_deviceResources->ValidateDevice();
}
#endif

// Properties
void Game::GetDefaultSize(int& width, int& height) const
{
    width = 1280;
    height = 720;
}
#pragma endregion

#pragma region Direct3D Resources
// These are the resources that depend on the device.
void Game::CreateDeviceDependentResources()
{
    auto device = m_deviceResources->GetD3DDevice();
    auto context = m_deviceResources->GetD3DDeviceContext();

#if defined(_XBOX_ONE) && defined(_TITLE)
    m_graphicsMemory = std::make_unique<GraphicsMemory>(device, m_deviceResources->GetBackBufferCount());
#endif

    m_effect = std::make_unique<BasicEffect>(device);
    m_effect->SetVertexColorEnabled(true);

    void const* shaderByteCode;
    size_t byteCodeLength;

    m_effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);

    DX::ThrowIfFailed(
            device->CreateInputLayout(VertexPositionColor::InputElements,
                VertexPositionColor::InputElementCount,
                shaderByteCode, byteCodeLength,
                m_inputLayout.ReleaseAndGetAddressOf()));

    m_states = std::make_unique<CommonStates>(device);

    m_batch = std::make_unique<PrimitiveBatch<VertexPositionColor>>(context);
}

// Allocate all memory resources that change on a window SizeChanged event.
void Game::CreateWindowSizeDependentResources()
{
    SetDebugObjectName(m_deviceResources->GetDepthStencilView(), "DepthStencil");
}

#if !defined(_XBOX_ONE) || !defined(_TITLE)
void Game::OnDeviceLost()
{
    m_effect.reset();
    m_states.reset();
    m_batch.reset();
    m_inputLayout.Reset();
}

void Game::OnDeviceRestored()
{
    CreateDeviceDependentResources();

    CreateWindowSizeDependentResources();
}
#endif
#pragma endregion<|endoftext|>


<commit_before>#include "thcrap.h"
#include "gtest/gtest.h"
#include <fstream>
#include <filesystem>

// Also test file_stream and file_stream_read
TEST(PatchFile, FileRead)
{
    const char *fn = "FileRead_test.txt";
    const char content[] = "abcde";
    const size_t content_size = sizeof(content);
    void *buffer;
    size_t file_size;

    // Test with an invalid file. file_size must be set to 0 by file_read.
    file_size = 0x12345678;
    buffer = file_read("invalid_file.txt", &file_size);
    EXPECT_EQ(buffer, nullptr);
    EXPECT_EQ(file_size, 0u);

    {
        std::ofstream f(fn);
        f.write(content, content_size);
    }

    buffer = file_read(fn, &file_size);
    EXPECT_STREQ((const char*)buffer, content);
    EXPECT_EQ(file_size, content_size);
    free(buffer);

    // Check without the file_size parameter
    buffer = file_read(fn, nullptr);
    EXPECT_STREQ((const char*)buffer, content);
    free(buffer);

    file_size = 1;
    buffer = file_read("nul", &file_size);
    // malloc doesn't offer any guarantee for malloc(0),
    // only that the pointer can be safely passed to free().
    // It can return a NULL pointer or a pointer to a 0-byte buffer.
    EXPECT_EQ(file_size, 0u);
    free(buffer);

    std::filesystem::remove(fn);
}

TEST(PatchFile, FileWrite)
{
    EXPECT_NE(file_write("nul/file.txt", "data", 5), 0);

    if (std::filesystem::exists("test.txt")) {
        std::filesystem::remove("test.txt");
    }
    EXPECT_EQ(file_write("test.txt", "data", 5), 0);

    size_t size;
    void *file = file_read("test.txt", &size);
    ASSERT_NE(file, nullptr);
    EXPECT_EQ(size, 5u);
    EXPECT_STREQ((char*)file, "data");

    std::filesystem::remove("test.txt");
}

TEST(PatchFile, FnForGame)
{
    char *fn;

    fn = fn_for_game("test.js");
    EXPECT_STREQ(fn, "test.js");
    free(fn);

    ScopedJson runcfg = json_pack("{s:s}", "game", "th06");
    runconfig_load(*runcfg, 0);
    fn = fn_for_game("test.js");
    EXPECT_STREQ(fn, "th06/test.js");
    free(fn);
    runconfig_free();
}

TEST(PatchFile, FnForBuild)
{
    char *fn;

    fn = fn_for_build("test.js");
    EXPECT_EQ(fn, nullptr);

    runconfig_build_set("v1.2.3");
    fn = fn_for_build("test.js");
    EXPECT_STREQ(fn, "test.v1.2.3.js");
    free(fn);
    runconfig_build_set(nullptr);
}

TEST(PatchFile, FnForPatch)
{
    char *fn;
    patch_t patch = {};

    fn = fn_for_patch(nullptr, "test.js");
    EXPECT_EQ(fn, nullptr);

    patch.archive = strdup("C:/Path/to/thcrap/repos/repo_name/patch_name");
    fn = fn_for_patch(&patch, "test.js");
    EXPECT_STREQ(fn, "C:/Path/to/thcrap/repos/repo_name/patch_name/test.js");
    free(fn);
    free(patch.archive);

    // Double slash is accepted just fine by Windows
    patch.archive = strdup("C:/Path/to/thcrap/repos/repo_name/patch_name/");
    fn = fn_for_patch(&patch, "test.js");
    EXPECT_STREQ(fn, "C:/Path/to/thcrap/repos/repo_name/patch_name//test.js");
    free(fn);
    free(patch.archive);
}

TEST(PatchFile, DirCreateForFn)
{
    if (std::filesystem::exists("test_dir")) {
        std::filesystem::remove_all("test_dir");
    }

    dir_create_for_fn("test_dir/dir2/dir3/file.txt");
    EXPECT_TRUE(std::filesystem::is_directory("test_dir/dir2/dir3"));

    std::filesystem::remove_all("test_dir");
}

class PatchFileTest : public ::testing::Test
{
protected:
    patch_t patch;

    void SetUp() override
    {
        if (std::filesystem::exists("testdir")) {
            std::filesystem::remove_all("testdir");
        }
        std::filesystem::create_directory("testdir");

        ScopedJson patch_js = json_pack("{s:s,s:s,s:[s,s],s:[s,s],s:{s:b,s:b},s:s,s:s,s:i}",
            "id", "base_tsa",
            "title", "Base patch for TSA games",
            "servers", "https://www.example.com/", "https://www.example2.com/",
            "dependencies", "thpatch/patch1", "patch2",
            "fonts", "arial.ttf", true, "comicsans.ttf", true,
            "motd", "This is a message",
            "motd_title", "Title for motd",
            "motd_type", 12
        );
        json_dump_file(*patch_js, "testdir/patch.js", 0);

        ScopedJson runconfig_js = json_pack("{s:[s,s],s:b}",
            "ignore", "*.bmp", "*.jpg",
            "update", false
        );
        this->patch = patch_init("testdir", *runconfig_js, 0);
    }

    void TearDown() override
    {
        patch_free(&this->patch);
        std::filesystem::remove_all("testdir");
    }
};

TEST_F(PatchFileTest, PatchInit)
{
    // Writing a nice test for patch_archive is a bit hard and pointless,
    // we don't really care about its exact value.
    // The patch_file_exist will test if the archive value is actually meaningful.
    EXPECT_NE(patch.archive, nullptr);
    EXPECT_STREQ(patch.id, "base_tsa");
    EXPECT_STREQ(patch.title, "Base patch for TSA games");

    ASSERT_NE(patch.servers, nullptr);
    EXPECT_STREQ(patch.servers[0], "https://www.example.com/");
    EXPECT_STREQ(patch.servers[1], "https://www.example2.com/");
    EXPECT_EQ(patch.servers[2], nullptr);

    // TODO: dependencies
    ASSERT_NE(patch.dependencies, nullptr);
    EXPECT_STREQ(patch.dependencies[0].repo_id, "thpatch");
    EXPECT_STREQ(patch.dependencies[0].patch_id, "patch1");
    EXPECT_EQ(patch.dependencies[1].repo_id, nullptr);
    EXPECT_STREQ(patch.dependencies[1].patch_id, "patch2");
    EXPECT_EQ(patch.dependencies[2].patch_id, nullptr);

    ASSERT_NE(patch.fonts, nullptr);
    EXPECT_STREQ(patch.fonts[0], "arial.ttf");
    EXPECT_STREQ(patch.fonts[1], "comicsans.ttf");
    EXPECT_EQ(patch.fonts[2], nullptr);

    ASSERT_NE(patch.ignore, nullptr);
    EXPECT_STREQ(patch.ignore[0], "*.bmp");
    EXPECT_STREQ(patch.ignore[1], "*.jpg");
    EXPECT_EQ(patch.ignore[2], nullptr);

    EXPECT_EQ(patch.update, false);

    EXPECT_STREQ(patch.motd, "This is a message");
    EXPECT_STREQ(patch.motd_title, "Title for motd");
    EXPECT_EQ(patch.motd_type, 12u);

    // TODO: test invalid parameters
}

TEST_F(PatchFileTest, PatchFileExists)
{
    std::ofstream file;

    file.open("testdir/test_exist.js");
    file.close();
    EXPECT_TRUE (patch_file_exists(&patch, "test_exist.js"));
    EXPECT_FALSE(patch_file_exists(&patch, "test_missing.js"));

    std::filesystem::create_directory("testdir/subdir");
    file.open("testdir/subdir/test_exist.js");
    file.close();
    EXPECT_TRUE (patch_file_exists(&patch, "subdir/test_exist.js"));
    EXPECT_FALSE(patch_file_exists(&patch, "subdir/test_missing.js"));

    // Test blacklist
    file.open("testdir/test_blacklist.bmp");
    file.close();
    EXPECT_FALSE(patch_file_exists(&patch, "test_blacklist.bmp"));
}

TEST_F(PatchFileTest, PatchFileBlacklisted)
{
    EXPECT_TRUE (patch_file_blacklisted(&patch, "file.bmp"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "file.jpg"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "file.png"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "file_without_extension"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "dir/file.bmp"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "dir/file.jpg"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "dir/file.png"));
}

TEST_F(PatchFileTest, PatchFileLoad)
{
    std::ofstream file;
    char *buffer;
    size_t file_size;

    file.open("testdir/test_exist.txt");
    file.write("abcde", 6);
    file.close();
    buffer = (char*)patch_file_load(&patch, "test_exist.txt", &file_size);
    EXPECT_STREQ(buffer, "abcde");
    EXPECT_EQ(file_size, 6u);
    free(buffer);

    EXPECT_EQ(patch_file_load(&patch, "test_missing.txt", &file_size), nullptr);
    EXPECT_EQ(file_size, 0u);

    std::filesystem::create_directory("testdir/subdir");
    file.open("testdir/subdir/test_exist.txt");
    file.write("abcde", 6);
    file.close();
    buffer = (char*)patch_file_load(&patch, "subdir/test_exist.txt", &file_size);
    EXPECT_STREQ(buffer, "abcde");
    EXPECT_EQ(file_size, 6u);
    free(buffer);

    // Test blacklist
    file.open("testdir/test_blacklist.bmp");
    file.write("abcde", 6);
    file.close();
    EXPECT_EQ(patch_file_load(&patch, "test_blacklist.bmp", &file_size), nullptr);
    EXPECT_EQ(file_size, 0u);

    // Test without size
    buffer = (char*)patch_file_load(&patch, "test_exist.txt", nullptr);
    EXPECT_STREQ(buffer, "abcde");
    free(buffer);
}

// TODO: patch_json_load, patch_json_merge, patch_file_store, patch_json_store, patch_file_delete

TEST_F(PatchFileTest, PatchToRunconfigJson)
{
    ScopedJson json = patch_to_runconfig_json(&patch);
    EXPECT_NE(*json, nullptr);
    EXPECT_STREQ(json_string_value(json_object_get(*json, "archive")), patch.archive);
}

TEST_F(PatchFileTest, PatchRelToAbs)
{
    // Create a file
    patch_file_store(&patch, "test.txt", "abcde", 6);

    std::string cwd = std::filesystem::current_path().generic_u8string();
    patch_rel_to_abs(&patch, cwd.c_str());

    // The patch path should point to the same directory
    EXPECT_TRUE(patch_file_exists(&patch, "test.txt"));

    // The path should be absolute
    std::filesystem::path path = patch.archive;
    EXPECT_TRUE(path.is_absolute());
}

TEST(PatchFile, PatchBuild)
{
    patch_desc_t desc = {
        strdup("repo_name"),
        strdup("patch_name")
    };
    patch_t patch = patch_build(&desc);
    EXPECT_STREQ(patch.archive, "repos/repo_name/patch_name/");
    free(desc.repo_id);
    free(desc.patch_id);
    patch_free(&patch);
}

TEST(PatchFile, PatchDepToDesc)
{
    patch_desc_t desc;

    // Invalid value
    desc = patch_dep_to_desc(nullptr);
    EXPECT_EQ(desc.repo_id, nullptr);
    EXPECT_EQ(desc.patch_id, nullptr);

    // Absolute value
    desc = patch_dep_to_desc("nmlgc/base_tsa");
    EXPECT_STREQ(desc.repo_id, "nmlgc");
    EXPECT_STREQ(desc.patch_id, "base_tsa");
    free(desc.repo_id);
    free(desc.patch_id);

    // Relative value
    desc = patch_dep_to_desc("base_tsa");
    EXPECT_EQ(desc.repo_id, nullptr);
    EXPECT_STREQ(desc.patch_id, "base_tsa");
    free(desc.patch_id);
}

// TODO: patchhook
<commit_msg>thcrap_test: Fix failing test<commit_after>#include "thcrap.h"
#include "gtest/gtest.h"
#include <fstream>
#include <filesystem>

// Also test file_stream and file_stream_read
TEST(PatchFile, FileRead)
{
    const char *fn = "FileRead_test.txt";
    const char content[] = "abcde";
    const size_t content_size = sizeof(content);
    void *buffer;
    size_t file_size;

    // Test with an invalid file. file_size must be set to 0 by file_read.
    file_size = 0x12345678;
    buffer = file_read("invalid_file.txt", &file_size);
    EXPECT_EQ(buffer, nullptr);
    EXPECT_EQ(file_size, 0u);

    {
        std::ofstream f(fn);
        f.write(content, content_size);
    }

    buffer = file_read(fn, &file_size);
    EXPECT_STREQ((const char*)buffer, content);
    EXPECT_EQ(file_size, content_size);
    free(buffer);

    // Check without the file_size parameter
    buffer = file_read(fn, nullptr);
    EXPECT_STREQ((const char*)buffer, content);
    free(buffer);

    file_size = 1;
    buffer = file_read("nul", &file_size);
    // malloc doesn't offer any guarantee for malloc(0),
    // only that the pointer can be safely passed to free().
    // It can return a NULL pointer or a pointer to a 0-byte buffer.
    EXPECT_EQ(file_size, 0u);
    free(buffer);

    std::filesystem::remove(fn);
}

TEST(PatchFile, FileWrite)
{
    EXPECT_NE(file_write("nul/file.txt", "data", 5), 0);

    if (std::filesystem::exists("test.txt")) {
        std::filesystem::remove("test.txt");
    }
    EXPECT_EQ(file_write("test.txt", "data", 5), 0);

    size_t size;
    void *file = file_read("test.txt", &size);
    ASSERT_NE(file, nullptr);
    EXPECT_EQ(size, 5u);
    EXPECT_STREQ((char*)file, "data");

    std::filesystem::remove("test.txt");
}

TEST(PatchFile, FnForGame)
{
    char *fn;

    fn = fn_for_game("test.js");
    EXPECT_STREQ(fn, "test.js");
    free(fn);

    ScopedJson runcfg = json_pack("{s:s}", "game", "th06");
    runconfig_load(*runcfg, 0);
    fn = fn_for_game("test.js");
    EXPECT_STREQ(fn, "th06/test.js");
    free(fn);
    runconfig_free();
}

TEST(PatchFile, FnForBuild)
{
    char *fn;

    fn = fn_for_build("test.js");
    EXPECT_EQ(fn, nullptr);

    runconfig_build_set("v1.2.3");
    fn = fn_for_build("test.js");
    EXPECT_STREQ(fn, "test.v1.2.3.js");
    free(fn);
    runconfig_build_set(nullptr);
}

TEST(PatchFile, FnForPatch)
{
    char *fn;
    patch_t patch = {};

    fn = fn_for_patch(nullptr, "test.js");
    EXPECT_EQ(fn, nullptr);

    std::string_view test_pathA = "C:/Path/to/thcrap/repos/repo_name/patch_name";
    patch.archive = strdup(test_pathA.data());
    patch.archive_length = test_pathA.length();
    fn = fn_for_patch(&patch, "test.js");
    EXPECT_STREQ(fn, "C:/Path/to/thcrap/repos/repo_name/patch_name/test.js");
    free(fn);
    free(patch.archive);

    // Double slash is accepted just fine by Windows
    std::string_view test_pathB = "C:/Path/to/thcrap/repos/repo_name/patch_name/";
    patch.archive = strdup(test_pathB.data());
    patch.archive_length = test_pathB.length();
    fn = fn_for_patch(&patch, "test.js");
    EXPECT_STREQ(fn, "C:/Path/to/thcrap/repos/repo_name/patch_name//test.js");
    free(fn);
    free(patch.archive);
}

TEST(PatchFile, DirCreateForFn)
{
    if (std::filesystem::exists("test_dir")) {
        std::filesystem::remove_all("test_dir");
    }

    dir_create_for_fn("test_dir/dir2/dir3/file.txt");
    EXPECT_TRUE(std::filesystem::is_directory("test_dir/dir2/dir3"));

    std::filesystem::remove_all("test_dir");
}

class PatchFileTest : public ::testing::Test
{
protected:
    patch_t patch;

    void SetUp() override
    {
        if (std::filesystem::exists("testdir")) {
            std::filesystem::remove_all("testdir");
        }
        std::filesystem::create_directory("testdir");

        ScopedJson patch_js = json_pack("{s:s,s:s,s:[s,s],s:[s,s],s:{s:b,s:b},s:s,s:s,s:i}",
            "id", "base_tsa",
            "title", "Base patch for TSA games",
            "servers", "https://www.example.com/", "https://www.example2.com/",
            "dependencies", "thpatch/patch1", "patch2",
            "fonts", "arial.ttf", true, "comicsans.ttf", true,
            "motd", "This is a message",
            "motd_title", "Title for motd",
            "motd_type", 12
        );
        json_dump_file(*patch_js, "testdir/patch.js", 0);

        ScopedJson runconfig_js = json_pack("{s:[s,s],s:b}",
            "ignore", "*.bmp", "*.jpg",
            "update", false
        );
        this->patch = patch_init("testdir", *runconfig_js, 0);
    }

    void TearDown() override
    {
        patch_free(&this->patch);
        std::filesystem::remove_all("testdir");
    }
};

TEST_F(PatchFileTest, PatchInit)
{
    // Writing a nice test for patch_archive is a bit hard and pointless,
    // we don't really care about its exact value.
    // The patch_file_exist will test if the archive value is actually meaningful.
    EXPECT_NE(patch.archive, nullptr);
    EXPECT_STREQ(patch.id, "base_tsa");
    EXPECT_STREQ(patch.title, "Base patch for TSA games");

    ASSERT_NE(patch.servers, nullptr);
    EXPECT_STREQ(patch.servers[0], "https://www.example.com/");
    EXPECT_STREQ(patch.servers[1], "https://www.example2.com/");
    EXPECT_EQ(patch.servers[2], nullptr);

    // TODO: dependencies
    ASSERT_NE(patch.dependencies, nullptr);
    EXPECT_STREQ(patch.dependencies[0].repo_id, "thpatch");
    EXPECT_STREQ(patch.dependencies[0].patch_id, "patch1");
    EXPECT_EQ(patch.dependencies[1].repo_id, nullptr);
    EXPECT_STREQ(patch.dependencies[1].patch_id, "patch2");
    EXPECT_EQ(patch.dependencies[2].patch_id, nullptr);

    ASSERT_NE(patch.fonts, nullptr);
    EXPECT_STREQ(patch.fonts[0], "arial.ttf");
    EXPECT_STREQ(patch.fonts[1], "comicsans.ttf");
    EXPECT_EQ(patch.fonts[2], nullptr);

    ASSERT_NE(patch.ignore, nullptr);
    EXPECT_STREQ(patch.ignore[0], "*.bmp");
    EXPECT_STREQ(patch.ignore[1], "*.jpg");
    EXPECT_EQ(patch.ignore[2], nullptr);

    EXPECT_EQ(patch.update, false);

    EXPECT_STREQ(patch.motd, "This is a message");
    EXPECT_STREQ(patch.motd_title, "Title for motd");
    EXPECT_EQ(patch.motd_type, 12u);

    // TODO: test invalid parameters
}

TEST_F(PatchFileTest, PatchFileExists)
{
    std::ofstream file;

    file.open("testdir/test_exist.js");
    file.close();
    EXPECT_TRUE (patch_file_exists(&patch, "test_exist.js"));
    EXPECT_FALSE(patch_file_exists(&patch, "test_missing.js"));

    std::filesystem::create_directory("testdir/subdir");
    file.open("testdir/subdir/test_exist.js");
    file.close();
    EXPECT_TRUE (patch_file_exists(&patch, "subdir/test_exist.js"));
    EXPECT_FALSE(patch_file_exists(&patch, "subdir/test_missing.js"));

    // Test blacklist
    file.open("testdir/test_blacklist.bmp");
    file.close();
    EXPECT_FALSE(patch_file_exists(&patch, "test_blacklist.bmp"));
}

TEST_F(PatchFileTest, PatchFileBlacklisted)
{
    EXPECT_TRUE (patch_file_blacklisted(&patch, "file.bmp"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "file.jpg"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "file.png"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "file_without_extension"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "dir/file.bmp"));
    EXPECT_TRUE (patch_file_blacklisted(&patch, "dir/file.jpg"));
    EXPECT_FALSE(patch_file_blacklisted(&patch, "dir/file.png"));
}

TEST_F(PatchFileTest, PatchFileLoad)
{
    std::ofstream file;
    char *buffer;
    size_t file_size;

    file.open("testdir/test_exist.txt");
    file.write("abcde", 6);
    file.close();
    buffer = (char*)patch_file_load(&patch, "test_exist.txt", &file_size);
    EXPECT_STREQ(buffer, "abcde");
    EXPECT_EQ(file_size, 6u);
    free(buffer);

    EXPECT_EQ(patch_file_load(&patch, "test_missing.txt", &file_size), nullptr);
    EXPECT_EQ(file_size, 0u);

    std::filesystem::create_directory("testdir/subdir");
    file.open("testdir/subdir/test_exist.txt");
    file.write("abcde", 6);
    file.close();
    buffer = (char*)patch_file_load(&patch, "subdir/test_exist.txt", &file_size);
    EXPECT_STREQ(buffer, "abcde");
    EXPECT_EQ(file_size, 6u);
    free(buffer);

    // Test blacklist
    file.open("testdir/test_blacklist.bmp");
    file.write("abcde", 6);
    file.close();
    EXPECT_EQ(patch_file_load(&patch, "test_blacklist.bmp", &file_size), nullptr);
    EXPECT_EQ(file_size, 0u);

    // Test without size
    buffer = (char*)patch_file_load(&patch, "test_exist.txt", nullptr);
    EXPECT_STREQ(buffer, "abcde");
    free(buffer);
}

// TODO: patch_json_load, patch_json_merge, patch_file_store, patch_json_store, patch_file_delete

TEST_F(PatchFileTest, PatchToRunconfigJson)
{
    ScopedJson json = patch_to_runconfig_json(&patch);
    EXPECT_NE(*json, nullptr);
    EXPECT_STREQ(json_string_value(json_object_get(*json, "archive")), patch.archive);
}

TEST_F(PatchFileTest, PatchRelToAbs)
{
    // Create a file
    patch_file_store(&patch, "test.txt", "abcde", 6);

    std::string cwd = std::filesystem::current_path().generic_u8string();
    patch_rel_to_abs(&patch, cwd.c_str());

    // The patch path should point to the same directory
    EXPECT_TRUE(patch_file_exists(&patch, "test.txt"));

    // The path should be absolute
    std::filesystem::path path = patch.archive;
    EXPECT_TRUE(path.is_absolute());
}

TEST(PatchFile, PatchBuild)
{
    patch_desc_t desc = {
        strdup("repo_name"),
        strdup("patch_name")
    };
    patch_t patch = patch_build(&desc);
    EXPECT_STREQ(patch.archive, "repos/repo_name/patch_name/");
    free(desc.repo_id);
    free(desc.patch_id);
    patch_free(&patch);
}

TEST(PatchFile, PatchDepToDesc)
{
    patch_desc_t desc;

    // Invalid value
    desc = patch_dep_to_desc(nullptr);
    EXPECT_EQ(desc.repo_id, nullptr);
    EXPECT_EQ(desc.patch_id, nullptr);

    // Absolute value
    desc = patch_dep_to_desc("nmlgc/base_tsa");
    EXPECT_STREQ(desc.repo_id, "nmlgc");
    EXPECT_STREQ(desc.patch_id, "base_tsa");
    free(desc.repo_id);
    free(desc.patch_id);

    // Relative value
    desc = patch_dep_to_desc("base_tsa");
    EXPECT_EQ(desc.repo_id, nullptr);
    EXPECT_STREQ(desc.patch_id, "base_tsa");
    free(desc.patch_id);
}

// TODO: patchhook
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2012, Achilleas Margaritis
 * Copyright (c) 2014, David T. Chisnall
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <cassert>
#include "ast.hh"

namespace {
/**
 * The current AST container.  When constructing an object, this is set and
 * then the constructors for the fields run, accessing it to detect their
 * parents.
 */
// FIXME: Should be thread_local, but that doesn't seem to work on OS X for
// some reason (__thread does)
__thread pegmatite::ASTContainer *current = 0;
/**
 * The current parser delegate.  When constructing an object, this is set and
 * then the constructors for the fields run, accessing it to detect their
 * parents.
 */
__thread pegmatite::ASTParserDelegate *currentParserDelegate;
}

namespace pegmatite {

/**
 * Out-of-line virtual destructor forces vtable to be emitted in this
 * translation unit only.
 */
ASTNode::~ASTNode()
{
}

/** sets the container under construction to be this.
 */
ASTContainer::ASTContainer()
{
	current = this;
}

/** Asks all members to construct themselves from the stack.
	The members are asked to construct themselves in reverse order.
	from a node stack.
	@param st stack.
 */
void ASTContainer::construct(const InputRange &r, ASTStack &st)
{
	for(auto it = members.rbegin(); it != members.rend(); ++it)
	{
		ASTMember *member = *it;
		member->construct(r, st);
	}
	// We don't need the members vector anymore, so clean up the storage it
	// uses.
	ASTMember_vector().swap(members);
}

ASTMember::ASTMember()
{
	assert(current);
	container_node = current;
	current->members.push_back(this);
}
ASTMember::~ASTMember() {}

ASTParserDelegate::ASTParserDelegate()
{
	currentParserDelegate = this;
}

void ASTParserDelegate::set_parse_proc(const Rule &r, parse_proc p)
{
	handlers[std::addressof(r)] = p;
}
void ASTParserDelegate::bind_parse_proc(const Rule &r, parse_proc p)
{
	currentParserDelegate->set_parse_proc(r, p);
}
parse_proc ASTParserDelegate::get_parse_proc(const Rule &r) const
{
	auto it = handlers.find(std::addressof(r));
	if (it == handlers.end()) return 0;
	return it->second;
}

/** parses the given input.
	@param i input.
	@param g root rule of grammar.
	@param ws whitespace rule.
	@param err callback for reporting errors.
	@param d user data, passed to the parse procedures.
	@return pointer to AST node created, or null if there was an Error.
		The return object must be deleted by the caller.
 */
std::unique_ptr<ASTNode> parse(Input &input, const Rule &g, const Rule &ws,
                               ErrorReporter &err, const ParserDelegate &d)
{
	ASTStack st;
	if (!parse(input, g, ws, err, d, &st)) return 0;
	if (st.size() > 1)
	{
		int i = 0;
		for (auto &I : st)
		{
			auto *val = I.second.get();
			fprintf(stderr, "[%d] %s\n", i++, typeid(*val).name());
		}
	}
	assert(st.size() == 1);
	return std::move(st[0].second);
}

} //namespace pegmatite
<commit_msg>Include C standard library header.<commit_after>/*-
 * Copyright (c) 2012, Achilleas Margaritis
 * Copyright (c) 2014, David T. Chisnall
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <cassert>
#include <cstdlib>
#include "ast.hh"

namespace {
/**
 * The current AST container.  When constructing an object, this is set and
 * then the constructors for the fields run, accessing it to detect their
 * parents.
 */
// FIXME: Should be thread_local, but that doesn't seem to work on OS X for
// some reason (__thread does)
__thread pegmatite::ASTContainer *current = 0;
/**
 * The current parser delegate.  When constructing an object, this is set and
 * then the constructors for the fields run, accessing it to detect their
 * parents.
 */
__thread pegmatite::ASTParserDelegate *currentParserDelegate;
}

namespace pegmatite {

/**
 * Out-of-line virtual destructor forces vtable to be emitted in this
 * translation unit only.
 */
ASTNode::~ASTNode()
{
}

/** sets the container under construction to be this.
 */
ASTContainer::ASTContainer()
{
	current = this;
}

/** Asks all members to construct themselves from the stack.
	The members are asked to construct themselves in reverse order.
	from a node stack.
	@param st stack.
 */
void ASTContainer::construct(const InputRange &r, ASTStack &st)
{
	for(auto it = members.rbegin(); it != members.rend(); ++it)
	{
		ASTMember *member = *it;
		member->construct(r, st);
	}
	// We don't need the members vector anymore, so clean up the storage it
	// uses.
	ASTMember_vector().swap(members);
}

ASTMember::ASTMember()
{
	assert(current);
	container_node = current;
	current->members.push_back(this);
}
ASTMember::~ASTMember() {}

ASTParserDelegate::ASTParserDelegate()
{
	currentParserDelegate = this;
}

void ASTParserDelegate::set_parse_proc(const Rule &r, parse_proc p)
{
	handlers[std::addressof(r)] = p;
}
void ASTParserDelegate::bind_parse_proc(const Rule &r, parse_proc p)
{
	currentParserDelegate->set_parse_proc(r, p);
}
parse_proc ASTParserDelegate::get_parse_proc(const Rule &r) const
{
	auto it = handlers.find(std::addressof(r));
	if (it == handlers.end()) return 0;
	return it->second;
}

/** parses the given input.
	@param i input.
	@param g root rule of grammar.
	@param ws whitespace rule.
	@param err callback for reporting errors.
	@param d user data, passed to the parse procedures.
	@return pointer to AST node created, or null if there was an Error.
		The return object must be deleted by the caller.
 */
std::unique_ptr<ASTNode> parse(Input &input, const Rule &g, const Rule &ws,
                               ErrorReporter &err, const ParserDelegate &d)
{
	ASTStack st;
	if (!parse(input, g, ws, err, d, &st)) return 0;
	if (st.size() > 1)
	{
		int i = 0;
		for (auto &I : st)
		{
			auto *val = I.second.get();
			fprintf(stderr, "[%d] %s\n", i++, typeid(*val).name());
		}
	}
	assert(st.size() == 1);
	return std::move(st[0].second);
}

} //namespace pegmatite
<|endoftext|>


<commit_before>#include <CameraHandler.hpp>
#include <InputHandler.hpp>
#include <CameraClasses/ThirdPersonCamera.hpp>
#include <CameraClasses/FreeLookCamera.hpp>
#include <PlayerHandler.hpp>
#include <InputHandlerClient.hpp>
// Engine
#include <DoremiEngine/Graphic/Include/Interface/Camera/Camera.hpp>
#include <DoremiEngine/Graphic/Include/Interface/Manager/CameraManager.hpp>
#include <DoremiEngine/Graphic/Include/Interface/Manager/SubModuleManager.hpp>
#include <DoremiEngine/Graphic/Include/GraphicModule.hpp>
// Third party

namespace Doremi
{
    namespace Core
    {
        CameraHandler* CameraHandler::m_singleton = nullptr;

        void CameraHandler::StartCameraHandler(const DoremiEngine::Core::SharedContext& p_sharedContext)
        {
            if(m_singleton == nullptr)
            {
                m_singleton = new CameraHandler(p_sharedContext);
            }
        }
        CameraHandler* CameraHandler::GetInstance()
        {
            if(m_singleton == nullptr)
            {
                // TODOKO error message, not started
            }
            return m_singleton;
        }

        CameraHandler::CameraHandler(const DoremiEngine::Core::SharedContext& p_sharedContext) : m_sharedContext(p_sharedContext)
        {
            using namespace DirectX;
            DoremiEngine::Graphic::CameraManager& t_graphicModuleCameraManager = m_sharedContext.GetGraphicModule().GetSubModuleManager().GetCameraManager();
            XMFLOAT4X4 projection;
            XMMATRIX mat =
                XMMatrixTranspose(XMMatrixPerspectiveFovLH(90.0f * 3.14f / 180.0f, 800.0f / 600.0f, 0.1f, 1000.0f)); // TODOKO use config values
            XMStoreFloat4x4(&projection, mat);

            DoremiEngine::Graphic::Camera* freecamera = t_graphicModuleCameraManager.BuildNewCamera(projection);
            DoremiEngine::Graphic::Camera* thirdPersonCamera = t_graphicModuleCameraManager.BuildNewCamera(projection);
            m_thirdPersonCamera = new ThirdPersonCamera(thirdPersonCamera);
            m_freeLookCamera = new FreeLookCamera(freecamera);
            m_currentCamera = CameraType::FREELOOK;
        }

        CameraHandler::~CameraHandler() {}

        void CameraHandler::Update(double p_dt)
        {
            DoremiEngine::Graphic::CameraManager& t_graphicModuleCameraManager = m_sharedContext.GetGraphicModule().GetSubModuleManager().GetCameraManager();
            InputHandlerClient* inputHandler = (InputHandlerClient*)PlayerHandler::GetInstance()->GetDefaultInputHandler();
            if(inputHandler != nullptr)
            {
                if(inputHandler->CheckForOnePress((int)UserCommandPlaying::DebugButton))
                {
                    switch(m_currentCamera)
                    {
                        case CameraType::FREELOOK:
                            ChangeCamera(CameraType::THIRDPERSON);
                            break;
                        case CameraType::THIRDPERSON:
                            ChangeCamera(CameraType::FREELOOK);
                            break;
                        default:
                            break;
                    }
                }
                switch(m_currentCamera)
                {
                    case CameraType::FREELOOK:
                        inputHandler->SetCursorInvisibleAndMiddle(false);
                        m_freeLookCamera->Update(p_dt);
                        t_graphicModuleCameraManager.PushCameraToDevice(m_freeLookCamera->GetCamera());
                        break;
                    case CameraType::THIRDPERSON:
                        inputHandler->SetCursorInvisibleAndMiddle(true);
                        m_thirdPersonCamera->Update(p_dt);
                        t_graphicModuleCameraManager.PushCameraToDevice(m_thirdPersonCamera->GetCamera());
                        break;
                    default:
                        break;
                }
            }
        }
        void CameraHandler::ChangeCamera(CameraType p_type) { m_currentCamera = p_type; }
        DirectX::XMFLOAT3 CameraHandler::GetActiveCameraPosition()
        {
            switch(m_currentCamera)
            {
                case CameraType::FREELOOK:
                    return m_freeLookCamera->GetCamera().GetCameraPosition();
                    break;
                case CameraType::THIRDPERSON:
                    return m_thirdPersonCamera->GetCamera().GetCameraPosition();
                    break;
                default:
                    break;
            }
        }
    }
}<commit_msg>Added todo for a warning<commit_after>#include <CameraHandler.hpp>
#include <InputHandler.hpp>
#include <CameraClasses/ThirdPersonCamera.hpp>
#include <CameraClasses/FreeLookCamera.hpp>
#include <PlayerHandler.hpp>
#include <InputHandlerClient.hpp>
// Engine
#include <DoremiEngine/Graphic/Include/Interface/Camera/Camera.hpp>
#include <DoremiEngine/Graphic/Include/Interface/Manager/CameraManager.hpp>
#include <DoremiEngine/Graphic/Include/Interface/Manager/SubModuleManager.hpp>
#include <DoremiEngine/Graphic/Include/GraphicModule.hpp>
// Third party

namespace Doremi
{
    namespace Core
    {
        CameraHandler* CameraHandler::m_singleton = nullptr;

        void CameraHandler::StartCameraHandler(const DoremiEngine::Core::SharedContext& p_sharedContext)
        {
            if(m_singleton == nullptr)
            {
                m_singleton = new CameraHandler(p_sharedContext);
            }
        }
        CameraHandler* CameraHandler::GetInstance()
        {
            if(m_singleton == nullptr)
            {
                // TODOKO error message, not started
            }
            return m_singleton;
        }

        CameraHandler::CameraHandler(const DoremiEngine::Core::SharedContext& p_sharedContext) : m_sharedContext(p_sharedContext)
        {
            using namespace DirectX;
            DoremiEngine::Graphic::CameraManager& t_graphicModuleCameraManager = m_sharedContext.GetGraphicModule().GetSubModuleManager().GetCameraManager();
            XMFLOAT4X4 projection;
            XMMATRIX mat =
                XMMatrixTranspose(XMMatrixPerspectiveFovLH(90.0f * 3.14f / 180.0f, 800.0f / 600.0f, 0.1f, 1000.0f)); // TODOKO use config values
            XMStoreFloat4x4(&projection, mat);

            DoremiEngine::Graphic::Camera* freecamera = t_graphicModuleCameraManager.BuildNewCamera(projection);
            DoremiEngine::Graphic::Camera* thirdPersonCamera = t_graphicModuleCameraManager.BuildNewCamera(projection);
            m_thirdPersonCamera = new ThirdPersonCamera(thirdPersonCamera);
            m_freeLookCamera = new FreeLookCamera(freecamera);
            m_currentCamera = CameraType::FREELOOK;
        }

        CameraHandler::~CameraHandler() {}

        void CameraHandler::Update(double p_dt)
        {
            DoremiEngine::Graphic::CameraManager& t_graphicModuleCameraManager = m_sharedContext.GetGraphicModule().GetSubModuleManager().GetCameraManager();
            InputHandlerClient* inputHandler = (InputHandlerClient*)PlayerHandler::GetInstance()->GetDefaultInputHandler();
            if(inputHandler != nullptr)
            {
                if(inputHandler->CheckForOnePress((int)UserCommandPlaying::DebugButton))
                {
                    switch(m_currentCamera)
                    {
                        case CameraType::FREELOOK:
                            ChangeCamera(CameraType::THIRDPERSON);
                            break;
                        case CameraType::THIRDPERSON:
                            ChangeCamera(CameraType::FREELOOK);
                            break;
                        default:
                            break;
                    }
                }
                switch(m_currentCamera)
                {
                    case CameraType::FREELOOK:
                        inputHandler->SetCursorInvisibleAndMiddle(false);
                        m_freeLookCamera->Update(p_dt);
                        t_graphicModuleCameraManager.PushCameraToDevice(m_freeLookCamera->GetCamera());
                        break;
                    case CameraType::THIRDPERSON:
                        inputHandler->SetCursorInvisibleAndMiddle(true);
                        m_thirdPersonCamera->Update(p_dt);
                        t_graphicModuleCameraManager.PushCameraToDevice(m_thirdPersonCamera->GetCamera());
                        break;
                    default:
                        break;
                }
            }
        }
        void CameraHandler::ChangeCamera(CameraType p_type) { m_currentCamera = p_type; }
        DirectX::XMFLOAT3 CameraHandler::GetActiveCameraPosition()
        {
            switch(m_currentCamera)
            {
                case CameraType::FREELOOK:
                    return m_freeLookCamera->GetCamera().GetCameraPosition();
                    break;
                case CameraType::THIRDPERSON:
                    return m_thirdPersonCamera->GetCamera().GetCameraPosition();
                    break;
                default:
                    break;
            }
            // TODO Returnpath
        }
    }
}<|endoftext|>