text:
#include <boost/config.hpp>

//  sp_convertible_test.cpp
//
//  Copyright (c) 2012 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0.
//  See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt

#include <boost/detail/lightweight_test.hpp>
#include <boost/smart_ptr/detail/sp_convertible.hpp>

//

class X;

class B
{
};

class D: public B
{
};

#define TEST_CV_TRUE( T, U ) \
    BOOST_TEST(( sp_convertible< T, U >::value == true )); \
    BOOST_TEST(( sp_convertible< T, const U >::value == true )); \
    BOOST_TEST(( sp_convertible< T, volatile U >::value == true )); \
    BOOST_TEST(( sp_convertible< T, const volatile U >::value == true )); \
    BOOST_TEST(( sp_convertible< const T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, const U >::value == true )); \
    BOOST_TEST(( sp_convertible< const T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, const volatile U >::value == true )); \
    BOOST_TEST(( sp_convertible< volatile T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, volatile U >::value == true )); \
    BOOST_TEST(( sp_convertible< volatile T, const volatile U >::value == true )); \
    BOOST_TEST(( sp_convertible< const volatile T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, const volatile U >::value == true ));

#define TEST_CV_FALSE( T, U ) \
    BOOST_TEST(( sp_convertible< T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< T, const volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const T, const volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< volatile T, const volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, const U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, volatile U >::value == false )); \
    BOOST_TEST(( sp_convertible< const volatile T, const volatile U >::value == false ));

int main()
{
#if !defined( BOOST_SP_NO_SP_CONVERTIBLE )

    using boost::detail::sp_convertible;

    TEST_CV_TRUE( X, X )
    TEST_CV_TRUE( X, void )
    TEST_CV_FALSE( void, X )
    TEST_CV_TRUE( D, B )
    TEST_CV_FALSE( B, D )

    TEST_CV_TRUE( X[], X[] )
    TEST_CV_FALSE( D[], B[] )

    TEST_CV_TRUE( X[3], X[3] )
    TEST_CV_FALSE( X[3], X[4] )
    TEST_CV_FALSE( D[3], B[3] )

    TEST_CV_TRUE( X[3], X[] )
    TEST_CV_FALSE( X[], X[3] )

    TEST_CV_TRUE( X[], void )
    TEST_CV_FALSE( void, X[] )

    TEST_CV_TRUE( X[3], void )
    TEST_CV_FALSE( void, X[3] )

#endif

    return boost::report_errors();
}


text:
#include <NTL/pair_ZZ_pEX_long.h>

#include <NTL/new.h>

NTL_START_IMPL

NTL_pair_impl(ZZ_pEX,long,pair_ZZ_pEX_long)
NTL_pair_io_impl(ZZ_pEX,long,pair_ZZ_pEX_long)
NTL_pair_eq_impl(ZZ_pEX,long,pair_ZZ_pEX_long)

NTL_vector_impl(pair_ZZ_pEX_long,vec_pair_ZZ_pEX_long)
NTL_io_vector_impl(pair_ZZ_pEX_long,vec_pair_ZZ_pEX_long)
NTL_eq_vector_impl(pair_ZZ_pEX_long,vec_pair_ZZ_pEX_long)

NTL_END_IMPL

ext: cpp

max_stars_repo_name: vshesh/RUNEtag

max_issues_repo_name: vshesh/RUNEtag

max_forks_repo_name: vshesh/RUNEtag


text:
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
// Helmet Software Framework
//
// Copyright (C) 2018 Hat Boy Software, Inc.
//
//  @author Matthew Alan Gray - <mgray@hatboysoftware.com>
//  @author Tony Richards - <trichards@indiezen.com>
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
#pragma once

#include "Configuration.hpp"

#include <memory>

#include <boost/noncopyable.hpp>

//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
namespace Helmet {
namespace Enterprise {
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

class HELMET_ENTERPRISE_DLL_LINK I_TimeoutHandler
:   boost::noncopyable
{
    /// @name Types
    /// @{
public:
    /// @}

    /// @name I_TimeoutHandler interface.
    /// @{
public:
    /// Handle the timeout
    virtual void handleTimeout() = 0;
    /// @}

    /// @name Events
    /// @{
public:
    /// @}

    /// @name 'Structors
    /// @{
protected:
             I_TimeoutHandler() = default;
    virtual ~I_TimeoutHandler() = default;
    /// @}

};  // interface I_TimeoutHandler

//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
}   // namespace Enterprise
}   // namespace Helmet
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

ext: hpp

max_stars_repo_name: hatboysoftware/helmet

max_issues_repo_name: hatboysoftware/helmet

max_forks_repo_name: hatboysoftware/helmet


text:
// Copyright 2015-2021 Carnegie Mellon University.  See LICENSE file for terms.

#ifndef Pharos_Calls_H
#define Pharos_Calls_H

#include <boost/format.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/iterator/filter_iterator.hpp> // for filter_iterator

#include "funcs.hpp"
#include "imports.hpp"
#include "state.hpp"
#include "convention.hpp"
#include "typedb.hpp"
#include "vcall.hpp"
#include "threads.hpp"

namespace pharos {

class PDG;

enum CallType {
  CallImmediate,
  CallRegister,
  CallImport,
  CallGlobalVariable,
  CallVirtualFunction,
  CallUnknown,
  CallUnspecified
};

enum CallTargetLocation {
  CallInternal,
  CallExternal,
  CallLocationUnknown
};

// Added to quickly extract calls where the target is known/unknown
enum TargetSetCardinality {
  TargetSizeUnspecified,
  TargetSizeZero,
  TargetSizeOne,
  TargetSizeMany
};

// this forward declaration is needed to create a filtered iterator on the call descriptor map
class CallDescMapPredicate;
class VirtualFunctionCallAnalyzer;

class CallDescriptor : private Immobile {

  // The predicate class must be a friend to access class members. Alternatively, we could
  // define additional get/set methods.
  friend class CallDescMapPredicate;

  mutable shared_mutex mutex;

  // The address of the call instruction.  This can refer to address that does not yet have a
  // call instruction, or it can be an invalid address (typically zero).
  rose_addr_t address = 0;

  // The SgAsmX86Instruction object for the call instruction.  This can be NULL if the
  // instruction has not been found yet.
  SgAsmX86Instruction* insn = nullptr;

  // The function descriptor that describes the aspects of this call that are specific to the
  // call target.  In many calls, this will point directly to the descriptor for the actual
  // function that is called.  But if there are multiple targets, we will allocate a new
  // descriptor that merges the results from all of the targets.  If the call target is a
  // single import, this pointer will point to the function descriptor associated with the
  // import.
  FunctionDescriptor* function_descriptor = nullptr;

  // If the FunctionDescriptor pointed to by function_descriptor is owned (allocated) by this
  // object, it is managed here.
  std::unique_ptr<FunctionDescriptor> owned_function_descriptor;

  // The addresses that the call can transfer control to.  This list could be empty if we don't
  // know the answers.  If could have multiple answers if the call is to a register which could
  // contain multiple values.  Typically the set contains a single value.  For imports, the
  // address of the import itself is added to represent the address that the loader would
  // supply dynamically when it resolved the import.  There's still some ambiguity here about
  // whether there's any simplification via thunks being done on this list, but Cory's
  // unconfirmed belief is that this list only contains direct targets, although it does
  // contain virtual call resolutions and other kinds of simplification, which is perhaps an
  // inconsistent design choice.
  CallTargetSet targets;

  // The symbolic state at the time of the call (useful for getting the values of parameters).
  SymbolicStatePtr state;

  // How confident are we in the target list being complete and correct?  One one extreme we
  // know conclusively that the list is correct.  At the other, we know that it's actively
  // wrong (e.g. empty).  In between, we may be confident or just guessing.
  GenericConfidence confidence = ConfidenceNone;

  // A variable to represent the stack delta when then delta is unknown (missing)
  mutable LeafNodePtr stack_delta_variable;

  // Is the call internal, external, or unknown?
  CallTargetLocation call_location = CallLocationUnknown;

  // What type is this call?  e.g. Is it a call to an immediate value, a register?
  CallType call_type = CallUnknown;

  // What is the import descriptor for this call, if it is external?
  ImportDescriptor* import_descriptor = nullptr;

  // The overridden function from the user config file.
  std::unique_ptr<FunctionDescriptor> function_override;

  // The function that contains this call descriptor.
  FunctionDescriptor* containing_function = nullptr;

  // The parameters for this call.
  ParameterList parameters;

  // The symbolic return value.
  SymbolicValuePtr return_value;

  // For calls that indeterminate targets, we may want to record the "expected" stack delta for
  // the call, without any rigorous knowledge of where we call to.  This stack delta contains
  // that information.
  StackDelta stack_delta;

  // The resolution details for virtual function calls.
  VirtualFunctionCallVector virtual_calls;

  void analyze();

  void _update_connections();

  void _print(std::ostream &o) const;

  struct self { CallDescriptor & self; };

  friend std::ostream& operator<<(std::ostream &o, const self &cd) {
    cd.self._print(o);
    return o;
  }

 public:

  DescriptorSet& ds;

  CallDescriptor(DescriptorSet& d) : ds(d) {}

  CallDescriptor(DescriptorSet& d, SgAsmX86Instruction* i)
    : insn(i), ds(d)
  {
    analyze();
  }

  void update_connections() {
    write_guard<decltype(mutex)> guard{mutex};
    _update_connections();
  }

  SgAsmInstruction* get_insn() const {
    return insn;
  }
  rose_addr_t get_address() const {
    return address;
  }
  CallTargetLocation get_target_location() const {
    return call_location;
  }

  // Add a new address to the targets list, recompute the merged descriptions, and update
  // connections with the affected function descriptor.
  void add_target(rose_addr_t taddr);
  // Return the set of target addresses.
  auto get_targets() const {
    return make_read_locked_range(targets, mutex);
  }

  std::string address_string() const {
    return str(boost::format("0x%08X") % address);
  }

  // One spot needs read/write access to update the callers.
  FunctionDescriptor* get_rw_function_descriptor() { return function_descriptor; }

  // Provide read-only access to the function descriptor and import descriptor.
  const FunctionDescriptor* get_function_descriptor() const { return function_descriptor; }
  const ImportDescriptor* get_import_descriptor() const { return import_descriptor; }

  // Get and set the return value.
  const SymbolicValuePtr & get_return_value() const {
    read_guard<decltype(mutex)> guard{mutex};
    return return_value;
  }
  void set_return_value(SymbolicValuePtr r) {
    write_guard<decltype(mutex)> guard{mutex};
    return_value = r;
  }

  void add_import_target(ImportDescriptor* id);

  void update_call_type(CallType ct, GenericConfidence conf);

  // Are we a tail-call optimized JMP instruction?
  bool is_tail_call() const {
    return insn_is_jmp(insn);
  }

  // Add virtual call resolutions
  void add_virtual_resolution(VirtualFunctionCallInformation& vci, GenericConfidence conf);
  // Available only if object oriented analysis has been performed, empty otherwise.
  auto get_virtual_calls() const {
    return make_read_locked_range(virtual_calls, mutex);
  }

  CallType get_call_type() const  {
    read_guard<decltype(mutex)> guard{mutex};
    return call_type;
  }

  StackDelta get_stack_delta() const;
  LeafNodePtr get_stack_delta_variable() const;
  StackDelta get_stack_parameters() const;

  // Cory wants to isolate this better.  Maybe with friend, or by moving defuse code into funcs?
  ParameterList& get_rw_parameters() { return parameters; }
  const ParameterList& get_parameters() const { return parameters; }

  // Return the function that contains this call.
  FunctionDescriptor* get_containing_function() const { return containing_function; }

  void print(std::ostream &o) const {
    read_guard<decltype(mutex)> guard{mutex};
    _print(o);
  }
  // Return the "real" call targets, e.g. with thunks removed.
  // CallTargetSet get_real_targets();

  // Does the call never return (because all of the targets never return, e.g. exits).
  bool get_never_returns() const;

  // Get the state at the time of the call.
  const SymbolicStatePtr get_state() const { return state; }
  // Set the state for this call (called while building the PDG).
  void set_state(SymbolicStatePtr s) {
    // Clone the state so that we get an unchanging copy?  In theory the reference counted
    // pointer should protect against deallocation.  Do we really need a clone?  Cory thinks
    // this is only set in defuse.cpp evaluate bblock, and then never used?
    state = s->sclone();
  }
  // Discard the state, freeing memory by releasing references to the smart pointers.
  void discard_state() {
    state.reset();
  }

  // Validate the call description, complaining about any unusual.
  void validate(std::ostream &o, const FunctionDescriptorMap& fdmap) const;

  // Unused?
  bool operator<(const CallDescriptor& other) {
    return (address < other.get_address());
  }

  friend std::ostream& operator<<(std::ostream &o, const CallDescriptor &cd) {
    cd.print(o);
    return o;
  }
};

class CallDescriptorMap: public std::map<rose_addr_t, CallDescriptor>, private Immobile {

 public:

  CallDescriptorMap() = default;

  template<typename... T>
  CallDescriptor * add(rose_addr_t addr, T &&... cd_args) {
    auto it = find(addr);
    if (it != end()) {
      erase(it);
    }
    auto result = emplace(std::piecewise_construct, std::forward_as_tuple(addr),
                          std::forward_as_tuple(std::forward<T>(cd_args)...));
    assert(result.second);
    return &result.first->second;
  }

  const CallDescriptor* get_call(rose_addr_t addr) const {
    CallDescriptorMap::const_iterator it = this->find(addr);
    if (it != this->end())
      return &(it->second);
    else
      return NULL;
  }

  CallDescriptor* get_call(rose_addr_t addr) {
    CallDescriptorMap::iterator it = this->find(addr);
    if (it != this->end())
      return &(it->second);
    else
      return NULL;
  }
};

//
// Predicate for filtered iterators on CallDescriptors. There are two configuration parameters
// to carve the CallDescriptorMap: call type and confidence.  The predicate determines which
// values are included during iteration. Currently, the included values are a conjunction. That
// is, they are "and'd" together
class CallDescMapPredicate {
 public:

  // The default values for predicate parameters are unspecified, which is different than
  // unknown.
  CallDescMapPredicate(CallType ct = CallUnspecified, GenericConfidence gc =
                       ConfidenceUnspecified, TargetSetCardinality tc = TargetSizeUnspecified):
    call_type(ct), conf(gc), target_card(tc) {
  }

  // evaluate whether CallDescriptorMap value should be included in the iterator
  // unspecified values are ignored
  bool operator()(CallDescriptorMap::value_type & c) const {

    bool ct_match = true, gc_match = true, tc_match = true;

    if (call_type != CallUnspecified) {
      ct_match = (c.second.call_type == call_type);
    }

    if (conf != ConfidenceUnspecified) {
      gc_match = (c.second.confidence == conf);
    }

    if (target_card != TargetSizeUnspecified) {
      if (target_card == TargetSizeZero) {
        tc_match = c.second.targets.isEmpty();
      } else if (target_card == TargetSizeOne) {
        tc_match = (c.second.targets.size() == 1);
      } else if (target_card == TargetSizeMany) {
        tc_match = (c.second.targets.size() > 1);
      }
    }

    // Currently This is a conjunction. In the future it may support different logic
    return (ct_match && gc_match && tc_match);
  }

 private:

  CallType call_type;
  GenericConfidence conf;
  TargetSetCardinality target_card;
};

using ValueList = std::vector<typedb::Value>;

class CallParamInfoBuilder;

class CallParamInfo {
 private:
  CallDescriptor const & cd;
  ValueList const val;
  CallParamInfo(CallDescriptor const & cd_, ValueList && val_)
    : cd(cd_), val(std::move(val_))
  {}
  friend class CallParamInfoBuilder;

  static typedb::TypeRef type_from_value(typedb::Value const & val) {
    return val.get_type();
  }
  static std::string const & name_from_param(ParameterDefinition const & pd) {
    return pd.get_name();
  }
  static std::string const & type_name_from_param(ParameterDefinition const & pd) {
    return pd.get_type();
  }

 public:
  CallDescriptor const & descriptor() const {
    return cd;
  }
  ValueList const & values() const {
    return val;
  }
  auto params() const {
    return cd.get_parameters().get_params();
  }

  auto types() const {
    return values() | boost::adaptors::transformed(type_from_value);
  }

  auto names() const {
    return params() | boost::adaptors::transformed(name_from_param);
  }

  auto type_names() const {
    return params() | boost::adaptors::transformed(type_name_from_param);
  }
};

class CallParamInfoBuilder {
 private:
  typedb::DB db;
  Memory const & memory;
 public:
  CallParamInfoBuilder(typedb::DB && db_, Memory const & mem) : db(db_), memory(mem) {}
  CallParamInfoBuilder(ProgOptVarMap const & vm, Memory const & mem) :
    db(typedb::DB::create_standard(vm)), memory(mem) {}

  CallParamInfo create(CallDescriptor const & cd) const;
  CallParamInfo operator()(CallDescriptor const & cd) const {
    return create(cd);
  }
};

} // namespace pharos

#endif
/* Local Variables:   */
/* mode: c++          */
/* fill-column:    95 */
/* comment-column: 0  */
/* End:               */

max_stars_repo_name: CyberFlameGO/pharos

max_issues_repo_name: CyberFlameGO/pharos

max_forks_repo_name: CyberFlameGO/pharos


text:
/*
 * This file was quickly hacked together to provide simple graphics output and
 * button controls for simulating a TinyScreen. It's really bad quality but does
 * its job: Providing an environment with visual output for compiling code that
 * runs on the TinyScreen+ in a similar way.
 */

#ifdef SDL2LIB
#include <SDL.h>
#else
#include <GLFW/glfw3.h>
#include <GL/gl.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <TinyScreen.h>
#include <SPI.h>
#include <Wire.h>
#include <stdio.h>
#include <sys/time.h>

#ifdef PNGSAVE
#define png_infopp_NULL (png_infopp)NULL
#define int_p_NULL (int*)NULL
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
#endif

#define TINYSCREEN_WIDTH 96
#define TINYSCREEN_HEIGHT 64

#define SCREEN_TEXTURE_SIZE 128
#define SCREEN_UMAX (96.0f / (float)SCREEN_TEXTURE_SIZE)
#define SCREEN_VMAX (64.0f / (float)SCREEN_TEXTURE_SIZE)

#define SCREEN_Y 64
#define SCREEN_X 96

#define KEYBIT_UP 0x01
#define KEYBIT_DOWN 0x02
#define KEYBIT_LEFT 0x04
#define KEYBIT_RIGHT 0x08
#define KEYBIT_BUTTON1 0x10
#define KEYBIT_BUTTON2 0x20

#define TinyArcadePinX 42
#define TinyArcadePinY 1
#define TinyArcadePin1 45
#define TinyArcadePin2 44

SerialX Serial;
TwoWire Wire;
#ifdef SDL2LIB
unsigned int controls = 0;
#endif

void delay(int msec)
{
    struct timespec val;
    struct timespec rem;
    val.tv_sec = msec / 1000;
    val.tv_nsec = msec % 1000 * 1000000L;
    rem.tv_sec = 0;
    rem.tv_nsec = 0;
    nanosleep(&val, &rem);
}

#include <boost/random/linear_congruential.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>

typedef boost::minstd_rand base_generator_type;
#include <ctime>

static base_generator_type generator(42u);

void randomSeed(int seed)
{
 generator.seed(static_cast<unsigned int>(std::time(0)));
}

int random(int min, int max)
{
 boost::uniform_int<> ran_dist(min, max - 1);
 boost::variate_generator<base_generator_type&, boost::uniform_int<> > ran(generator, ran_dist);
 return ran();
/*    int x = random();
    x = x % (max - min);
    return x + min;*/
}

typedef struct {
    unsigned char screenData[SCREEN_TEXTURE_SIZE*SCREEN_TEXTURE_SIZE * 3];
    unsigned char rawFrameBuffer[TINYSCREEN_WIDTH * TINYSCREEN_HEIGHT * 2];
    unsigned char x,y;
    bool is16bit;
    bool isRecordingTSV;
#ifdef SDL2LIB
    SDL_Window *window;
    SDL_Renderer *mainRenderer;
    SDL_Texture *mainTexture;
    SDL_Surface *mainScreen;
    SDL_Rect *rect;
    int mult;
#else
    GLuint screenTexture;
    GLFWwindow* window;
#endif
    FILE *tsvFP;
} Emulator;

Emulator emulator;

static void writeFrameBufferToTSV() {
    if (emulator.isRecordingTSV && emulator.tsvFP) {
        printf("Writing framebuffer to tsv\n");
        fwrite(emulator.rawFrameBuffer,1,sizeof(emulator.rawFrameBuffer),emulator.tsvFP);
    }
}

static void updateScreen() {
#ifdef SDL2LIB
    SDL_UpdateTexture(emulator.mainTexture, emulator.rect, emulator.mainScreen->pixels, emulator.mainScreen->pitch);
    SDL_RenderClear(emulator.mainRenderer);
    SDL_RenderCopy(emulator.mainRenderer, emulator.mainTexture, NULL, NULL);
    SDL_RenderPresent(emulator.mainRenderer);
#else
    glBindTexture(GL_TEXTURE_2D, emulator.screenTexture);
    glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, SCREEN_TEXTURE_SIZE, SCREEN_TEXTURE_SIZE, 0,
                 GL_RGB, GL_UNSIGNED_BYTE, emulator.screenData);
#endif
}

void TwoWire::requestFrom(byte address, int quantity)
{
#ifdef SDL2LIB
    where = 0;
    int RXY = 511;
    data[0] = RXY >> 2;
    data[1] = RXY >> 2;
    int LX = ((controls & KEYBIT_RIGHT) ? -511 : 0) + ((controls & KEYBIT_LEFT) ? 511 : 0)+511;
    data[2] = LX >> 2;
    int LY = ((controls & KEYBIT_UP) ? 511 : 0) + ((controls & KEYBIT_DOWN) ? -511 : 0)+511;
    data[3] = LY >> 2;
    data[4] = ((((((LX & 3) << 2) | (LY & 3)) << 2) | (RXY & 3)) << 2) | (RXY & 3);
    data[5] = ((controls & KEYBIT_BUTTON1) ? 0 : 4) | ((controls & KEYBIT_BUTTON2) ? 0 : 8);
#else
    GLFWwindow* window = emulator.window;
    where = 0;
    int RXY = 511;
    data[0] = RXY >> 2;
    data[1] = RXY >> 2;
    int LX = (glfwGetKey(window, GLFW_KEY_RIGHT) ? -511 : 0) + (glfwGetKey(window, GLFW_KEY_LEFT) ? 511 : 0)+511;
    data[2] = LX >> 2;
    int LY = (glfwGetKey(window, GLFW_KEY_UP) ? 511 : 0) + (glfwGetKey(window, GLFW_KEY_DOWN) ? -511 : 0)+511;
    data[3] = LY >> 2;
    data[4] = ((((((LX & 3) << 2) | (LY & 3)) << 2) | (RXY & 3)) << 2) | (RXY & 3);
    data[5] = (glfwGetKey(window, GLFW_KEY_G) ? 0 : 4) | (glfwGetKey(window, GLFW_KEY_H) ? 0 : 8);
#endif
}

int digitalRead(int pin) {
#ifndef SDL2LIB
    GLFWwindow* window = emulator.window;
    switch (pin) {
        case 4: case TinyArcadePin1: return (glfwGetKey(window, GLFW_KEY_G) ? 0 : 1);
        case 5: case TinyArcadePin2: return (glfwGetKey(window, GLFW_KEY_H) ? 0 : 1);
        default: return 0;
    }
#endif
    return 0;
}

int analogWrite(int pin, int val) {
 return 0;
}

int analogRead(int pin) {
 #ifndef SDL2LIB
    GLFWwindow* window = emulator.window;
    switch (pin) {
        case 2: case TinyArcadePinX: return (glfwGetKey(window, GLFW_KEY_RIGHT) ? -511 : 0) + (glfwGetKey(window, GLFW_KEY_LEFT) ? 511 : 0)+511;
        case 3: case TinyArcadePinY: return (glfwGetKey(window, GLFW_KEY_UP) ? -511 : 0) + (glfwGetKey(window, GLFW_KEY_DOWN) ? 511 : 0)+511;
        default: return 0;
    }
#endif
    return 0;
}

void setup();

void loop();

#ifndef SDL2LIB
static void drawCircle(float x, float y, float radius, int div) {
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x, y);
    for (int i=0;i<=div;i+=1) {
        float ang = (float)i/(float)div * 3.141593f * 2.0f;
        float px = sinf(ang) * radius;
        float py = cosf(ang) * radius;
        glVertex2f(px+x,py+y);
    }
    glEnd();
}

static void drawG(float x, float y, float size) {
    glBegin(GL_QUADS);
    glVertex2f(x, y + size);
    glVertex2f(x + size, y + size);
    glVertex2f(x + size, y + size - size/8);
    glVertex2f(x, y + size - size/8);
    glVertex2f(x + size - size/8, y + size);
    glVertex2f(x + size, y + size);
    glVertex2f(x + size, y + size - size/4);
    glVertex2f(x + size - size/8, y + size - size/4);
    glVertex2f(x, y);
    glVertex2f(x, y + size);
    glVertex2f(x + size/8, y + size);
    glVertex2f(x + size/8, y);
    glVertex2f(x, y);
    glVertex2f(x + size, y);
    glVertex2f(x + size, y + size/8);
    glVertex2f(x, y + size/8);
    glVertex2f(x + size, y);
    glVertex2f(x + size, y + size/2 + size/16);
    glVertex2f(x + size - size/8, y + size/2 + size/16);
    glVertex2f(x + size - size/8, y);
    glVertex2f(x + size/2, y + size/2 + size/16);
    glVertex2f(x + size/2, y + size/2 - size/16);
    glVertex2f(x + size, y + size/2 - size/16);
    glVertex2f(x + size, y + size/2 + size/16);
    glEnd();
}

static void drawH(float x, float y, float size) {
    glBegin(GL_QUADS);
    glVertex2f(x, y);
    glVertex2f(x, y + size);
    glVertex2f(x + size/8, y + size);
    glVertex2f(x + size/8, y);
    glVertex2f(x + size, y);
    glVertex2f(x + size, y + size);
    glVertex2f(x + size - size/8, y + size);
    glVertex2f(x + size - size/8, y);
    glVertex2f(x + size/8, y + size/2 + size/16);
    glVertex2f(x + size - size/8, y + size/2 + size/16);
    glVertex2f(x + size - size/8, y + size/2 - size/16);
    glVertex2f(x + size/8, y + size/2 - size/16);
    glEnd();
}
#endif

void TinyScreen::startData(void) {
#ifdef SDL2LIB
    SDL_LockSurface(emulator.mainScreen);
#endif
}
void TinyScreen::startCommand(void) {}
void TinyScreen::endTransfer(void) {
#ifdef SDL2LIB
    SDL_UnlockSurface(emulator.mainScreen);
#endif
    updateScreen();
#ifndef SDL2LIB
    GLFWwindow* window = emulator.window;
    float ratio;
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    ratio = width / (float) height;
    glViewport(0, 0, width, height);
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.25f,0.25f,0.5f,0.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
   // glRotatef((float) glfwGetTime() * 50.f, 0.f, 0.f, 1.f);
    /*glBegin(GL_TRIANGLES);
    glColor3f(1.f, 0.f, 0.f);
    glVertex3f(-0.6f, -0.4f, 0.f);
    glColor3f(0.f, 1.f, 0.f);
    glVertex3f(0.6f, -0.4f, 0.f);
    glColor3f(0.f, 0.f, 1.f);
    glVertex3f(0.f, 0.6f, 0.f);
    glEnd();*/
    glDisable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, emulator.screenTexture);
    float scale = ratio > 1.3f ? 2.25f : (2.25f/1.3f) * ratio;

    glScalef(scale,scale,scale);
    glTranslatef(-0.48f, -0.12f,0);

    glColor3f(.0f,.0f,0.f);
    drawCircle(.15f,-.25f,.18f,16);
    glColor3f(.8f,.8f,0.8f);
    float stickX = -(float)analogRead(2) / 1023.f + .5f;
    float stickY = -(float)analogRead(3) / 1023.f + .5f;
    drawCircle(.15f + stickX*.15f,-.25f + stickY * .15f,.10f,12);

    glColor3f(.0f,.0f,0.f);
    drawCircle(.6f,-.325f,.075f,12);
    drawCircle(.8f,-.225f,.075f,12);
    glColor3f(.8f,.2f,0.f);
    float buttonY = !digitalRead(4) ? -.32f : -.3f;
    drawCircle(.6f,buttonY,.075f,12);
    glColor3f(.0f,.0f,0.f);
    drawG(.6f - .0375f, buttonY - .0375f, .075f);
    glColor3f(.8f,.2f,0.f);
    buttonY = !digitalRead(5) ? -.22f : -.2f;
    drawCircle(.8f,buttonY,.075f,12);
    glColor3f(.0f,.0f,0.f);
    drawH(.8f - .0375f, buttonY - .0375f, .075f);
    glColor3f(.8f,.2f,0.f);
    int buttons = getButtons();
    glBegin(GL_QUADS);
    // bottom left
    float buttonX = buttons & 1 ? -.05f : -.075f;
    glVertex2f(buttonX, .2f);
    glVertex2f(buttonX, .1f);
    glVertex2f(.1f, .1f);
    glVertex2f(.1f, .2f);
    glEnd();

    buttonX = (buttons & 2) ? -.05f : -.075f;
    glColor3f(.8f,.2f,0.f);
    glBegin(GL_QUADS);
    glVertex2f(buttonX, .54f);
    glVertex2f(buttonX, .44f);
    glVertex2f(.1f, .44f);
    glVertex2f(.1f, .54f);
    glEnd();
    glColor3f(.8f,.2f,0.f);
    buttonX = (buttons & 4) ? 1.01f : 1.035f;
    glBegin(GL_QUADS);
    glVertex2f(.5f, .54f);
    glVertex2f(.5f, .44f);
    glVertex2f(buttonX, .44f);
    glVertex2f(buttonX, .54f);
    glEnd();
    buttonX = (buttons & 8) ? 1.01f : 1.035f;
    glBegin(GL_QUADS);
    glVertex2f(.5f, .2f);
    glVertex2f(.5f, .1f);
    glVertex2f(buttonX, .1f);
    glVertex2f(buttonX, .2f);
    glEnd();

    float margin = .02f;
    glColor3f(0,0,0);
    glBegin(GL_QUADS);
    glVertex3f(-margin, -margin, 0);
    glVertex3f(-margin, 0.64f+margin, 0);
    glVertex3f(0.96f+margin, 0.64f+margin, 0);
    glVertex3f(0.96f+margin, -margin, 0);
    glEnd();

    glEnable(GL_TEXTURE_2D);
    glColor3f(1,1,1);
    glBegin(GL_QUADS);
    glTexCoord2f(0, SCREEN_VMAX); glVertex3f(0, 0, 0);
    glTexCoord2f(0, 0); glVertex3f(0, 0.64f, 0);
    glTexCoord2f(SCREEN_UMAX, 0); glVertex3f(0.96f, 0.64f, 0);
    glTexCoord2f(SCREEN_UMAX, SCREEN_VMAX); glVertex3f(0.96f, 0, 0);
    glEnd();

    if (emulator.isRecordingTSV) {
        writeFrameBufferToTSV();
    }

    glfwSwapBuffers(window);
    glfwPollEvents();

    if (glfwWindowShouldClose(window)) {
        if (emulator.tsvFP) {
            fclose(emulator.tsvFP);
            emulator.tsvFP = 0;
        }
        glfwDestroyWindow(window);
        glfwTerminate();
        exit(EXIT_SUCCESS);
    }
#endif
}
void TinyScreen::begin(void) {}
void TinyScreen::begin(uint8_t) {}
void TinyScreen::on(void) {}
void TinyScreen::off(void) {}
void TinyScreen::setFlip(uint8_t) {}
void TinyScreen::setMirror(uint8_t) {}
void TinyScreen::setBitDepth(uint8_t is16bit) {
    emulator.is16bit = (is16bit & TSBitDepth16) ? true : false;
}
void TinyScreen::setBrightness(uint8_t) {}
void TinyScreen::setWindowTitle(const char *title)
{
#ifndef SDL2LIB
    glfwSetWindowTitle(emulator.window, title);
#endif
}
//void TinyScreen::writeRemap(void) {}
//accelerated drawing commands
void TinyScreen::drawPixel(uint8_t, uint8_t, uint16_t) {}
void TinyScreen::drawLine(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) {}
void TinyScreen::drawLine(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) {}
void TinyScreen::drawRect(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) {}
void TinyScreen::drawRect(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) {}
void TinyScreen::clearWindow(uint8_t, uint8_t, uint8_t, uint8_t) {}
//basic graphics commands
void TinyScreen::writePixel(uint16_t) {
}
void TinyScreen::writeBuffer(uint8_t *rgb, int num) {
    if (num > TINYSCREEN_WIDTH * (emulator.is16bit ? 2 : 1)) {
        printf("line too long: %d\n",num);
    }
    //uint16_t *rgb565_16 = (uint16_t*)&rgb565[0];
    int idx = emulator.x + emulator.y * SCREEN_TEXTURE_SIZE;
    int bufferIdx = (emulator.x + emulator.y * TINYSCREEN_WIDTH) * 2;
#ifdef SDL2LIB
    Uint8 *pixels = (Uint8*)emulator.mainScreen->pixels;
#endif
    uint8_t *rgb565 = rgb;
    for (int i=0;i<num; i+=1) {
        uint8_t r,g,b;
        if (emulator.is16bit) {
            uint16_t word = 0;
            word = rgb565[i]<<8 | rgb565[i+1];
            r = word & 31;
            g = (word >> 5) & 63;
            b = word >> 11;

            r = (r << 3 | r >> 2);
            g = (g << 2 | g >> 4);
            b = (b << 3 | b >> 2);
            emulator.rawFrameBuffer[bufferIdx++ % sizeof(emulator.rawFrameBuffer)] = rgb565[i];
            emulator.rawFrameBuffer[bufferIdx++ % sizeof(emulator.rawFrameBuffer)] = rgb565[i+1];
            i+=1;
        } else {
            uint8_t rgb233 = rgb[i];
            r = rgb233 & 3;
            r = (r << 6) | (r << 4) | (r << 2) | r;
            g = (rgb233 >> 2) & 7;
            g = g << 5 | g << 2 | g >> 1;
            b = rgb233 >> 5 & 7;
            b = b << 5 | b << 2 | b >> 1;
            uint16_t word = 0;
            word |= (((uint16_t)r) & 0x00F8) >> 3;
            word |= (((uint16_t)g) & 0x00FC) << 2;
            word |= (((uint16_t)b) & 0x00F8) << 8;
            emulator.rawFrameBuffer[bufferIdx++ % sizeof(emulator.rawFrameBuffer)] = word >> 8;
            emulator.rawFrameBuffer[bufferIdx++ % sizeof(emulator.rawFrameBuffer)] = word & 0x00FF;
        }
        // my gif screencsat program doesn't like 00ff00
        if (r == 0 && g >= 250 && b == 0) g = 250;
        emulator.screenData[idx*3+0] = r;
        emulator.screenData[idx*3+1] = g;
        emulator.screenData[idx*3+2] = b;
#ifdef SDL2LIB
        int startIdx = ((emulator.x + ((i / (emulator.is16bit ? 2 : 1)) % TINYSCREEN_WIDTH) + emulator.y * TINYSCREEN_WIDTH * emulator.mult) * emulator.mult) * 4;
        for (int yMod = 0; yMod < emulator.mult; yMod++)
        {
         for (int xMod = 0; xMod < emulator.mult; xMod++)
         {
          pixels[startIdx + xMod * 4] = b;
          pixels[startIdx + xMod * 4 + 1] = g;
          pixels[startIdx + xMod * 4 + 2] = r;
          pixels[startIdx + xMod * 4 + 3] = 255;
         }
         startIdx += TINYSCREEN_WIDTH * emulator.mult * 4;
        }
#endif
        if (idx % SCREEN_TEXTURE_SIZE == TINYSCREEN_WIDTH-1) {
            idx = emulator.x + (++emulator.y) * SCREEN_TEXTURE_SIZE;//SCREEN_TEXTURE_SIZE - TINYSCREEN_WIDTH + 1;
        } else {
            idx += 1;
        }
    }
    //emulator.y+=1;
}
/*void TinyScreen::setX(uint8_t, uint8_t);
void TinyScreen::setY(uint8_t, uint8_t);*/
void TinyScreen::goTo(uint8_t x, uint8_t y) {
    emulator.x = x;
    emulator.y = y;
}

//I2C GPIO related
uint8_t TinyScreen::getButtons(void) {
#ifdef SDL2LIB
    return 0;
#else
    GLFWwindow* window = emulator.window;
    int tr = (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS);
    int br = (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS);
    int tl = (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS);
    int bl = (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS);
    return tl << 1 | bl | tr << 2 | br << 3;
#endif
}
/*void TinyScreen::writeGPIO(uint8_t, uint8_t);*/
//font
void TinyScreen::setFont(const FONT_INFO&) {}
void TinyScreen::setCursor(uint8_t, uint8_t) {}
void TinyScreen::fontColor(uint8_t, uint8_t) {}
size_t TinyScreen::write(uint8_t) { return 0; }

static void init() {

    for (int x=0;x<TINYSCREEN_WIDTH;x+=1) {
        for (int y=0;y<TINYSCREEN_HEIGHT;y+=1) {
            int idx = (x + y * SCREEN_TEXTURE_SIZE) * 3;
            emulator.screenData[idx] = x * 255 / TINYSCREEN_WIDTH;
            emulator.screenData[idx + 1] = y * 255 / TINYSCREEN_HEIGHT;
        }
    }

#ifndef SDL2LIB
    glGenTextures(1, &emulator.screenTexture);
#endif
    updateScreen();
#ifndef SDL2LIB
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#endif
}

unsigned long millis() {
    struct timeval ret;
    gettimeofday(&ret, NULL);
    return ret.tv_sec * 1000 + ret.tv_usec / 1000;
}

static void error_callback(int /*error*/, const char* description)
{
    fputs(description, stderr);
}

#ifndef SDL2LIB
static void key_callback(GLFWwindow* window, int key, int /*scancode*/, int action, int /*mods*/)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key == GLFW_KEY_R && action == GLFW_RELEASE) {
        emulator.isRecordingTSV = !emulator.isRecordingTSV;
        if (emulator.isRecordingTSV) {
            char filename[128];
            sprintf(filename,"rec-%s.tsv","test");
            printf("Starting recording to %s\n", filename);
            emulator.tsvFP = fopen(filename, "wb");
        } else {
            printf("Recording finished\n");
            if (emulator.tsvFP) {
                fclose(emulator.tsvFP);
                emulator.tsvFP = 0;
            }
        }
    }
#ifdef PNGSAVE
    if (key == GLFW_KEY_P && action == GLFW_RELEASE) {
        boost::gil::rgb8_image_t img(TINYSCREEN_WIDTH, TINYSCREEN_HEIGHT);
        for (int y = 0; y < TINYSCREEN_HEIGHT; y++)
        {
            for (int x = 0; x < TINYSCREEN_WIDTH; x++)
            {
                int indx = (x + y * SCREEN_TEXTURE_SIZE) * 3;
                boost::gil::rgb8_pixel_t p(emulator.screenData[indx], emulator.screenData[indx + 1], emulator.screenData[indx + 2]);
                *(view(img).at(x, y)) = p;
            }
        }
        boost::gil::png_write_view("screenshot.png", const_view(img));
    }
#endif
}
#endif

int main(int argc, char *argv[])
{
    bool full = false;
    bool softRender = false;
    int opt;
    static struct option long_options[] =
    {
        {"multiplier", 1, 0, 'u'},
        {0, 0, 0, 0}
    };
#ifdef SDL2LIB
    int screenX, screenY;
    emulator.mult = 4;
    emulator.rect = NULL;
    screenX = SCREEN_X * emulator.mult;
    screenY = SCREEN_Y * emulator.mult;
#endif
    while ((opt = getopt_long(argc,argv,"pu:fw", long_options, NULL)) != -1)
    {
        switch (opt)
        {
#ifdef SDL2LIB
            case 'p':
                emulator.rect = new SDL_Rect;
                emulator.rect->x = 48;
                emulator.rect->y = 8;
                emulator.rect->w = 384;
                emulator.rect->h = 256;
                emulator.mult = 4;
                softRender = true;
                screenX = 480;
                screenY = 272;
                full = true;
                break;
            case 'u':
                if (optarg)
                {
                    emulator.mult = atol(optarg);
                    screenX = SCREEN_X * emulator.mult;
                    screenY = SCREEN_Y * emulator.mult;
                }
                break;
#endif
            case 'f':
                full = true;
                break;
            case 'w':
                softRender = true;
                break;
            default:
                break;
        }
    }
#ifdef SDL2LIB
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0)
    {
        printf("Failed - SDL_Init\n");
        exit(0);
    }
    SDL_Window* window;
    window = SDL_CreateWindow("TinyScreen Simulator",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              screenX,
                              screenY,
                              (full ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
    emulator.window = window;
    if (window == NULL)
    {
        printf("Failed - SDL_CreateWindow\n");
        exit(0);
    }

    emulator.mainRenderer = SDL_CreateRenderer(emulator.window, -1, (softRender ? SDL_RENDERER_SOFTWARE : 0));
    if (emulator.mainRenderer == NULL)
    {
        printf("Failed - SDL_CreateRenderer\n");
        exit(0);
    }
    emulator.mainTexture = SDL_CreateTexture(emulator.mainRenderer,
                                SDL_PIXELFORMAT_ARGB8888,
                                SDL_TEXTUREACCESS_STREAMING,
                                screenX,
                                screenY);
    if (emulator.mainTexture == NULL)
    {
        printf("Failed - SDL_CreateTexture\n");
        exit(0);
    }
    emulator.mainScreen = SDL_CreateRGBSurface(0, SCREEN_X * emulator.mult, SCREEN_Y * emulator.mult, 32,
                                           0x00FF0000,
                                           0x0000FF00,
                                           0x000000FF,
                                           0xFF000000);
    if (emulator.mainScreen == NULL)
    {
        printf("Failed - SDL_CreateRGBSurface\n");
        exit(0);
    }
#else
    GLFWwindow* window;
    glfwSetErrorCallback(error_callback);
    if (!glfwInit())
        exit(EXIT_FAILURE);
    window = glfwCreateWindow(420, 420, "TinyScreen Simulator", NULL, NULL);
    emulator.window = window;
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    glfwSetKeyCallback(window, key_callback);
#endif
    init();
    setup();
    int frame = 0;
#ifdef SDL2LIB
    while (true)
#else
    while (!glfwWindowShouldClose(window))
#endif
    {
        loop();
        frame++;
#ifdef SDL2LIB
        SDL_Event sdlevent;
        while (SDL_PollEvent(&sdlevent))
        {
            if (sdlevent.type == SDL_QUIT)
            {
                SDL_Quit();
                exit(EXIT_SUCCESS);
            }
            else if (sdlevent.type == SDL_KEYDOWN)
            {
                if ((sdlevent.key.keysym.sym == SDLK_UP) || (sdlevent.key.keysym.sym == SDLK_KP_8))
                {
                    controls |= KEYBIT_UP;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_LEFT) || (sdlevent.key.keysym.sym == SDLK_KP_4))
                {
                    controls |= KEYBIT_LEFT;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_DOWN) || (sdlevent.key.keysym.sym == SDLK_KP_2))
                {
                    controls |= KEYBIT_DOWN;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_RIGHT) || (sdlevent.key.keysym.sym == SDLK_KP_6))
                {
                    controls |= KEYBIT_RIGHT;
                }
                else if (sdlevent.key.keysym.sym == SDLK_g)
                {
                    controls |= KEYBIT_BUTTON1;
                }
                else if (sdlevent.key.keysym.sym == SDLK_h)
                {
                    controls |= KEYBIT_BUTTON2;
                }
                else if (sdlevent.key.keysym.sym == SDLK_ESCAPE)
                {
                    SDL_Quit();
                    exit(EXIT_SUCCESS);
                }
            }
            else if (sdlevent.type == SDL_KEYUP)
            {
                if ((sdlevent.key.keysym.sym == SDLK_UP) || (sdlevent.key.keysym.sym == SDLK_KP_8))
                {
                    controls &= ~KEYBIT_UP;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_LEFT) || (sdlevent.key.keysym.sym == SDLK_KP_4))
                {
                    controls &= ~KEYBIT_LEFT;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_DOWN) || (sdlevent.key.keysym.sym == SDLK_KP_2))
                {
                    controls &= ~KEYBIT_DOWN;
                }
                else if ((sdlevent.key.keysym.sym == SDLK_RIGHT) || (sdlevent.key.keysym.sym == SDLK_KP_6))
                {
                    controls &= ~KEYBIT_RIGHT;
                }
                else if (sdlevent.key.keysym.sym == SDLK_g)
                {
                    controls &= ~KEYBIT_BUTTON1;
                }
                else if (sdlevent.key.keysym.sym == SDLK_h)
                {
                    controls &= ~KEYBIT_BUTTON2;
                }
            }
        }
#endif
/*        if ((emulator.isRecordingTSV) && ((emulator.is16bit) || (frame % 2))) {
            writeFrameBufferToTSV();
        }*/
    }
    if (emulator.tsvFP) {
        fclose(emulator.tsvFP);
        emulator.tsvFP = 0;
    }
#ifdef SDL2LIB
    SDL_Quit();
#else
    glfwDestroyWindow(window);
    glfwTerminate();
#endif
    exit(EXIT_SUCCESS);
}

max_stars_repo_name: dulsi/tinyscreensim

max_issues_repo_name: dulsi/tinyscreensim

max_forks_repo_name: dulsi/tinyscreensim


text:
/** Copyright : Arthur Bit-Monnot (2013)  arthur.bit-monnot@laas.fr

This software is a computer program whose purpose is to [describe
functionalities and technical features of your software].

This software is governed by the CeCILL-B license under French law and
abiding by the rules of distribution of free software.  You can  use,
modify and/ or redistribute the software under the terms of the CeCILL-B
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability.

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL-B license and that you accept its terms.
*/

#include <boost/foreach.hpp>

#include "node_filter_utils.h"

#include "nodes_filter.h"
#include "AlgoTypedefs.h"

using namespace RLC;

NodeSet * isochrone ( const RLC::AbstractGraph * g, const int center, const int max_time )
{
    typedef AspectMaxCostPruning<AspectMinCost<DRegLC> > Dij;
    NodeSet * ns = new NodeSet( g->transport->num_vertices() );

    Dij::ParamType p(
        DRegLCParams( g, 0, 1 ),
        AspectMaxCostPruningParams( max_time )
    );

    Dij dij( p );
    BOOST_FOREACH( int state, g->dfa_start_states() ) {
        dij.add_source_node( RLC::Vertice(center, state), 0, 0 );
    }
    while( !dij.finished() ) {
        RLC::Label lab = dij.treat_next();
        ns->addNode( lab.node.first );
    }

    return ns;
}

VisualResult show_isochrone ( const RLC::AbstractGraph * g, const int center, const int max_time )
{
    NodeSet * ns = isochrone( g, center, max_time );
    return ns->visualization();
}

BBNodeFilter* rectangle_containing ( const Transport::Graph* trans, const int node1, const int node2, const float margin )
{

    float lat1 = trans->latitude( node1 );
    float lon1 = trans->longitude( node1 );
    float lat2 = trans->latitude( node2 );
    float lon2 = trans->longitude( node2 );

    float max_lat, min_lat, max_lon, min_lon;
    if(lat1 > lat2) {
        max_lat = lat1 + margin;
        min_lat = lat2 - margin;
    } else {
        max_lat = lat2 + margin;
        min_lat = lat1 - margin;
    }

    if(lon1 > lon2) {
        max_lon = lon1 + margin;
        min_lon = lon2 - margin;
    } else {
        max_lon = lon2 + margin;
        min_lon = lon1 - margin;
    }

    return new BBNodeFilter(trans, max_lon, min_lon, max_lat, min_lat);
}

BBNodeFilter* rectangle_containing ( const Transport::Graph* trans, const std::vector<int> nodes, const float margin )
{
    float max_lat = std::numeric_limits< float >::min();
    float min_lat = std::numeric_limits< float >::max();
    float max_lon = std::numeric_limits< float >::min();
    float min_lon = std::numeric_limits< float >::max();

    BOOST_FOREACH(int node, nodes) {
        float lat = trans->latitude( node );
        float lon = trans->longitude( node );

        if( lat > max_lat ) {
            max_lat = lat;
        }
        if( lat < min_lat ) {
            min_lat = lat;
        }
        if( lon > max_lon ) {
            max_lon = lon;
        }
        if( lon < min_lon ) {
            min_lon = lon;
        }
    }
    return new BBNodeFilter(trans, max_lon + margin, min_lon - margin, max_lat + margin, min_lat - margin);
}

BBNodeFilter * bordeaux_bb( const Transport::Graph * trans )
{
    return new BBNodeFilter(trans, -0.455148, -0.860322, 45.0317, 44.7266);
}

BBNodeFilter * toulouse_bb( const Transport::Graph * trans )
{
    return new BBNodeFilter(trans, 1.68514, 1.2078, 43.7794, 43.3935);
}

max_stars_repo_name: arthur-bit-monnot/mumoro

max_issues_repo_name: arthur-bit-monnot/mumoro

max_forks_repo_name: arthur-bit-monnot/mumoro


text: #include <boost/thread/csbl/functional.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include <QtTools/StandardItemModel.hqt>
#include <iterator>
#include <algorithm>
#include <ext/algorithm.hpp>

#include <boost/iterator/iterator_facade.hpp>
#include <boost/assert.hpp>

namespace QtTools
{
	namespace
	{
		/// ссылка на StandardItemModel Item по номеру строки,
		/// умеет обменивать строки, фактически адаптер для стандартных алгоритмов
		struct RowItemReference
		{
			StandardItemModel * owner;
			int row;
		};

		void swap(RowItemReference op1, RowItemReference op2)
		{
			BOOST_ASSERT(op1.owner == op2.owner);
			op1.owner->SwapRows(op1.row, op2.row);
		}

		struct RowIterator :
			public boost::iterator_facade<
				RowIterator,
				RowItemReference,
				std::random_access_iterator_tag,
				RowItemReference
			>
		{
			StandardItemModel * owner;
			int row;

			RowIterator(StandardItemModel * owner, int row) : owner(owner), row(row) {}

			RowItemReference dereference() const { return RowItemReference{ owner, row }; }
			bool equal(const RowIterator & other) const { return row == other.row; }
			void increment() { ++row; }
			void decrement() { --row; }
			void advance(std::ptrdiff_t step) { row += step; }
			std::ptrdiff_t distance_to(const RowIterator & other) const { return other.row - row; }
		};
	}

	void StandardItemModel::SwapRows(int row1, int row2)
	{
		for (int i = 0, n = columnCount(); i < n; ++i)
		{
			auto idx1 = index(row1, i);
			auto idx2 = index(row2, i);
			auto itd1 = itemData(idx1);
			auto itd2 = itemData(idx2);

			setItemData(idx1, itd2);
			setItemData(idx2, itd1);
		}
	}

	bool StandardItemModel::moveRows(const QModelIndex & sourceParent, int sourceRow, int count,
									 const QModelIndex & destinationParent, int destinationChild)
	{
		if (sourceParent != destinationParent)
			return false;

		bool allowed = beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
		if (!allowed) return false;

		RowIterator first {this, sourceRow};
		RowIterator last {this, sourceRow + count};
		RowIterator new_pos {this, destinationChild};
		ext::slide(first, last, new_pos);

		endMoveRows();
		return true;
	}

	/*
	 *just to remember reverse_iterator usage
	bool StandardItemModel::moveRows(const QModelIndex & sourceParent, int sourceRow, int count,
									 const QModelIndex & destinationParent, int destinationChild)
	{
		if (sourceParent != destinationParent)
			return false;

		bool allowed = beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
		if (!allowed) return false;

		// используем std::rotate, нужно перевести координаты в соотвествующие итераторы
		if (destinationChild < sourceRow) // move up: right rotate
		{
			// right rotate делается реверсными итераторами
			typedef std::reverse_iterator<RowIterator> Iterator;
			// let src = 5, count = 2, dest = 2
			//  rend should point at 1
			//  rbeg should point at 6
			//  rnewBeg should point at 4
			// ------------------------------------------
			//        rend      rnewBeg  rbeg
			//  it(dst)|    it(src)|       | it(src+n)
			//        / \         / \     / \
			// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
			// ------------------------------------------
			Iterator rbeg{RowIterator(this, sourceRow + count)};
			Iterator rnewBeg{RowIterator(this, sourceRow)};
			Iterator rend{RowIterator(this, destinationChild)};

			std::rotate(rbeg, rnewBeg, rend);
		}
		else // move down: left rotate
		{
			// left rotate делается прямыми итераторами
			RowIterator beg{this, sourceRow};
			RowIterator newBeg{this, sourceRow + count}; // элемент который должен стать первым, это первый элемент, который не перемещаем
			RowIterator end{this, destinationChild};

			std::rotate(beg, newBeg, end);
		}

		endMoveRows();
		return true;
	}
	}*/
}

max_stars_repo_name: dmlys/QtTools

max_issues_repo_name: dmlys/QtTools

max_forks_repo_name: dmlys/QtTools


text:
/*

   Copyright [2008] [Trevor Hogan]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/

*/

#include "ghost.h"
#include "util.h"
#include "config.h"
#include "ghostdb.h"
#include "ghostdbmysql.h"

#include <signal.h>

#ifdef WIN32
 #include <winsock.h>
#endif

#include <mysql/mysql.h>
#include <boost/thread/thread.hpp>

//
// CGHostDBMySQL
//

CGHostDBMySQL :: CGHostDBMySQL( CConfig *CFG ) : CGHostDB( CFG )
{
	m_Server = CFG->GetString( "db_mysql_server", string( ) );
	m_Database = CFG->GetString( "db_mysql_database", "ghost" );
	m_User = CFG->GetString( "db_mysql_user", string( ) );
	m_Password = CFG->GetString( "db_mysql_password", string( ) );
	m_Port = CFG->GetInt( "db_mysql_port", 0 );
	m_BotID = CFG->GetInt( "db_mysql_botid", 0 );
	m_NumConnections = 1;
	m_OutstandingCallables = 0;

	mysql_library_init( 0, NULL, NULL );

	// create the first connection

	CONSOLE_Print( "[MYSQL] connecting to database server" );
	MYSQL *Connection = NULL;

	if( !( Connection = mysql_init( NULL ) ) )
	{
		CONSOLE_Print( string( "[MYSQL] " ) + mysql_error( Connection ) );
		m_HasError = true;
		m_Error = "error initializing MySQL connection";
		return;
	}

	my_bool Reconnect = true;
	mysql_options( Connection, MYSQL_OPT_RECONNECT, &Reconnect );

	if( !( mysql_real_connect( Connection, m_Server.c_str( ), m_User.c_str( ), m_Password.c_str( ), m_Database.c_str( ), m_Port, NULL, 0 ) ) )
	{
		CONSOLE_Print( string( "[MYSQL] " ) + mysql_error( Connection ) );
		m_HasError = true;
		m_Error = "error connecting to MySQL server";
		return;
	}

	m_IdleConnections.push( Connection );
}

CGHostDBMySQL :: ~CGHostDBMySQL( )
{
	CONSOLE_Print( "[MYSQL] closing " + UTIL_ToString( m_IdleConnections.size( ) ) + "/" + UTIL_ToString( m_NumConnections ) + " idle MySQL connections" );

	while( !m_IdleConnections.empty( ) )
	{
		mysql_close( (MYSQL *)m_IdleConnections.front( ) );
		m_IdleConnections.pop( );
	}

	if( m_OutstandingCallables > 0 )
		CONSOLE_Print( "[MYSQL] " + UTIL_ToString( m_OutstandingCallables ) + " outstanding callables were never recovered" );

	mysql_library_end( );
}

string CGHostDBMySQL :: GetStatus( )
{
	return "DB STATUS --- Connections: " + UTIL_ToString( m_IdleConnections.size( ) ) + "/" + UTIL_ToString( m_NumConnections ) + " idle. Outstanding callables: " + UTIL_ToString( m_OutstandingCallables ) + ".";
}

void CGHostDBMySQL :: RecoverCallable( CBaseCallable *callable )
{
	CMySQLCallable *MySQLCallable = dynamic_cast<CMySQLCallable *>( callable );

	if( MySQLCallable )
	{
		if( m_IdleConnections.size( ) > 30 )
		{
			mysql_close( (MYSQL *)MySQLCallable->GetConnection( ) );
			m_NumConnections--;
		}
		else
			m_IdleConnections.push( MySQLCallable->GetConnection( ) );

		if( m_OutstandingCallables == 0 )
			CONSOLE_Print( "[MYSQL] recovered a mysql callable with zero outstanding" );
		else
			m_OutstandingCallables--;

		if( !MySQLCallable->GetError( ).empty( ) )
			CONSOLE_Print( "[MYSQL] error --- " + MySQLCallable->GetError( ) );
	}
	else
		CONSOLE_Print( "[MYSQL] tried to recover a non-mysql callable" );
}

void CGHostDBMySQL :: CreateThread( CBaseCallable *callable )
{
	try
	{
		boost :: thread Thread( boost :: ref( *callable ) );
	}
	catch( boost :: thread_resource_error tre )
	{
		CONSOLE_Print( "[MYSQL] error spawning thread on attempt #1 [" + string( tre.what( ) ) + "], pausing execution and trying again in 50ms" );
		MILLISLEEP( 50 );

		try
		{
			boost :: thread Thread( boost :: ref( *callable ) );
		}
		catch( boost :: thread_resource_error tre2 )
		{
			CONSOLE_Print( "[MYSQL] error spawning thread on attempt #2 [" + string( tre2.what( ) ) + "], giving up" );
			callable->SetReady( true );
		}
	}
}

CCallableAdminCount *CGHostDBMySQL :: ThreadedAdminCount( string server )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableAdminCount *Callable = new CMySQLCallableAdminCount( server, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableAdminCheck *CGHostDBMySQL :: ThreadedAdminCheck( string server, string user )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableAdminCheck *Callable = new CMySQLCallableAdminCheck( server, user, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableAdminAdd *CGHostDBMySQL :: ThreadedAdminAdd( string server, string user )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableAdminAdd *Callable = new CMySQLCallableAdminAdd( server, user, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableAdminRemove *CGHostDBMySQL :: ThreadedAdminRemove( string server, string user )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableAdminRemove *Callable = new CMySQLCallableAdminRemove( server, user, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableAdminList *CGHostDBMySQL :: ThreadedAdminList( string server )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableAdminList *Callable = new CMySQLCallableAdminList( server, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanCount *CGHostDBMySQL :: ThreadedBanCount( string server )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanCount *Callable = new CMySQLCallableBanCount( server, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanCheck *CGHostDBMySQL :: ThreadedBanCheck( string server, string user, string ip )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanCheck *Callable = new CMySQLCallableBanCheck( server, user, ip, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanAdd *CGHostDBMySQL :: ThreadedBanAdd( string server, string user, string ip, string gamename, string admin, string reason )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanAdd *Callable = new CMySQLCallableBanAdd( server, user, ip, gamename, admin, reason, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanRemove *CGHostDBMySQL :: ThreadedBanRemove( string server, string user )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanRemove *Callable = new CMySQLCallableBanRemove( server, user, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanRemove *CGHostDBMySQL :: ThreadedBanRemove( string user )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanRemove *Callable = new CMySQLCallableBanRemove( string( ), user, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableBanList *CGHostDBMySQL :: ThreadedBanList( )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableBanList *Callable = new CMySQLCallableBanList( Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableGameAdd *CGHostDBMySQL :: ThreadedGameAdd( string server, string map, string gamename, string ownername, uint32_t duration, uint32_t gamestate, string creatorname, string creatorserver )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableGameAdd *Callable = new CMySQLCallableGameAdd( server, map, gamename, ownername, duration, gamestate, creatorname, creatorserver, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableGamePlayerAdd *CGHostDBMySQL :: ThreadedGamePlayerAdd( uint32_t gameid, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t reserved, uint32_t loadingtime, uint32_t left, string leftreason, uint32_t team, uint32_t colour )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableGamePlayerAdd *Callable = new CMySQLCallableGamePlayerAdd( gameid, name, ip, spoofed, spoofedrealm, reserved, loadingtime, left, leftreason, team, colour, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableGamePlayerSummaryCheck *CGHostDBMySQL :: ThreadedGamePlayerSummaryCheck( string name )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableGamePlayerSummaryCheck *Callable = new CMySQLCallableGamePlayerSummaryCheck( name, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableDotAGameAdd *CGHostDBMySQL :: ThreadedDotAGameAdd( uint32_t gameid, uint32_t winner, uint32_t min, uint32_t sec )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableDotAGameAdd *Callable = new CMySQLCallableDotAGameAdd( gameid, winner, min, sec, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableDotAPlayerAdd *CGHostDBMySQL :: ThreadedDotAPlayerAdd( uint32_t gameid, uint32_t colour, uint32_t kills, uint32_t deaths, uint32_t creepkills, uint32_t creepdenies, uint32_t assists, uint32_t gold, uint32_t neutralkills, string item1, string item2, string item3, string item4, string item5, string item6, string hero, uint32_t newcolour, uint32_t towerkills, uint32_t raxkills, uint32_t courierkills )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableDotAPlayerAdd *Callable = new CMySQLCallableDotAPlayerAdd( gameid, colour, kills, deaths, creepkills, creepdenies, assists, gold, neutralkills, item1, item2, item3, item4, item5, item6, hero, newcolour, towerkills, raxkills, courierkills, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableDotAPlayerSummaryCheck *CGHostDBMySQL :: ThreadedDotAPlayerSummaryCheck( string name )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableDotAPlayerSummaryCheck *Callable = new CMySQLCallableDotAPlayerSummaryCheck( name, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableDownloadAdd *CGHostDBMySQL :: ThreadedDownloadAdd( string map, uint32_t mapsize, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t downloadtime )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableDownloadAdd *Callable = new CMySQLCallableDownloadAdd( map, mapsize, name, ip, spoofed, spoofedrealm, downloadtime, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableScoreCheck *CGHostDBMySQL :: ThreadedScoreCheck( string category, string name, string server )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableScoreCheck *Callable = new CMySQLCallableScoreCheck( category, name, server, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableW3MMDPlayerAdd *CGHostDBMySQL :: ThreadedW3MMDPlayerAdd( string category, uint32_t gameid, uint32_t pid, string name, string flag, uint32_t leaver, uint32_t practicing )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableW3MMDPlayerAdd *Callable = new CMySQLCallableW3MMDPlayerAdd( category, gameid, pid, name, flag, leaver, practicing, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableW3MMDVarAdd *CGHostDBMySQL :: ThreadedW3MMDVarAdd( uint32_t gameid, map<VarP,int32_t> var_ints )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableW3MMDVarAdd *Callable = new CMySQLCallableW3MMDVarAdd( gameid, var_ints, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableW3MMDVarAdd *CGHostDBMySQL :: ThreadedW3MMDVarAdd( uint32_t gameid, map<VarP,double> var_reals )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableW3MMDVarAdd *Callable = new CMySQLCallableW3MMDVarAdd( gameid, var_reals, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

CCallableW3MMDVarAdd *CGHostDBMySQL :: ThreadedW3MMDVarAdd( uint32_t gameid, map<VarP,string> var_strings )
{
	void *Connection = GetIdleConnection( );

	if( !Connection )
		m_NumConnections++;

	CCallableW3MMDVarAdd *Callable = new CMySQLCallableW3MMDVarAdd( gameid, var_strings, Connection, m_BotID, m_Server, m_Database, m_User, m_Password, m_Port );
	CreateThread( Callable );
	m_OutstandingCallables++;
	return Callable;
}

void *CGHostDBMySQL :: GetIdleConnection( )
{
	void *Connection = NULL;

	if( !m_IdleConnections.empty( ) )
	{
		Connection = m_IdleConnections.front( );
		m_IdleConnections.pop( );
	}

	return Connection;
}

//
// unprototyped global helper functions
//

string MySQLEscapeString( void *conn, string str )
{
	char *to = new char[str.size( ) * 2 + 1];
	unsigned long size = mysql_real_escape_string( (MYSQL *)conn, to, str.c_str( ), str.size( ) );
	string result( to, size );
	delete [] to;
	return result;
}

vector<string> MySQLFetchRow( MYSQL_RES *res )
{
	vector<string> Result;

	MYSQL_ROW Row = mysql_fetch_row( res );

	if( Row )
	{
		unsigned long *Lengths;
		Lengths = mysql_fetch_lengths( res );

		for( unsigned int i = 0; i < mysql_num_fields( res ); i++ )
		{
			if( Row[i] )
				Result.push_back( string( Row[i], Lengths[i] ) );
			else
				Result.push_back( string( ) );
		}
	}

	return Result;
}

//
// global helper functions
//

uint32_t MySQLAdminCount( void *conn, string *error, uint32_t botid, string server )
{
	string EscServer = MySQLEscapeString( conn, server );
	uint32_t Count = 0;
	string Query = "SELECT COUNT(*) FROM admins WHERE server='" + EscServer + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 1 )
				Count = UTIL_ToUInt32( Row[0] );
			else
				*error = "error counting admins [" + server + "] - row doesn't have 1 column";

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return Count;
}

bool MySQLAdminCheck( void *conn, string *error, uint32_t botid, string server, string user )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	bool IsAdmin = false;
	string Query = "SELECT * FROM admins WHERE server='" + EscServer + "' AND name='" + EscUser + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( !Row.empty( ) )
				IsAdmin = true;

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return IsAdmin;
}

bool MySQLAdminAdd( void *conn, string *error, uint32_t botid, string server, string user )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	bool Success = false;
	string Query = "INSERT INTO admins ( botid, server, name ) VALUES ( " + UTIL_ToString( botid ) + ", '" + EscServer + "', '" + EscUser + "' )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

bool MySQLAdminRemove( void *conn, string *error, uint32_t botid, string server, string user )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	bool Success = false;
	string Query = "DELETE FROM admins WHERE server='" + EscServer + "' AND name='" + EscUser + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

vector<string> MySQLAdminList( void *conn, string *error, uint32_t botid, string server )
{
	string EscServer = MySQLEscapeString( conn, server );
	vector<string> AdminList;
	string Query = "SELECT name FROM admins WHERE server='" + EscServer + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			while( !Row.empty( ) )
			{
				AdminList.push_back( Row[0] );
				Row = MySQLFetchRow( Result );
			}

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return AdminList;
}

uint32_t MySQLBanCount( void *conn, string *error, uint32_t botid, string server )
{
	string EscServer = MySQLEscapeString( conn, server );
	uint32_t Count = 0;
	string Query = "SELECT COUNT(*) FROM bans WHERE server='" + EscServer + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 1 )
				Count = UTIL_ToUInt32( Row[0] );
			else
				*error = "error counting bans [" + server + "] - row doesn't have 1 column";

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return Count;
}

CDBBan *MySQLBanCheck( void *conn, string *error, uint32_t botid, string server, string user, string ip )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	string EscIP = MySQLEscapeString( conn, ip );
	CDBBan *Ban = NULL;
	string Query;

	if( ip.empty( ) )
		Query = "SELECT name, ip, DATE(date), gamename, admin, reason FROM bans WHERE server='" + EscServer + "' AND name='" + EscUser + "'";
	else
		Query = "SELECT name, ip, DATE(date), gamename, admin, reason FROM bans WHERE (server='" + EscServer + "' AND name='" + EscUser + "') OR ip='" + EscIP + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 6 )
				Ban = new CDBBan( server, Row[0], Row[1], Row[2], Row[3], Row[4], Row[5] );
			/* else
				*error = "error checking ban [" + server + " : " + user + "] - row doesn't have 6 columns"; */

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return Ban;
}

bool MySQLBanAdd( void *conn, string *error, uint32_t botid, string server, string user, string ip, string gamename, string admin, string reason )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	string EscIP = MySQLEscapeString( conn, ip );
	string EscGameName = MySQLEscapeString( conn, gamename );
	string EscAdmin = MySQLEscapeString( conn, admin );
	string EscReason = MySQLEscapeString( conn, reason );
	bool Success = false;
	string Query = "INSERT INTO bans ( botid, server, name, ip, date, gamename, admin, reason ) VALUES ( " + UTIL_ToString( botid ) + ", '" + EscServer + "', '" + EscUser + "', '" + EscIP + "', CURDATE( ), '" + EscGameName + "', '" + EscAdmin + "', '" + EscReason + "' )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

bool MySQLBanRemove( void *conn, string *error, uint32_t botid, string server, string user )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscServer = MySQLEscapeString( conn, server );
	string EscUser = MySQLEscapeString( conn, user );
	bool Success = false;
	string Query = "DELETE FROM bans WHERE server='" + EscServer + "' AND name='" + EscUser + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

bool MySQLBanRemove( void *conn, string *error, uint32_t botid, string user )
{
	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
	string EscUser = MySQLEscapeString( conn, user );
	bool Success = false;
	string Query = "DELETE FROM bans WHERE name='" + EscUser + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

vector<CDBBan *> MySQLBanList( void *conn, string *error, uint32_t botid)
{
	vector<CDBBan *> BanList;
	string Query = "SELECT name, ip, DATE(date), gamename, admin, reason FROM bans";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			while( Row.size( ) == 6 )
			{
				BanList.push_back( new CDBBan( "", Row[0], Row[1], Row[2], Row[3], Row[4], Row[5] ) );
				Row = MySQLFetchRow( Result );
			}

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return BanList;
}

uint32_t MySQLGameAdd( void *conn, string *error, uint32_t botid, string server, string map, string gamename, string ownername, uint32_t duration, uint32_t gamestate, string creatorname, string creatorserver )
{
	uint32_t RowID = 0;
	string EscServer = MySQLEscapeString( conn, server );
	string EscMap = MySQLEscapeString( conn, map );
	string EscGameName = MySQLEscapeString( conn, gamename );
	string EscOwnerName = MySQLEscapeString( conn, ownername );
	string EscCreatorName = MySQLEscapeString( conn, creatorname );
	string EscCreatorServer = MySQLEscapeString( conn, creatorserver );
	string Query = "INSERT INTO games ( botid, server, map, datetime, gamename, ownername, duration, gamestate, creatorname, creatorserver ) VALUES ( " + UTIL_ToString( botid ) + ", '" + EscServer + "', '" + EscMap + "', NOW( ), '" + EscGameName + "', '" + EscOwnerName + "', " + UTIL_ToString( duration ) + ", " + UTIL_ToString( gamestate ) + ", '" + EscCreatorName + "', '" + EscCreatorServer + "' )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		RowID = mysql_insert_id( (MYSQL *)conn );

	return RowID;
}

uint32_t MySQLGamePlayerAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t reserved, uint32_t loadingtime, uint32_t left, string leftreason, uint32_t team, uint32_t colour )
{
	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
	uint32_t RowID = 0;
	string EscName = MySQLEscapeString( conn, name );
	string EscIP = MySQLEscapeString( conn, ip );
	string EscSpoofedRealm = MySQLEscapeString( conn, spoofedrealm );
	string EscLeftReason = MySQLEscapeString( conn, leftreason );
	string Query = "INSERT INTO gameplayers ( botid, gameid, name, ip, spoofed, reserved, loadingtime, `left`, leftreason, team, colour, spoofedrealm ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", '" + EscName + "', '" + EscIP + "', " + UTIL_ToString( spoofed ) + ", " + UTIL_ToString( reserved ) + ", " + UTIL_ToString( loadingtime ) + ", " + UTIL_ToString( left ) + ", '" + EscLeftReason + "', " + UTIL_ToString( team ) + ", " + UTIL_ToString( colour ) + ", '" + EscSpoofedRealm + "' )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		RowID = mysql_insert_id( (MYSQL *)conn );

	return RowID;
}

CDBGamePlayerSummary *MySQLGamePlayerSummaryCheck( void *conn, string *error, uint32_t botid, string name )
{
	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
	string EscName = MySQLEscapeString( conn, name );
	CDBGamePlayerSummary *GamePlayerSummary = NULL;
	string Query = "SELECT MIN(DATE(datetime)), MAX(DATE(datetime)), COUNT(*), MIN(loadingtime), AVG(loadingtime), MAX(loadingtime), MIN(`left`/duration)*100, AVG(`left`/duration)*100, MAX(`left`/duration)*100, MIN(duration), AVG(duration), MAX(duration) FROM gameplayers LEFT JOIN games ON games.id=gameid WHERE LOWER(name)='" + EscName + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 12 )
			{
				string FirstGameDateTime = Row[0];
				string LastGameDateTime = Row[1];
				uint32_t TotalGames = UTIL_ToUInt32( Row[2] );
				uint32_t MinLoadingTime = UTIL_ToUInt32( Row[3] );
				uint32_t AvgLoadingTime = UTIL_ToUInt32( Row[4] );
				uint32_t MaxLoadingTime = UTIL_ToUInt32( Row[5] );
				uint32_t MinLeftPercent = UTIL_ToUInt32( Row[6] );
				uint32_t AvgLeftPercent = UTIL_ToUInt32( Row[7] );
				uint32_t MaxLeftPercent = UTIL_ToUInt32( Row[8] );
				uint32_t MinDuration = UTIL_ToUInt32( Row[9] );
				uint32_t AvgDuration = UTIL_ToUInt32( Row[10] );
				uint32_t MaxDuration = UTIL_ToUInt32( Row[11] );
				GamePlayerSummary = new CDBGamePlayerSummary( string( ), name, FirstGameDateTime, LastGameDateTime, TotalGames, MinLoadingTime, AvgLoadingTime, MaxLoadingTime, MinLeftPercent, AvgLeftPercent, MaxLeftPercent, MinDuration, AvgDuration, MaxDuration );
			}
			else
				*error = "error checking gameplayersummary [" + name + "] - row doesn't have 12 columns";

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return GamePlayerSummary;
}

uint32_t MySQLDotAGameAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, uint32_t winner, uint32_t min, uint32_t sec )
{
	uint32_t RowID = 0;
	string Query = "INSERT INTO dotagames ( botid, gameid, winner, min, sec ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( winner ) + ", " + UTIL_ToString( min ) + ", " + UTIL_ToString( sec ) + " )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		RowID = mysql_insert_id( (MYSQL *)conn );

	return RowID;
}

uint32_t MySQLDotAPlayerAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, uint32_t colour, uint32_t kills, uint32_t deaths, uint32_t creepkills, uint32_t creepdenies, uint32_t assists, uint32_t gold, uint32_t neutralkills, string item1, string item2, string item3, string item4, string item5, string item6, string hero, uint32_t newcolour, uint32_t towerkills, uint32_t raxkills, uint32_t courierkills )
{
	uint32_t RowID = 0;
	string EscItem1 = MySQLEscapeString( conn, item1 );
	string EscItem2 = MySQLEscapeString( conn, item2 );
	string EscItem3 = MySQLEscapeString( conn, item3 );
	string EscItem4 = MySQLEscapeString( conn, item4 );
	string EscItem5 = MySQLEscapeString( conn, item5 );
	string EscItem6 = MySQLEscapeString( conn, item6 );
	string EscHero = MySQLEscapeString( conn, hero );
	string Query = "INSERT INTO dotaplayers ( botid, gameid, colour, kills, deaths, creepkills, creepdenies, assists, gold, neutralkills, item1, item2, item3, item4, item5, item6, hero, newcolour, towerkills, raxkills, courierkills ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( colour ) + ", " + UTIL_ToString( kills ) + ", " + UTIL_ToString( deaths ) + ", " + UTIL_ToString( creepkills ) + ", " + UTIL_ToString( creepdenies ) + ", " + UTIL_ToString( assists ) + ", " + UTIL_ToString( gold ) + ", " + UTIL_ToString( neutralkills ) + ", '" + EscItem1 + "', '" + EscItem2 + "', '" + EscItem3 + "', '" + EscItem4 + "', '" + EscItem5 + "', '" + EscItem6 + "', '" + EscHero + "', " + UTIL_ToString( newcolour ) + ", " + UTIL_ToString( towerkills ) + ", " + UTIL_ToString( raxkills ) + ", " + UTIL_ToString( courierkills ) + " )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		RowID = mysql_insert_id( (MYSQL *)conn );

	return RowID;
}

CDBDotAPlayerSummary *MySQLDotAPlayerSummaryCheck( void *conn, string *error, uint32_t botid, string name )
{
	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
	string EscName = MySQLEscapeString( conn, name );
	CDBDotAPlayerSummary *DotAPlayerSummary = NULL;
	string Query = "SELECT COUNT(dotaplayers.id), SUM(kills), SUM(deaths), SUM(creepkills), SUM(creepdenies), SUM(assists), SUM(neutralkills), SUM(towerkills), SUM(raxkills), SUM(courierkills) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour WHERE LOWER(name)='" + EscName + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 10 )
			{
				uint32_t TotalGames = UTIL_ToUInt32( Row[0] );

				if( TotalGames > 0 )
				{
					uint32_t TotalWins = 0;
					uint32_t TotalLosses = 0;
					uint32_t TotalKills = UTIL_ToUInt32( Row[1] );
					uint32_t TotalDeaths = UTIL_ToUInt32( Row[2] );
					uint32_t TotalCreepKills = UTIL_ToUInt32( Row[3] );
					uint32_t TotalCreepDenies = UTIL_ToUInt32( Row[4] );
					uint32_t TotalAssists = UTIL_ToUInt32( Row[5] );
					uint32_t TotalNeutralKills = UTIL_ToUInt32( Row[6] );
					uint32_t TotalTowerKills = UTIL_ToUInt32( Row[7] );
					uint32_t TotalRaxKills = UTIL_ToUInt32( Row[8] );
					uint32_t TotalCourierKills = UTIL_ToUInt32( Row[9] );

					// calculate total wins

					string Query2 = "SELECT COUNT(*) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour LEFT JOIN dotagames ON games.id=dotagames.gameid WHERE name='" + EscName + "' AND ((winner=1 AND dotaplayers.newcolour>=1 AND dotaplayers.newcolour<=5) OR (winner=2 AND dotaplayers.newcolour>=7 AND dotaplayers.newcolour<=11))";

					if( mysql_real_query( (MYSQL *)conn, Query2.c_str( ), Query2.size( ) ) != 0 )
						*error = mysql_error( (MYSQL *)conn );
					else
					{
						MYSQL_RES *Result2 = mysql_store_result( (MYSQL *)conn );

						if( Result2 )
						{
							vector<string> Row2 = MySQLFetchRow( Result2 );

							if( Row2.size( ) == 1 )
								TotalWins = UTIL_ToUInt32( Row2[0] );
							else
								*error = "error checking dotaplayersummary wins [" + name + "] - row doesn't have 1 column";

							mysql_free_result( Result2 );
						}
						else
							*error = mysql_error( (MYSQL *)conn );
					}

					// calculate total losses

					string Query3 = "SELECT COUNT(*) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour LEFT JOIN dotagames ON games.id=dotagames.gameid WHERE name='" + EscName + "' AND ((winner=2 AND dotaplayers.newcolour>=1 AND dotaplayers.newcolour<=5) OR (winner=1 AND dotaplayers.newcolour>=7 AND dotaplayers.newcolour<=11))";

					if( mysql_real_query( (MYSQL *)conn, Query3.c_str( ), Query3.size( ) ) != 0 )
						*error = mysql_error( (MYSQL *)conn );
					else
					{
						MYSQL_RES *Result3 = mysql_store_result( (MYSQL *)conn );

						if( Result3 )
						{
							vector<string> Row3 = MySQLFetchRow( Result3 );

							if( Row3.size( ) == 1 )
								TotalLosses = UTIL_ToUInt32( Row3[0] );
							else
								*error = "error checking dotaplayersummary losses [" + name + "] - row doesn't have 1 column";

							mysql_free_result( Result3 );
						}
						else
							*error = mysql_error( (MYSQL *)conn );
					}

					// done

					DotAPlayerSummary = new CDBDotAPlayerSummary( string( ), name, TotalGames, TotalWins, TotalLosses, TotalKills, TotalDeaths, TotalCreepKills, TotalCreepDenies, TotalAssists, TotalNeutralKills, TotalTowerKills, TotalRaxKills, TotalCourierKills );
				}
			}
			else
				*error = "error checking dotaplayersummary [" + name + "] - row doesn't have 10 columns";

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return DotAPlayerSummary;
}

bool MySQLDownloadAdd( void *conn, string *error, uint32_t botid, string map, uint32_t mapsize, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t downloadtime )
{
	bool Success = false;
	string EscMap = MySQLEscapeString( conn, map );
	string EscName = MySQLEscapeString( conn, name );
	string EscIP = MySQLEscapeString( conn, ip );
	string EscSpoofedRealm = MySQLEscapeString( conn, spoofedrealm );
	string Query = "INSERT INTO downloads ( botid, map, mapsize, datetime, name, ip, spoofed, spoofedrealm, downloadtime ) VALUES ( " + UTIL_ToString( botid ) + ", '" + EscMap + "', " + UTIL_ToString( mapsize ) + ", NOW( ), '" + EscName + "', '" + EscIP + "', " + UTIL_ToString( spoofed ) + ", '" + EscSpoofedRealm + "', " + UTIL_ToString( downloadtime ) + " )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

double MySQLScoreCheck( void *conn, string *error, uint32_t botid, string category, string name, string server )
{
	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
	string EscCategory = MySQLEscapeString( conn, category );
	string EscName = MySQLEscapeString( conn, name );
	string EscServer = MySQLEscapeString( conn, server );
	double Score = -100000.0;
	string Query = "SELECT score FROM scores WHERE category='" + EscCategory + "' AND name='" + EscName + "' AND server='" + EscServer + "'";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
	{
		MYSQL_RES *Result = mysql_store_result( (MYSQL *)conn );

		if( Result )
		{
			vector<string> Row = MySQLFetchRow( Result );

			if( Row.size( ) == 1 )
				Score = UTIL_ToDouble( Row[0] );
			/* else
				*error = "error checking score [" + category + " : " + name + " : " + server + "] - row doesn't have 1 column"; */

			mysql_free_result( Result );
		}
		else
			*error = mysql_error( (MYSQL *)conn );
	}

	return Score;
}

uint32_t MySQLW3MMDPlayerAdd( void *conn, string *error, uint32_t botid, string category, uint32_t gameid, uint32_t pid, string name, string flag, uint32_t leaver, uint32_t practicing )
{
	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
	uint32_t RowID = 0;
	string EscCategory = MySQLEscapeString( conn, category );
	string EscName = MySQLEscapeString( conn, name );
	string EscFlag = MySQLEscapeString( conn, flag );
	string Query = "INSERT INTO w3mmdplayers ( botid, category, gameid, pid, name, flag, leaver, practicing ) VALUES ( " + UTIL_ToString( botid ) + ", '" + EscCategory + "', " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( pid ) + ", '" + EscName + "', '" + EscFlag + "', " + UTIL_ToString( leaver ) + ", " + UTIL_ToString( practicing ) + " )";

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		RowID = mysql_insert_id( (MYSQL *)conn );

	return RowID;
}

bool MySQLW3MMDVarAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, map<VarP,int32_t> var_ints )
{
	if( var_ints.empty( ) )
		return false;

	bool Success = false;
	string Query;

	for( map<VarP,int32_t> :: iterator i = var_ints.begin( ); i != var_ints.end( ); i++ )
	{
		string EscVarName = MySQLEscapeString( conn, i->first.second );

		if( Query.empty( ) )
			Query = "INSERT INTO w3mmdvars ( botid, gameid, pid, varname, value_int ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', " + UTIL_ToString( i->second ) + " )";
		else
			Query += ", ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', " + UTIL_ToString( i->second ) + " )";
	}

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

bool MySQLW3MMDVarAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, map<VarP,double> var_reals )
{
	if( var_reals.empty( ) )
		return false;

	bool Success = false;
	string Query;

	for( map<VarP,double> :: iterator i = var_reals.begin( ); i != var_reals.end( ); i++ )
	{
		string EscVarName = MySQLEscapeString( conn, i->first.second );

		if( Query.empty( ) )
			Query = "INSERT INTO w3mmdvars ( botid, gameid, pid, varname, value_real ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', " + UTIL_ToString( i->second, 10 ) + " )";
		else
			Query += ", ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', " + UTIL_ToString( i->second, 10 ) + " )";
	}

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

bool MySQLW3MMDVarAdd( void *conn, string *error, uint32_t botid, uint32_t gameid, map<VarP,string> var_strings )
{
	if( var_strings.empty( ) )
		return false;

	bool Success = false;
	string Query;

	for( map<VarP,string> :: iterator i = var_strings.begin( ); i != var_strings.end( ); i++ )
	{
		string EscVarName = MySQLEscapeString( conn, i->first.second );
		string EscValueString = MySQLEscapeString( conn, i->second );

		if( Query.empty( ) )
			Query = "INSERT INTO w3mmdvars ( botid, gameid, pid, varname, value_string ) VALUES ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', '" + EscValueString + "' )";
		else
			Query += ", ( " + UTIL_ToString( botid ) + ", " + UTIL_ToString( gameid ) + ", " + UTIL_ToString( i->first.first ) + ", '" + EscVarName + "', '" + EscValueString + "' )";
	}

	if( mysql_real_query( (MYSQL *)conn, Query.c_str( ), Query.size( ) ) != 0 )
		*error = mysql_error( (MYSQL *)conn );
	else
		Success = true;

	return Success;
}

//
// MySQL Callables
//

void CMySQLCallable :: Init( )
{
	CBaseCallable :: Init( );

#ifndef WIN32
	// disable SIGPIPE since this is (or should be) a new thread and it doesn't inherit the spawning thread's signal handlers
	// MySQL should automatically disable SIGPIPE when we initialize it but we do so anyway here

	signal( SIGPIPE, SIG_IGN );
#endif

	mysql_thread_init( );

	if( !m_Connection )
	{
		if( !( m_Connection = mysql_init( NULL ) ) )
			m_Error = mysql_error( (MYSQL *)m_Connection );

		my_bool Reconnect = true;
		mysql_options( (MYSQL *)m_Connection, MYSQL_OPT_RECONNECT, &Reconnect );

		if( !( mysql_real_connect( (MYSQL *)m_Connection, m_SQLServer.c_str( ), m_SQLUser.c_str( ), m_SQLPassword.c_str( ), m_SQLDatabase.c_str( ), m_SQLPort, NULL, 0 ) ) )
			m_Error = mysql_error( (MYSQL *)m_Connection );
	}
	else if( mysql_ping( (MYSQL *)m_Connection ) != 0 )
		m_Error = mysql_error( (MYSQL *)m_Connection );
}

void CMySQLCallable :: Close( )
{
	mysql_thread_end( );

	CBaseCallable :: Close( );
}

void CMySQLCallableAdminCount :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLAdminCount( m_Connection, &m_Error, m_SQLBotID, m_Server );

	Close( );
}

void CMySQLCallableAdminCheck :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLAdminCheck( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User );

	Close( );
}

void CMySQLCallableAdminAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLAdminAdd( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User );

	Close( );
}

void CMySQLCallableAdminRemove :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLAdminRemove( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User );

	Close( );
}

void CMySQLCallableAdminList :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLAdminList( m_Connection, &m_Error, m_SQLBotID, m_Server );

	Close( );
}

void CMySQLCallableBanCount :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLBanCount( m_Connection, &m_Error, m_SQLBotID, m_Server );

	Close( );
}

void CMySQLCallableBanCheck :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLBanCheck( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User, m_IP );

	Close( );
}

void CMySQLCallableBanAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLBanAdd( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User, m_IP, m_GameName, m_Admin, m_Reason );

	Close( );
}

void CMySQLCallableBanRemove :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
	{
		if( m_Server.empty( ) )
			m_Result = MySQLBanRemove( m_Connection, &m_Error, m_SQLBotID, m_User );
		else
			m_Result = MySQLBanRemove( m_Connection, &m_Error, m_SQLBotID, m_Server, m_User );
	}

	Close( );
}

void CMySQLCallableBanList :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLBanList( m_Connection, &m_Error, m_SQLBotID );

	Close( );
}

void CMySQLCallableGameAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLGameAdd( m_Connection, &m_Error, m_SQLBotID, m_Server, m_Map, m_GameName, m_OwnerName, m_Duration, m_GameState, m_CreatorName, m_CreatorServer );

	Close( );
}

void CMySQLCallableGamePlayerAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLGamePlayerAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_Name, m_IP, m_Spoofed, m_SpoofedRealm, m_Reserved, m_LoadingTime, m_Left, m_LeftReason, m_Team, m_Colour );

	Close( );
}

void CMySQLCallableGamePlayerSummaryCheck :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLGamePlayerSummaryCheck( m_Connection, &m_Error, m_SQLBotID, m_Name );

	Close( );
}

void CMySQLCallableDotAGameAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLDotAGameAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_Winner, m_Min, m_Sec );

	Close( );
}

void CMySQLCallableDotAPlayerAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLDotAPlayerAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_Colour, m_Kills, m_Deaths, m_CreepKills, m_CreepDenies, m_Assists, m_Gold, m_NeutralKills, m_Item1, m_Item2, m_Item3, m_Item4, m_Item5, m_Item6, m_Hero, m_NewColour, m_TowerKills, m_RaxKills, m_CourierKills );

	Close( );
}

void CMySQLCallableDotAPlayerSummaryCheck :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLDotAPlayerSummaryCheck( m_Connection, &m_Error, m_SQLBotID, m_Name );

	Close( );
}

void CMySQLCallableDownloadAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLDownloadAdd( m_Connection, &m_Error, m_SQLBotID, m_Map, m_MapSize, m_Name, m_IP, m_Spoofed, m_SpoofedRealm, m_DownloadTime );

	Close( );
}

void CMySQLCallableScoreCheck :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLScoreCheck( m_Connection, &m_Error, m_SQLBotID, m_Category, m_Name, m_Server );

	Close( );
}

void CMySQLCallableW3MMDPlayerAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
		m_Result = MySQLW3MMDPlayerAdd( m_Connection, &m_Error, m_SQLBotID, m_Category, m_GameID, m_PID, m_Name, m_Flag, m_Leaver, m_Practicing );

	Close( );
}

void CMySQLCallableW3MMDVarAdd :: operator( )( )
{
	Init( );

	if( m_Error.empty( ) )
	{
		if( m_ValueType == VALUETYPE_INT )
			m_Result = MySQLW3MMDVarAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_VarInts );
		else if( m_ValueType == VALUETYPE_REAL )
			m_Result = MySQLW3MMDVarAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_VarReals );
		else
			m_Result = MySQLW3MMDVarAdd( m_Connection, &m_Error, m_SQLBotID, m_GameID, m_VarStrings );
	}

	Close( );
}