<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "Copyright (c) 2009-2010 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
#ident "$Id$"

/***********
 * The purpose of this file is to implement the high-level logic for
 * taking a checkpoint.
 *
 * There are three locks used for taking a checkpoint.  They are listed below.
 *
 * NOTE: The reader-writer locks may be held by either multiple clients
 *       or the checkpoint function.  (The checkpoint function has the role
 *       of the writer, the clients have the reader roles.)
 *
 *  - multi_operation_lock
 *    This is a new reader-writer lock.
 *    This lock is held by the checkpoint function only for as long as is required to
 *    to set all the "pending" bits and to create the checkpoint-in-progress versions
 *    of the header and translation table (btt).
 *    The following operations must take the multi_operation_lock:
 *     - any set of operations that must be atomic with respect to begin checkpoint
 *
 *  - checkpoint_safe_lock
 *    This is a new reader-writer lock.
 *    This lock is held for the entire duration of the checkpoint.
 *    It is used to prevent more than one checkpoint from happening at a time
 *    (the checkpoint function is non-re-entrant), and to prevent certain operations
 *    that should not happen during a checkpoint.
 *    The following operations must take the checkpoint_safe lock:
 *       - delete a dictionary
 *       - rename a dictionary
 *    The application can use this lock to disable checkpointing during other sensitive
 *    operations, such as making a backup copy of the database.
 *
 * Once the "pending" bits are set and the snapshots are taken of the header and btt,
 * most normal database operations are permitted to resume.
 *
 *
 *
 *****/

#include <toku_portability.h>
#include <time.h>

#include "fttypes.h"
#include "cachetable.h"
#include "log-internal.h"
#include "logger.h"
#include "checkpoint.h"

///////////////////////////////////////////////////////////////////////////////////
// Engine status
//
// Status is intended for display to humans to help understand system behavior.
// It does not need to be perfectly thread-safe.

static CHECKPOINT_STATUS_S cp_status;

#define STATUS_INIT(k,t,l) { \
        cp_status.status[k].keyname = #k; \
        cp_status.status[k].type    = t;  \
        cp_status.status[k].legend  = "checkpoint: " l; \
    }

static void
status_init(void) {
    // Note, this function initializes the keyname, type, and legend fields.
    // Value fields are initialized to zero by compiler.

    STATUS_INIT(CP_PERIOD,                              UINT64,   "period");
    STATUS_INIT(CP_FOOTPRINT,                           UINT64,   "footprint");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_BEGIN,          UNIXTIME, "last checkpoint began ");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_BEGIN_COMPLETE, UNIXTIME, "last complete checkpoint began ");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_END,            UNIXTIME, "last complete checkpoint ended");
    STATUS_INIT(CP_LAST_LSN,                            UINT64,   "last complete checkpoint LSN");
    STATUS_INIT(CP_CHECKPOINT_COUNT,                    UINT64,   "checkpoints taken ");
    STATUS_INIT(CP_CHECKPOINT_COUNT_FAIL,               UINT64,   "checkpoints failed");
    STATUS_INIT(CP_WAITERS_NOW,                         UINT64,   "waiters now");
    STATUS_INIT(CP_WAITERS_MAX,                         UINT64,   "waiters max");
    STATUS_INIT(CP_CLIENT_WAIT_ON_MO,                   UINT64,   "non-checkpoint client wait on mo lock");
    STATUS_INIT(CP_CLIENT_WAIT_ON_CS,                   UINT64,   "non-checkpoint client wait on cs lock");
    cp_status.initialized = true;
}
#undef STATUS_INIT

#define STATUS_VALUE(x) cp_status.status[x].value.num

void
toku_checkpoint_get_status(CACHETABLE ct, CHECKPOINT_STATUS statp) {
    if (!cp_status.initialized)
        status_init();
    STATUS_VALUE(CP_PERIOD) = toku_get_checkpoint_period_unlocked(ct);
    *statp = cp_status;
}

static LSN last_completed_checkpoint_lsn;

static toku_pthread_rwlock_t checkpoint_safe_lock;
static toku_pthread_rwlock_t multi_operation_lock;

static bool initialized = false;     // sanity check
static volatile bool locked_mo = false;       // true when the multi_operation write lock is held (by checkpoint)
static volatile bool locked_cs = false;       // true when the checkpoint_safe write lock is held (by checkpoint)

// Note following static functions are called from checkpoint internal logic only,
// and use the "writer" calls for locking and unlocking.

static void
multi_operation_lock_init(void) {
    pthread_rwlockattr_t attr;
    pthread_rwlockattr_init(&attr);
#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
    pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
#else
    // TODO: need to figure out how to make writer-preferential rwlocks
    // happen on osx
#endif
    toku_pthread_rwlock_init(&multi_operation_lock, &attr);
    pthread_rwlockattr_destroy(&attr);
    locked_mo = false;
}

static void
multi_operation_lock_destroy(void) {
    toku_pthread_rwlock_destroy(&multi_operation_lock);
}

static void
multi_operation_checkpoint_lock(void) {
    toku_pthread_rwlock_wrlock(&multi_operation_lock);
    locked_mo = true;
}

static void
multi_operation_checkpoint_unlock(void) {
    locked_mo = false;
    toku_pthread_rwlock_wrunlock(&multi_operation_lock);
}

static void
checkpoint_safe_lock_init(void) {
    toku_pthread_rwlock_init(&checkpoint_safe_lock, NULL);
    locked_cs = false;
}

static void
checkpoint_safe_lock_destroy(void) {
    toku_pthread_rwlock_destroy(&checkpoint_safe_lock);
}

static void
checkpoint_safe_checkpoint_lock(void) {
    toku_pthread_rwlock_wrlock(&checkpoint_safe_lock);
    locked_cs = true;
}

static void
checkpoint_safe_checkpoint_unlock(void) {
    locked_cs = false;
    toku_pthread_rwlock_wrunlock(&checkpoint_safe_lock);
}

// toku_xxx_client_(un)lock() functions are only called from client code,
// never from checkpoint code, and use the "reader" interface to the lock functions.

void
toku_multi_operation_client_lock(void) {
    if (locked_mo)
        (void) __sync_fetch_and_add(&STATUS_VALUE(CP_CLIENT_WAIT_ON_MO), 1);
    toku_pthread_rwlock_rdlock(&multi_operation_lock);
}

void
toku_multi_operation_client_unlock(void) {
    toku_pthread_rwlock_rdunlock(&multi_operation_lock);
}

void
toku_checkpoint_safe_client_lock(void) {
    if (locked_cs)
        (void) __sync_fetch_and_add(&STATUS_VALUE(CP_CLIENT_WAIT_ON_CS), 1);
    toku_pthread_rwlock_rdlock(&checkpoint_safe_lock);
    toku_multi_operation_client_lock();
}

void
toku_checkpoint_safe_client_unlock(void) {
    toku_pthread_rwlock_rdunlock(&checkpoint_safe_lock);
    toku_multi_operation_client_unlock();
}

// Initialize the checkpoint mechanism, must be called before any client operations.
void
toku_checkpoint_init(void) {
    multi_operation_lock_init();
    checkpoint_safe_lock_init();
    initialized = true;
}

void
toku_checkpoint_destroy(void) {
    multi_operation_lock_destroy();
    checkpoint_safe_lock_destroy();
    initialized = false;
}

static bool checkpoint_caller_is_aggressive(checkpoint_caller_t caller_id) {
    bool retval;
    switch (caller_id) {
    case SCHEDULED_CHECKPOINT:
    case CLIENT_CHECKPOINT:
        retval = false;
        break;
    case TXN_COMMIT_CHECKPOINT:
    case STARTUP_CHECKPOINT:
    case UPGRADE_CHECKPOINT:
    case RECOVERY_CHECKPOINT:
    case SHUTDOWN_CHECKPOINT:
        retval = true;
        break;
    }
    return retval;
}

#define SET_CHECKPOINT_FOOTPRINT(x) STATUS_VALUE(CP_FOOTPRINT) = footprint_offset + x

// Take a checkpoint of all currently open dictionaries
int
toku_checkpoint(CHECKPOINTER cp, TOKULOGGER logger,
                void (*callback_f)(void*),  void * extra,
                void (*callback2_f)(void*), void * extra2,
                checkpoint_caller_t caller_id) {
    int r;
    int footprint_offset = (int) caller_id * 1000;

    assert(initialized);

    (void) __sync_fetch_and_add(&STATUS_VALUE(CP_WAITERS_NOW), 1);
    checkpoint_safe_checkpoint_lock();
    (void) __sync_fetch_and_sub(&STATUS_VALUE(CP_WAITERS_NOW), 1);

    if (STATUS_VALUE(CP_WAITERS_NOW) > STATUS_VALUE(CP_WAITERS_MAX))
        STATUS_VALUE(CP_WAITERS_MAX) = STATUS_VALUE(CP_WAITERS_NOW);  // threadsafe, within checkpoint_safe lock

    SET_CHECKPOINT_FOOTPRINT(10);
    multi_operation_checkpoint_lock();
    SET_CHECKPOINT_FOOTPRINT(20);
    toku_ft_open_close_lock();

    SET_CHECKPOINT_FOOTPRINT(30);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN) = time(NULL);
    r = toku_cachetable_begin_checkpoint(cp);

    toku_ft_open_close_unlock();
    multi_operation_checkpoint_unlock();

    SET_CHECKPOINT_FOOTPRINT(40);
    if (r==0) {
        if (callback_f) {
            callback_f(extra);      // callback is called with checkpoint_safe_lock still held
        }
        bool aggressive = checkpoint_caller_is_aggressive(caller_id);
        r = toku_cachetable_end_checkpoint(cp, aggressive, callback2_f, extra2);
    }
    SET_CHECKPOINT_FOOTPRINT(50);
    if (r==0 && logger) {
        last_completed_checkpoint_lsn = logger->last_completed_checkpoint_lsn;
        r = toku_logger_maybe_trim_log(logger, last_completed_checkpoint_lsn);
        STATUS_VALUE(CP_LAST_LSN) = last_completed_checkpoint_lsn.lsn;
    }

    SET_CHECKPOINT_FOOTPRINT(60);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_END) = time(NULL);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN_COMPLETE) = STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN);

    if (r == 0)
        STATUS_VALUE(CP_CHECKPOINT_COUNT)++;
    else
        STATUS_VALUE(CP_CHECKPOINT_COUNT_FAIL)++;

    STATUS_VALUE(CP_FOOTPRINT) = 0;
    checkpoint_safe_checkpoint_unlock();
    return r;
}

#include <valgrind/helgrind.h>
void __attribute__((__constructor__)) toku_checkpoint_helgrind_ignore(void);
void
toku_checkpoint_helgrind_ignore(void) {
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&cp_status, sizeof cp_status);
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&locked_mo, sizeof locked_mo);
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&locked_cs, sizeof locked_cs);
}

#undef SET_CHECKPOINT_FOOTPRINT
#undef STATUS_VALUE
<commit_msg>refs #5467 fix -Wmaybe-uninitialized warning when compiling with optimizations<commit_after>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "Copyright (c) 2009-2010 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
#ident "$Id$"

/***********
 * The purpose of this file is to implement the high-level logic for
 * taking a checkpoint.
 *
 * There are three locks used for taking a checkpoint.  They are listed below.
 *
 * NOTE: The reader-writer locks may be held by either multiple clients
 *       or the checkpoint function.  (The checkpoint function has the role
 *       of the writer, the clients have the reader roles.)
 *
 *  - multi_operation_lock
 *    This is a new reader-writer lock.
 *    This lock is held by the checkpoint function only for as long as is required to
 *    to set all the "pending" bits and to create the checkpoint-in-progress versions
 *    of the header and translation table (btt).
 *    The following operations must take the multi_operation_lock:
 *     - any set of operations that must be atomic with respect to begin checkpoint
 *
 *  - checkpoint_safe_lock
 *    This is a new reader-writer lock.
 *    This lock is held for the entire duration of the checkpoint.
 *    It is used to prevent more than one checkpoint from happening at a time
 *    (the checkpoint function is non-re-entrant), and to prevent certain operations
 *    that should not happen during a checkpoint.
 *    The following operations must take the checkpoint_safe lock:
 *       - delete a dictionary
 *       - rename a dictionary
 *    The application can use this lock to disable checkpointing during other sensitive
 *    operations, such as making a backup copy of the database.
 *
 * Once the "pending" bits are set and the snapshots are taken of the header and btt,
 * most normal database operations are permitted to resume.
 *
 *
 *
 *****/

#include <toku_portability.h>
#include <time.h>

#include "fttypes.h"
#include "cachetable.h"
#include "log-internal.h"
#include "logger.h"
#include "checkpoint.h"

///////////////////////////////////////////////////////////////////////////////////
// Engine status
//
// Status is intended for display to humans to help understand system behavior.
// It does not need to be perfectly thread-safe.

static CHECKPOINT_STATUS_S cp_status;

#define STATUS_INIT(k,t,l) { \
        cp_status.status[k].keyname = #k; \
        cp_status.status[k].type    = t;  \
        cp_status.status[k].legend  = "checkpoint: " l; \
    }

static void
status_init(void) {
    // Note, this function initializes the keyname, type, and legend fields.
    // Value fields are initialized to zero by compiler.

    STATUS_INIT(CP_PERIOD,                              UINT64,   "period");
    STATUS_INIT(CP_FOOTPRINT,                           UINT64,   "footprint");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_BEGIN,          UNIXTIME, "last checkpoint began ");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_BEGIN_COMPLETE, UNIXTIME, "last complete checkpoint began ");
    STATUS_INIT(CP_TIME_LAST_CHECKPOINT_END,            UNIXTIME, "last complete checkpoint ended");
    STATUS_INIT(CP_LAST_LSN,                            UINT64,   "last complete checkpoint LSN");
    STATUS_INIT(CP_CHECKPOINT_COUNT,                    UINT64,   "checkpoints taken ");
    STATUS_INIT(CP_CHECKPOINT_COUNT_FAIL,               UINT64,   "checkpoints failed");
    STATUS_INIT(CP_WAITERS_NOW,                         UINT64,   "waiters now");
    STATUS_INIT(CP_WAITERS_MAX,                         UINT64,   "waiters max");
    STATUS_INIT(CP_CLIENT_WAIT_ON_MO,                   UINT64,   "non-checkpoint client wait on mo lock");
    STATUS_INIT(CP_CLIENT_WAIT_ON_CS,                   UINT64,   "non-checkpoint client wait on cs lock");
    cp_status.initialized = true;
}
#undef STATUS_INIT

#define STATUS_VALUE(x) cp_status.status[x].value.num

void
toku_checkpoint_get_status(CACHETABLE ct, CHECKPOINT_STATUS statp) {
    if (!cp_status.initialized)
        status_init();
    STATUS_VALUE(CP_PERIOD) = toku_get_checkpoint_period_unlocked(ct);
    *statp = cp_status;
}

static LSN last_completed_checkpoint_lsn;

static toku_pthread_rwlock_t checkpoint_safe_lock;
static toku_pthread_rwlock_t multi_operation_lock;

static bool initialized = false;     // sanity check
static volatile bool locked_mo = false;       // true when the multi_operation write lock is held (by checkpoint)
static volatile bool locked_cs = false;       // true when the checkpoint_safe write lock is held (by checkpoint)

// Note following static functions are called from checkpoint internal logic only,
// and use the "writer" calls for locking and unlocking.

static void
multi_operation_lock_init(void) {
    pthread_rwlockattr_t attr;
    pthread_rwlockattr_init(&attr);
#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
    pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
#else
    // TODO: need to figure out how to make writer-preferential rwlocks
    // happen on osx
#endif
    toku_pthread_rwlock_init(&multi_operation_lock, &attr);
    pthread_rwlockattr_destroy(&attr);
    locked_mo = false;
}

static void
multi_operation_lock_destroy(void) {
    toku_pthread_rwlock_destroy(&multi_operation_lock);
}

static void
multi_operation_checkpoint_lock(void) {
    toku_pthread_rwlock_wrlock(&multi_operation_lock);
    locked_mo = true;
}

static void
multi_operation_checkpoint_unlock(void) {
    locked_mo = false;
    toku_pthread_rwlock_wrunlock(&multi_operation_lock);
}

static void
checkpoint_safe_lock_init(void) {
    toku_pthread_rwlock_init(&checkpoint_safe_lock, NULL);
    locked_cs = false;
}

static void
checkpoint_safe_lock_destroy(void) {
    toku_pthread_rwlock_destroy(&checkpoint_safe_lock);
}

static void
checkpoint_safe_checkpoint_lock(void) {
    toku_pthread_rwlock_wrlock(&checkpoint_safe_lock);
    locked_cs = true;
}

static void
checkpoint_safe_checkpoint_unlock(void) {
    locked_cs = false;
    toku_pthread_rwlock_wrunlock(&checkpoint_safe_lock);
}

// toku_xxx_client_(un)lock() functions are only called from client code,
// never from checkpoint code, and use the "reader" interface to the lock functions.

void
toku_multi_operation_client_lock(void) {
    if (locked_mo)
        (void) __sync_fetch_and_add(&STATUS_VALUE(CP_CLIENT_WAIT_ON_MO), 1);
    toku_pthread_rwlock_rdlock(&multi_operation_lock);
}

void
toku_multi_operation_client_unlock(void) {
    toku_pthread_rwlock_rdunlock(&multi_operation_lock);
}

void
toku_checkpoint_safe_client_lock(void) {
    if (locked_cs)
        (void) __sync_fetch_and_add(&STATUS_VALUE(CP_CLIENT_WAIT_ON_CS), 1);
    toku_pthread_rwlock_rdlock(&checkpoint_safe_lock);
    toku_multi_operation_client_lock();
}

void
toku_checkpoint_safe_client_unlock(void) {
    toku_pthread_rwlock_rdunlock(&checkpoint_safe_lock);
    toku_multi_operation_client_unlock();
}

// Initialize the checkpoint mechanism, must be called before any client operations.
void
toku_checkpoint_init(void) {
    multi_operation_lock_init();
    checkpoint_safe_lock_init();
    initialized = true;
}

void
toku_checkpoint_destroy(void) {
    multi_operation_lock_destroy();
    checkpoint_safe_lock_destroy();
    initialized = false;
}

static bool checkpoint_caller_is_aggressive(checkpoint_caller_t caller_id) {
    bool retval;
    switch (caller_id) {
    case SCHEDULED_CHECKPOINT:
    case CLIENT_CHECKPOINT:
        retval = false;
        break;
    case TXN_COMMIT_CHECKPOINT:
    case STARTUP_CHECKPOINT:
    case UPGRADE_CHECKPOINT:
    case RECOVERY_CHECKPOINT:
    case SHUTDOWN_CHECKPOINT:
        retval = true;
        break;
    default:
        abort();
    }
    return retval;
}

#define SET_CHECKPOINT_FOOTPRINT(x) STATUS_VALUE(CP_FOOTPRINT) = footprint_offset + x

// Take a checkpoint of all currently open dictionaries
int
toku_checkpoint(CHECKPOINTER cp, TOKULOGGER logger,
                void (*callback_f)(void*),  void * extra,
                void (*callback2_f)(void*), void * extra2,
                checkpoint_caller_t caller_id) {
    int r;
    int footprint_offset = (int) caller_id * 1000;

    assert(initialized);

    (void) __sync_fetch_and_add(&STATUS_VALUE(CP_WAITERS_NOW), 1);
    checkpoint_safe_checkpoint_lock();
    (void) __sync_fetch_and_sub(&STATUS_VALUE(CP_WAITERS_NOW), 1);

    if (STATUS_VALUE(CP_WAITERS_NOW) > STATUS_VALUE(CP_WAITERS_MAX))
        STATUS_VALUE(CP_WAITERS_MAX) = STATUS_VALUE(CP_WAITERS_NOW);  // threadsafe, within checkpoint_safe lock

    SET_CHECKPOINT_FOOTPRINT(10);
    multi_operation_checkpoint_lock();
    SET_CHECKPOINT_FOOTPRINT(20);
    toku_ft_open_close_lock();

    SET_CHECKPOINT_FOOTPRINT(30);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN) = time(NULL);
    r = toku_cachetable_begin_checkpoint(cp);

    toku_ft_open_close_unlock();
    multi_operation_checkpoint_unlock();

    SET_CHECKPOINT_FOOTPRINT(40);
    if (r==0) {
        if (callback_f) {
            callback_f(extra);      // callback is called with checkpoint_safe_lock still held
        }
        bool aggressive = checkpoint_caller_is_aggressive(caller_id);
        r = toku_cachetable_end_checkpoint(cp, aggressive, callback2_f, extra2);
    }
    SET_CHECKPOINT_FOOTPRINT(50);
    if (r==0 && logger) {
        last_completed_checkpoint_lsn = logger->last_completed_checkpoint_lsn;
        r = toku_logger_maybe_trim_log(logger, last_completed_checkpoint_lsn);
        STATUS_VALUE(CP_LAST_LSN) = last_completed_checkpoint_lsn.lsn;
    }

    SET_CHECKPOINT_FOOTPRINT(60);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_END) = time(NULL);
    STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN_COMPLETE) = STATUS_VALUE(CP_TIME_LAST_CHECKPOINT_BEGIN);

    if (r == 0)
        STATUS_VALUE(CP_CHECKPOINT_COUNT)++;
    else
        STATUS_VALUE(CP_CHECKPOINT_COUNT_FAIL)++;

    STATUS_VALUE(CP_FOOTPRINT) = 0;
    checkpoint_safe_checkpoint_unlock();
    return r;
}

#include <valgrind/helgrind.h>
void __attribute__((__constructor__)) toku_checkpoint_helgrind_ignore(void);
void
toku_checkpoint_helgrind_ignore(void) {
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&cp_status, sizeof cp_status);
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&locked_mo, sizeof locked_mo);
    HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&locked_cs, sizeof locked_cs);
}

#undef SET_CHECKPOINT_FOOTPRINT
#undef STATUS_VALUE
<|endoftext|>


<commit_before><commit_msg>re-enable get/set 1d/2d/3d/4d<commit_after><|endoftext|>


<commit_before><commit_msg>Format code using clang-format version 11.0.0 (Fedora 11.0.0-2.fc33)<commit_after><|endoftext|>


<commit_before>/*
** Author(s):
**  - Herve Cuche <hcuche@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <iostream>
#include "src/transport_socket_p.hpp"

namespace qi
{
  TransportSocketPrivate::TransportSocketPrivate(TransportSocket *socket)
    : tcd(NULL)
    , connected(false)
    , readHdr(true)
    , msg(0)
    , self(socket)
  {
  }

  TransportSocketPrivate::~TransportSocketPrivate()
  {
  }

// if msecs < 0 no timeout
  bool TransportSocketPrivate::waitForConnected(int msecs)
  {
    if (!isConnected())
    {
      qiLogError("qimessaging.TransportSocket") << "socket is not connected.";
      return false;
    }

    // no timeout
    if (msecs < 0)
    {
      while (!isConnected())
        ;

      return true;
    }

    while (!isConnected() && msecs > 0)
    {
      qi::os::msleep(1);
      msecs--;
    }

    // timeout
    if (msecs == 0)
      return false;

    return true;
  }

  bool TransportSocketPrivate::waitForDisconnected(int msecs)
  {
    if (!isConnected())
      return true;

    // no timeout
    if (msecs < 0)
    {
      while (isConnected())
        ;

      return true;
    }

    while (isConnected() && msecs > 0)
    {
      qi::os::msleep(1);
      msecs--;
    }

    // timeout
    if (msecs == 0)
      return false;

    return true;
  }

  bool TransportSocketPrivate::waitForId(int id, int msecs)
  {
    if (!isConnected())
    {
      qiLogError("qimessaging.TransportSocket") << "socket is not connected.";
      return false;
    }

    std::map<unsigned int, qi::Message*>::iterator it;
    {
      {
        boost::mutex::scoped_lock l(mtx);
        it = msgSend.find(id);
        if (it != msgSend.end())
          return true;
        if (!isConnected())
          return false;
        if (msecs > 0)
          cond.timed_wait(l, boost::posix_time::milliseconds(msecs));
        else
          cond.wait(l);
        it = msgSend.find(id);
        if (it != msgSend.end())
          return true;
      }
    }
    return false;
  }

  bool TransportSocketPrivate::read(int id, qi::Message *msg)
  {
    if (!isConnected())
    {
      qiLogError("qimessaging.TransportSocket") << "socket is not connected.";
      return false;
    }

    std::map<unsigned int, qi::Message*>::iterator it;
    {
      boost::mutex::scoped_lock l(mtx);
      it = msgSend.find(id);
      if (it != msgSend.end())
      {
        *msg = *it->second;
        delete it->second;
        msgSend.erase(it);
        return true;
      }
    }

    qiLogError("qimessaging.TransportSocket") << "message #" << id
                                              << " could not be found.";

    return false;
  }

  void TransportSocketPrivate::setCallbacks(TransportSocketInterface *delegate)
  {
    tcd = delegate;
  }

  bool TransportSocketPrivate::isConnected()
  {
    return connected;
  }
}
<commit_msg>Fix none optimistic connect<commit_after>/*
** Author(s):
**  - Herve Cuche <hcuche@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <iostream>
#include "src/transport_socket_p.hpp"

namespace qi
{
  TransportSocketPrivate::TransportSocketPrivate(TransportSocket *socket)
    : tcd(NULL)
    , connected(false)
    , readHdr(true)
    , msg(0)
    , self(socket)
  {
  }

  TransportSocketPrivate::~TransportSocketPrivate()
  {
  }

// if msecs < 0 no timeout
  bool TransportSocketPrivate::waitForConnected(int msecs)
  {
    // no timeout
    if (msecs < 0)
    {
      while (!isConnected())
        ;

      return true;
    }

    while (!isConnected() && msecs > 0)
    {
      qi::os::msleep(1);
      msecs--;
    }

    // timeout
    if (msecs == 0)
      return false;

    return true;
  }

  bool TransportSocketPrivate::waitForDisconnected(int msecs)
  {
    // no timeout
    if (msecs < 0)
    {
      while (isConnected())
        ;

      return true;
    }

    while (isConnected() && msecs > 0)
    {
      qi::os::msleep(1);
      msecs--;
    }

    // timeout
    if (msecs == 0)
      return false;

    return true;
  }

  bool TransportSocketPrivate::waitForId(int id, int msecs)
  {
    if (!isConnected())
    {
      qiLogError("qimessaging.TransportSocket") << "socket is not connected.";
      return false;
    }

    std::map<unsigned int, qi::Message*>::iterator it;
    {
      {
        boost::mutex::scoped_lock l(mtx);
        it = msgSend.find(id);
        if (it != msgSend.end())
          return true;
        if (!isConnected())
          return false;
        if (msecs > 0)
          cond.timed_wait(l, boost::posix_time::milliseconds(msecs));
        else
          cond.wait(l);
        it = msgSend.find(id);
        if (it != msgSend.end())
          return true;
      }
    }
    return false;
  }

  bool TransportSocketPrivate::read(int id, qi::Message *msg)
  {
    if (!isConnected())
    {
      qiLogError("qimessaging.TransportSocket") << "socket is not connected.";
      return false;
    }

    std::map<unsigned int, qi::Message*>::iterator it;
    {
      boost::mutex::scoped_lock l(mtx);
      it = msgSend.find(id);
      if (it != msgSend.end())
      {
        *msg = *it->second;
        delete it->second;
        msgSend.erase(it);
        return true;
      }
    }

    qiLogError("qimessaging.TransportSocket") << "message #" << id
                                              << " could not be found.";

    return false;
  }

  void TransportSocketPrivate::setCallbacks(TransportSocketInterface *delegate)
  {
    tcd = delegate;
  }

  bool TransportSocketPrivate::isConnected()
  {
    return connected;
  }
}
<|endoftext|>


<commit_before><commit_msg>Updates for Qt 5 migration needed for Mac OS X 10.8+<commit_after><|endoftext|>


<commit_before>// Copyright 2017 Rick van Schijndel

#include <Arduino.h>
#include <stdio.h>

#include <Bounce2.h>
#include <U8g2lib.h>

#include "logo.h"
#include "player_dick.h"
#include "game_state.h"
#include "asset.h"
#include "player.h"

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

const uint8_t kButtonPin = D3;
const uint8_t kScreenWidth = 128;
const uint8_t kScreenHeight = 64;

// Global game state
gameState game_state = start;

// The player in the game
Player player;

// Assets
Asset player_asset(player_width, player_height, player_bits);
Asset logo_asset(bootup_width, bootup_height, bootup_bits);

// The display object
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);

Bounce button;

void setupButton() {
  pinMode(kButtonPin, INPUT_PULLUP);
  button.attach(kButtonPin);
  button.interval(10);
}

void setupGraphics() {
  u8g2.begin();
  u8g2.setFlipMode(0);
  u8g2.setFont(u8g2_font_artossans8_8r);
}

void setup(void) {
  setupGraphics();
  setupButton();
  Serial.begin(115200);
}

inline void nextGameState() {
  game_state = static_cast<gameState>((game_state + 1) % (gameOver + 1));
}

void drawPlayer(uint16_t y = 0) {
  int16_t y_position_from_ground = kScreenHeight - player_asset.getHeight() - y;
  int16_t player_x = ((kScreenWidth / 3) - (player_asset.getHeight() / 2));
  u8g2.drawXBM(
    player_x,
    y_position_from_ground,
    player_asset.getHeight(),
    player_asset.getHeight(),
    player_asset.getBitmap());
}

void drawObstacles() {
  // TODO: draw obstacles according to the values in some array
}

void drawBootupScreen() {
  int16_t logoMiddleX = (kScreenWidth / 2) - (bootup_width / 2);
  int16_t logoMiddleY = (kScreenHeight / 2) - (bootup_height / 2);
  u8g2.drawXBM(
    logoMiddleX,
    logoMiddleY,
    bootup_width,
    bootup_height,
    bootup_bits);
}

void drawGameOver() {
  static const uint8_t x_pos_gameover = 20;
  static const uint8_t y_pos_gameover = 20;
  u8g2.drawStr(x_pos_gameover, y_pos_gameover, "GAME OVER");
  // TODO: draw fancy asset?
}

void drawScore() {
  static const uint8_t kScoreX = 20;
  static const uint8_t kScoreY = 60;
  static const size_t kScoreBufferSize = 20;
  char score_buffer[kScoreBufferSize];
  snprintf(score_buffer, kScoreBufferSize, "Score: %u", player.getScore());
  u8g2.drawStr(kScoreX, kScoreY, score_buffer);
}

void drawHiscoreScreen() {
  static const uint8_t kHiscoreX = 0;
  static const uint8_t kHiscoreY = 20;
  u8g2.drawStr(kHiscoreX, kHiscoreY, "HI SCORES");
  // TODO: draw hi scores
}

bool collisionDetected() {
  /*
  if player_right_x_position >= obstacle_left_x_position and
    player_left_x_position <= obstacle_right_x_position
    player_bottom_y_position <= obstacle_top_y_position
  */

  // TODO: implement algorithm, remove auto logic
  return player.getScore() > (unsigned)random(100, 250);
}

void updateObstaclePosition() {
  // TODO
}

void resetGame() {
  player = Player();
}

void updateGameLogic(bool button_pressed) {
  switch (game_state) {
    case start:
      {
        if (button_pressed) {
          nextGameState();
        }
        break;
      }
    case hiscore:
      {
        if (button_pressed) {
          nextGameState();
        }
        break;
      }
    case play:
      {
        if (button_pressed) {
          if (player.onGround()) {
            player.jump();
          }
        }

        player.updateYPosition();
        updateObstaclePosition();

        if (collisionDetected()) {
          nextGameState();
          break;
        }
        // happens after collision detection, so score will only get higher
        // if player does not collide
        player.updateScore();
        break;
      }
    case gameOver:
      {
        if (button_pressed) {
          resetGame();
          nextGameState();
        }
        break;
      }
    default:
      {
        Serial.println("Invalid game state");
        break;
      }
  }
}

void updateGameGraphics() {
  // clear buffer to start drawing
  u8g2.clearBuffer();

  // update graphics
  switch (game_state) {
    case start:
      {
        drawBootupScreen();
        break;
      }
    case hiscore:
      {
        drawHiscoreScreen();
        break;
      }
    case play:
      {
        drawPlayer(player.getYPosition());
        drawObstacles();
        break;
      }
    case gameOver:
      {
        drawGameOver();
        drawScore();
        break;
      }
    default:
      {
        Serial.println("invalid game state");
        break;
      }
  }

  // draw everything on the screen
  u8g2.sendBuffer();
}

void loop(void) {
  // In the loop so it's only true once, after this it's false again.
  // This is so it won't think the button is pressed again every loop.
  bool buttonPressed = false;

  if (button.update()) {
    buttonPressed = button.read();
    Serial.println("Button update");
  }

  updateGameLogic(buttonPressed);
  updateGameGraphics();
}
<commit_msg>Fixed player asset being drawn incorrectly<commit_after>// Copyright 2017 Rick van Schijndel

#include <Arduino.h>
#include <stdio.h>

#include <Bounce2.h>
#include <U8g2lib.h>

#include "logo.h"
#include "player_dick.h"
#include "game_state.h"
#include "asset.h"
#include "player.h"

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

const uint8_t kButtonPin = D3;
const uint8_t kScreenWidth = 128;
const uint8_t kScreenHeight = 64;

// Global game state
gameState game_state = start;

// The player in the game
Player player;

// Assets
Asset player_asset(player_width, player_height, player_bits);
Asset logo_asset(bootup_width, bootup_height, bootup_bits);

// The display object
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);

Bounce button;

void setupButton() {
  pinMode(kButtonPin, INPUT_PULLUP);
  button.attach(kButtonPin);
  button.interval(10);
}

void setupGraphics() {
  u8g2.begin();
  u8g2.setFlipMode(0);
  u8g2.setFont(u8g2_font_artossans8_8r);
}

void setup(void) {
  setupGraphics();
  setupButton();
  Serial.begin(115200);
}

inline void nextGameState() {
  game_state = static_cast<gameState>((game_state + 1) % (gameOver + 1));
}

void drawPlayer(uint16_t y = 0) {
  int16_t y_position_from_ground = kScreenHeight - player_asset.getHeight() - y;
  int16_t player_x = ((kScreenWidth / 3) - (player_asset.getWidth() / 2));
  u8g2.drawXBM(
    player_x,
    y_position_from_ground,
    player_asset.getWidth(),
    player_asset.getHeight(),
    player_asset.getBitmap());
}

void drawObstacles() {
  // TODO: draw obstacles according to the values in some array
}

void drawBootupScreen() {
  int16_t logoMiddleX = (kScreenWidth / 2) - (bootup_width / 2);
  int16_t logoMiddleY = (kScreenHeight / 2) - (bootup_height / 2);
  u8g2.drawXBM(
    logoMiddleX,
    logoMiddleY,
    bootup_width,
    bootup_height,
    bootup_bits);
}

void drawGameOver() {
  static const uint8_t x_pos_gameover = 20;
  static const uint8_t y_pos_gameover = 20;
  u8g2.drawStr(x_pos_gameover, y_pos_gameover, "GAME OVER");
  // TODO: draw fancy asset?
}

void drawScore() {
  static const uint8_t kScoreX = 20;
  static const uint8_t kScoreY = 60;
  static const size_t kScoreBufferSize = 20;
  char score_buffer[kScoreBufferSize];
  snprintf(score_buffer, kScoreBufferSize, "Score: %u", player.getScore());
  u8g2.drawStr(kScoreX, kScoreY, score_buffer);
}

void drawHiscoreScreen() {
  static const uint8_t kHiscoreX = 0;
  static const uint8_t kHiscoreY = 20;
  u8g2.drawStr(kHiscoreX, kHiscoreY, "HI SCORES");
  // TODO: draw hi scores
}

bool collisionDetected() {
  /*
  if player_right_x_position >= obstacle_left_x_position and
    player_left_x_position <= obstacle_right_x_position
    player_bottom_y_position <= obstacle_top_y_position
  */

  // TODO: implement algorithm, remove auto logic
  return player.getScore() > (unsigned)random(100, 250);
}

void updateObstaclePosition() {
  // TODO
}

void resetGame() {
  player = Player();
}

void updateGameLogic(bool button_pressed) {
  switch (game_state) {
    case start:
      {
        if (button_pressed) {
          nextGameState();
        }
        break;
      }
    case hiscore:
      {
        if (button_pressed) {
          nextGameState();
        }
        break;
      }
    case play:
      {
        if (button_pressed) {
          if (player.onGround()) {
            player.jump();
          }
        }

        player.updateYPosition();
        updateObstaclePosition();

        if (collisionDetected()) {
          nextGameState();
          break;
        }
        // happens after collision detection, so score will only get higher
        // if player does not collide
        player.updateScore();
        break;
      }
    case gameOver:
      {
        if (button_pressed) {
          resetGame();
          nextGameState();
        }
        break;
      }
    default:
      {
        Serial.println("Invalid game state");
        break;
      }
  }
}

void updateGameGraphics() {
  // clear buffer to start drawing
  u8g2.clearBuffer();

  // update graphics
  switch (game_state) {
    case start:
      {
        drawBootupScreen();
        break;
      }
    case hiscore:
      {
        drawHiscoreScreen();
        break;
      }
    case play:
      {
        drawPlayer(player.getYPosition());
        drawObstacles();
        break;
      }
    case gameOver:
      {
        drawGameOver();
        drawScore();
        break;
      }
    default:
      {
        Serial.println("invalid game state");
        break;
      }
  }

  // draw everything on the screen
  u8g2.sendBuffer();
}

void loop(void) {
  // In the loop so it's only true once, after this it's false again.
  // This is so it won't think the button is pressed again every loop.
  bool buttonPressed = false;

  if (button.update()) {
    buttonPressed = button.read();
    Serial.println("Button update");
  }

  updateGameLogic(buttonPressed);
  updateGameGraphics();
}
<|endoftext|>


<commit_before><commit_msg>Added C++ version of tinyTest<commit_after><|endoftext|>


<commit_before>/*******************************************************************************/
/* Bela Csound Rendering functions                                             */
/*                                                                           */
/*******************************************************************************/

#include <Bela.h>
#include <csound/csound.hpp>
#include <vector>
#include <sstream>

static int OpenMidiInDevice(CSOUND *csound, void **userData, const char *dev);
static int CloseMidiInDevice(CSOUND *csound, void *userData);
static int ReadMidiData(CSOUND *csound, void *userData, unsigned char *mbuf,
			int nbytes);

struct csChan {
  std::vector<MYFLT> data;
  std::stringstream name;
};

struct csData{
  Csound *csound;
  int blocksize;
  int res;
  int count;
  csChan channel[8];
};

csData* gCsData;

bool setup(BelaContext *context, void *userData)
{
  Csound *csound = new Csound();
  const char *csdfile = "my.csd"; /* CSD name */
  int numArgs = 8;
  char *args[] = { "csound", csdfile, "-iadc", "-odac", "-+rtaudio=null",
		   "--realtime", "--daemon", "-Mhw:1,0,0"};

  gCsData  = new csData;
  csound->SetHostImplementedAudioIO(1,0);
  csound>SetHostImplementedMIDIIO(1);
  csound->SetExternalMidiInOpenCallback(OpenMidiInDevice);
  csound->SetExternalMidiReadCallback(ReadMidiData);
  csound->SetExternalMidiInCloseCallback(CloseMidiInDevice);
  gCsData->res = csound->Compile(numArgs, args);
  gCsData->csound = csound;
  gCsData->blocksize =
    csound->GetKsmps()*userData->csound->GetNchnls();
  gCsData->count = 0;

  /* set up the channels */
  for(int i; i < context->analogInChannels; i++) {
    gCsData.channel[i].data.resize(csound->GetKsmps());
    gCsData.channel[i].name << "analogue" << i+1;
  }

  if(gCsData->res != 0) return false;
  else return true;
}

void render(BelaContext *context, void *Data)
{
  csData *userData = gCsData;
  if(gCsData->res == 0) {
    int n,i,k,count, frmcount,blocksize,res;
    MYFLT scal = userData->csound->Get0dBFS();
    MYFLT* audioIn = userData->csound->GetSpin();
    MYFLT* audioOut = userData->csound->GetSpout();
    int nchnls = userData->csound->GetNchnls();
    int chns = nchnls;
    int an_chns = context->analogInChannels;
    CsChan *channel = userData->channel;
    float frm = 0, incr = ((float) context->analogFrames)/context->audioFrames;
    int an_chans = context->analogInChannels;
    count = userData->count;
    blocksize = userData->blocksize;

    /* this is called when Csound is not running */
    if(count < 0) {
      for(n = 0; n < context->audioFrames; n++){
	for(i = 0; i < context->audioOutChannels; i++){
	  audioWrite(context,n,i,0);
	}
      }
      return;
    }

    if(chns > context->audioOutChannels)
      chns = context->audioOutChannels;

    /* this is where Csound is called */
    for(n = 0; n < context->audioFrames; n++, frm+=incr, count+=nchnls){
      if(count == blocksize) {
	/* set the channels */
	for(i = 0; i < an_chns; i++) {
          csound->SetChannel(channel[i].name.str().c_str(),
			     &(channel[i].data[0]));
	}
	/* run csound */
	if((res = userData->csound->PerformKsmps()) == 0) count = 0;
	else {
	  count = -1;
	  break;
	}
      }
      /* read/write audio data */
      for(i = 0; i < chns; i++){
	audioIn[count+i] = audioRead(context,n,i);
	audioWrite(context,n,i,audioOut[count+i]/scal);
      }

      /* read analogue data
         analogue frame pos gets incremented according to the
         ratio analogFrames/audioFrames.
      */
      frmcount = count/nchnls;
      for(i = 0; i < an_chns; i++) {
	k = (int) frm;
        channel[i].data[frmcount] = analogRead(context,k,i);
      }
    }
    gCsData->res = res;
    userData->count = count;
  }
}

void cleanup(BelaContext *context, void *Data)
{
  csData *userData = gCsData;
  delete userData->csound;
  delete userData;
}

/** MIDI Input functions
 */
int OpenMidiInDevice(CSOUND *csound, void **userData, const char *dev) {
  CsMIDI *midiData = new CsMIDI;
  Midi &midi = midiData->midi;
  midi.readFrom(dev);
  midi.enableParser(false);
  *userData = (void *) midiData;
}

int CloseMidiInDevice(CSOUND *csound, void *userData) {
  CsMIDI *midiData = (CsMIDI *) userData;
  delete midiData;
}

int ReadMidiData(CSOUND *csound, void *userData,
                         unsigned char *mbuf, int nbytes) {
  int n = 0;
  CsMIDI *midiData = (CsMIDI *) userData;
  Midi &midi = midiData->midi;

  while((byte = midi.getInput()) > 0) {
    *mbuf++ = (unsigned char) byte;
    if(++n == nbytes) break;
  }

  return n;
}
<commit_msg>bela MIDI<commit_after>/*******************************************************************************/
/* Bela Csound Rendering functions                                             */
/*                                                                           */
/*******************************************************************************/

#include <Bela.h>
#include <csound/csound.hpp>
#include <vector>
#include <sstream>

static int OpenMidiInDevice(CSOUND *csound, void **userData, const char *dev);
static int CloseMidiInDevice(CSOUND *csound, void *userData);
static int ReadMidiData(CSOUND *csound, void *userData, unsigned char *mbuf,
			int nbytes);

struct csChan {
  std::vector<MYFLT> data;
  std::stringstream name;
};

struct csData{
  Csound *csound;
  int blocksize;
  int res;
  int count;
  csChan channel[8];
};

struct CsMIDI {
  Midi midi;
}

csData* gCsData;

bool setup(BelaContext *context, void *userData)
{
  Csound *csound = new Csound();
  const char *csdfile = "my.csd"; /* CSD name */
  int numArgs = 8;
  char *args[] = { "csound", csdfile, "-iadc", "-odac", "-+rtaudio=null",
		   "--realtime", "--daemon", "-Mhw:1,0,0"};

  gCsData  = new csData;
  csound->SetHostImplementedAudioIO(1,0);
  csound>SetHostImplementedMIDIIO(1);
  csound->SetExternalMidiInOpenCallback(OpenMidiInDevice);
  csound->SetExternalMidiReadCallback(ReadMidiData);
  csound->SetExternalMidiInCloseCallback(CloseMidiInDevice);
  gCsData->res = csound->Compile(numArgs, args);
  gCsData->csound = csound;
  gCsData->blocksize =
    csound->GetKsmps()*userData->csound->GetNchnls();
  gCsData->count = 0;

  /* set up the channels */
  for(int i; i < context->analogInChannels; i++) {
    gCsData.channel[i].data.resize(csound->GetKsmps());
    gCsData.channel[i].name << "analogue" << i+1;
  }

  if(gCsData->res != 0) return false;
  else return true;
}

void render(BelaContext *context, void *Data)
{
  csData *userData = gCsData;
  if(gCsData->res == 0) {
    int n,i,k,count, frmcount,blocksize,res;
    MYFLT scal = userData->csound->Get0dBFS();
    MYFLT* audioIn = userData->csound->GetSpin();
    MYFLT* audioOut = userData->csound->GetSpout();
    int nchnls = userData->csound->GetNchnls();
    int chns = nchnls;
    int an_chns = context->analogInChannels;
    CsChan *channel = userData->channel;
    float frm = 0, incr = ((float) context->analogFrames)/context->audioFrames;
    int an_chans = context->analogInChannels;
    count = userData->count;
    blocksize = userData->blocksize;

    /* this is called when Csound is not running */
    if(count < 0) {
      for(n = 0; n < context->audioFrames; n++){
	for(i = 0; i < context->audioOutChannels; i++){
	  audioWrite(context,n,i,0);
	}
      }
      return;
    }

    if(chns > context->audioOutChannels)
      chns = context->audioOutChannels;

    /* this is where Csound is called */
    for(n = 0; n < context->audioFrames; n++, frm+=incr, count+=nchnls){
      if(count == blocksize) {
	/* set the channels */
	for(i = 0; i < an_chns; i++) {
          csound->SetChannel(channel[i].name.str().c_str(),
			     &(channel[i].data[0]));
	}
	/* run csound */
	if((res = userData->csound->PerformKsmps()) == 0) count = 0;
	else {
	  count = -1;
	  break;
	}
      }
      /* read/write audio data */
      for(i = 0; i < chns; i++){
	audioIn[count+i] = audioRead(context,n,i);
	audioWrite(context,n,i,audioOut[count+i]/scal);
      }

      /* read analogue data
         analogue frame pos gets incremented according to the
         ratio analogFrames/audioFrames.
      */
      frmcount = count/nchnls;
      for(i = 0; i < an_chns; i++) {
	k = (int) frm;
        channel[i].data[frmcount] = analogRead(context,k,i);
      }
    }
    gCsData->res = res;
    userData->count = count;
  }
}

void cleanup(BelaContext *context, void *Data)
{
  csData *userData = gCsData;
  delete userData->csound;
  delete userData;
}

/** MIDI Input functions
 */
int OpenMidiInDevice(CSOUND *csound, void **userData, const char *dev) {
  CsMIDI *midiData = new CsMIDI;
  Midi &midi = midiData->midi;
  midi.readFrom(dev);
  midi.enableParser(false);
  *userData = (void *) midiData;
}

int CloseMidiInDevice(CSOUND *csound, void *userData) {
  CsMIDI *midiData = (CsMIDI *) userData;
  delete midiData;
}

int ReadMidiData(CSOUND *csound, void *userData,
                         unsigned char *mbuf, int nbytes) {
  int n = 0;
  CsMIDI *midiData = (CsMIDI *) userData;
  Midi &midi = midiData->midi;

  while((byte = midi.getInput()) > 0) {
    *mbuf++ = (unsigned char) byte;
    if(++n == nbytes) break;
  }

  return n;
}
<|endoftext|>


<commit_before><commit_msg>Add suffix array<commit_after><|endoftext|>


<commit_before>// Time:  O(1)
// Space: O(1)

class Solution {
public:
    /*
     * @param a: The first integer
     * @param b: The second integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        while (b != 0) {
            int carry = a & b;
            a ^= b;
            b = carry << 1;
        }
        return a;
    }
};

class Solution2 {
public:
    /*
     * @param a: The first integer
     * @param b: The second integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        if (b == 0) {
            return a;
        }
        return aplusb(a ^ b, (a & b) << 1);
    }
};
<commit_msg>Update a-b-problem.cpp<commit_after>// Time:  O(logn) = O(32)
// Space: O(1)

class Solution {
public:
    /*
     * @param a: The first integer
     * @param b: The second integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        while (b != 0) {
            int carry = a & b;
            a ^= b;
            b = carry << 1;
        }
        return a;
    }
};

class Solution2 {
public:
    /*
     * @param a: The first integer
     * @param b: The second integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        if (b == 0) {
            return a;
        }
        return aplusb(a ^ b, (a & b) << 1);
    }
};
<|endoftext|>


<commit_before>/************************************************************************************\
This source file is part of the KS(X) audio library                                  *
For latest info, see http://code.google.com/p/libxal/                                *
**************************************************************************************
Copyright (c) 2010 Kresimir Spes (kreso@cateia.com), Boris Mikic                     *
*                                                                                *
* This program is free software; you can redistribute it and/or modify it under      *
* the terms of the BSD license: http://www.opensource.org/licenses/bsd-license.php   *
\************************************************************************************/
#include <hltypes/hstring.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#ifndef __APPLE__
#include <AL/al.h>
#include <AL/alc.h>
#else
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif

#include "AudioManager.h"
#include "Category.h"
#include "Mutex.h"
#include "SimpleSound.h"
#include "SoundBuffer.h"
#include "Source.h"
#include "StreamSound.h"
#include "Thread.h"
#include "Util.h"

namespace xal
{
/******* GLOBAL ********************************************************/

	AudioManager* mgr;

	void init(chstr deviceName, bool threaded, float updateTime)
	{
		mgr = new AudioManager();
		mgr->init(deviceName, threaded, updateTime);
	}

	void destroy()
	{
		delete mgr;
	}

	void xal_writelog(chstr text)
	{
		printf("%s\n", text.c_str());
	}

	void (*gLogFunction)(chstr) = xal_writelog;
	ALCdevice* gDevice;
	ALCcontext* gContext;

	void setLogFunction(void (*function)(chstr))
	{
		gLogFunction = function;
	}

/******* CONSTRUCT / DESTRUCT ******************************************/

	AudioManager::AudioManager() : deviceName(""), updateTime(0.01f),
		sources(harray<Source*>()), gain(1.0f),
		categories(std::map<hstr, Category*>()),
		sounds(std::map<hstr, SoundBuffer*>()), thread(NULL), mutex(NULL)
	{
	}

	void AudioManager::init(chstr deviceName, bool threaded, float updateTime)
	{
		this->logMessage("Initializing XAL");
		if (deviceName == "nosound")
		{
			this->deviceName = "nosound";
			this->logMessage("- Audio is disabled");
			return;
		}
		this->logMessage("Initializing OpenAL");
		ALCdevice* currentDevice = alcOpenDevice(deviceName.c_str());
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		this->deviceName = alcGetString(currentDevice, ALC_DEVICE_SPECIFIER);
		this->logMessage("Audio device: " + this->deviceName);
		ALCcontext* currentContext = alcCreateContext(currentDevice, NULL);
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		alcMakeContextCurrent(currentContext);
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		alGenSources(XAL_MAX_SOURCES, this->sourceIds);
		gDevice = currentDevice;
		gContext = currentContext;
		this->deviceName = deviceName;
		if (threaded)
		{
			this->logMessage("Starting Thread Management");
			this->updateTime = updateTime;
			this->mutex = new Mutex();
			this->thread = new Thread();
			this->thread->start();
		}
	}

	AudioManager::~AudioManager()
	{
		if (this->mutex != NULL)
		{
			this->mutex->lock();
			this->thread->join();
			this->mutex->unlock();
			delete this->thread;
			delete this->mutex;
		}
		for (std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin(); it != this->sounds.end(); it++)
		{
			delete it->second;
		}
		for (std::map<hstr, Category*>::iterator it = this->categories.begin(); it != this->categories.end(); it++)
		{
			delete it->second;
		}
		this->logMessage("Destroying OpenAL");
		if (gDevice)
		{
			while (this->sources.size() > 0)
			{
				this->sources[0]->unlock();
				this->sources[0]->stop();
				this->destroySource(this->sources[0]);
			}
			alDeleteSources(XAL_MAX_SOURCES, this->sourceIds);
			alcMakeContextCurrent(NULL);
			alcDestroyContext(gContext);
			alcCloseDevice(gDevice);
		}
	}

/******* PROPERTIES ****************************************************/

	void AudioManager::logMessage(chstr message)
	{
		gLogFunction(message);
	}

	bool AudioManager::isEnabled()
	{
		return (gDevice != NULL);
	}

	void AudioManager::update()
	{
		this->update(this->updateTime);
	}

	void AudioManager::update(float k)
	{
		if (this->deviceName != "nosound")
		{
			for (Source** it = this->sources.iterate(); it; it = this->sources.next())
			{
				(*it)->update(k);
			}
			harray<Source*> sources(this->sources);
			for (Source** it = sources.iterate(); it; it = sources.next())
			{
				if (!(*it)->isBound())
				{
					(*it)->getSound()->unbindSource(*it);
					this->destroySource(*it);
				}
			}
		}
	}

	unsigned int AudioManager::allocateSourceId()
	{
		harray<unsigned int> allocated;
		unsigned int id = 0;
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			id = (*it)->getSourceId();
			if (id != 0)
			{
				allocated += id;
			}
		}
		harray<unsigned int> unallocated(this->sourceIds, XAL_MAX_SOURCES);
		unallocated -= allocated;
		if (unallocated.size() > 0)
		{
			return unallocated[0];
		}
		this->logMessage("Audio Manager: Unable to allocate audio source!");
		return 0;
	}

	Source* AudioManager::createSource(SoundBuffer* sound)
	{
		Source* source = new Source(sound);
		this->sources += source;
		return source;
	}

	void AudioManager::destroySource(Source* source)
	{
		this->sources -= source;
		delete source;
	}

	Sound* AudioManager::getSound(chstr name)
	{
		if (this->sounds.find(name) == this->sounds.end())
		{
			return NULL;
		}
		return this->sounds[name];
	}

	Sound* AudioManager::createSound(chstr filename, chstr categoryName, chstr prefix)
	{
		Category* category = this->getCategoryByName(categoryName);
		SoundBuffer* sound;
		if (category->isStreamed())
		{
			sound = new StreamSound(filename, categoryName, prefix);
		}
		else
		{
			sound = new SimpleSound(filename, categoryName, prefix);
		}
		if (!sound->load())
		{
			return NULL;
		}
		this->sounds[sound->getName()] = sound;
		return sound;
	}

	harray<hstr> AudioManager::createSoundsFromPath(chstr path, chstr prefix)
	{
		harray<hstr> result;
		hstr category;
		harray<hstr> dirs = getPathDirectories(path);
		for (hstr* it = dirs.iterate(); it; it = dirs.next())
		{
			category = (*it).rsplit("/").pop_back();
			result += this->createSoundsFromPath(hsprintf("%s/%s", path.c_str(), (*it).c_str()), category, prefix);
		}
		return result;
	}

	harray<hstr> AudioManager::createSoundsFromPath(chstr path, chstr category, chstr prefix)
	{
		this->createCategory(category);
		harray<hstr> result;
		harray<hstr> files = getPathFilesRecursive(path);
		SoundBuffer* sound;
		for (hstr* it = files.iterate(); it; it = files.next())
		{
			sound = (SoundBuffer*)this->createSound(hsprintf("%s/%s", path.c_str(), (*it).c_str()), category, prefix);
			if (sound != NULL)
			{
				result += sound->getName();
			}
		}
		return result;
	}

	void AudioManager::destroySound(SoundBuffer* sound)
	{
		std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin();
		for (;it != this->sounds.end(); it++)
		{
			if (it->second == sound)
			{
				this->sounds.erase(it);
				delete it->second;
				break;
			}
		}
	}

	void AudioManager::destroySoundsWithPrefix(chstr prefix)
	{
		harray<hstr> deleteList;
		std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin();
		for (;it != this->sounds.end(); it++)
		{
			if (it->first.starts_with(prefix))
			{
				delete it->second;
				deleteList.push_back(it->first);
			}
		}
		for (hstr* it = deleteList.iterate(); it ; it = deleteList.next())
		{
			this->sounds.erase(*it);
		}
	}

	void AudioManager::createCategory(chstr name, bool streamed)
	{
		if (this->categories.find(name) == this->categories.end())
		{
			this->categories[name] = new Category(name, streamed);
		}
	}

	Category* AudioManager::getCategoryByName(chstr name)
	{
		if (this->categories.find(name) == this->categories.end())
		{
			throw ("Audio Manager: Category '" + name + "' does not exist!").c_str();
		}
		return this->categories[name];
	}

	void AudioManager::setCategoryGain(chstr name, float gain)
	{
		this->getCategoryByName(name)->setGain(gain);
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			if ((*it)->getSound()->getCategory()->getName() == name)
			{
				alSourcef((*it)->getSourceId(), AL_GAIN, gain * (*it)->getGain() * this->gain);
			}
		}
	}

	void AudioManager::stopAll(float fadeTime)
	{
		while (this->sources.size() > 0)
		{
			this->sources[0]->unlock();
			this->sources[0]->stop(fadeTime);
			this->destroySource(this->sources[0]);
		}
	}

	void AudioManager::stopCategory(chstr category, float fadeTime)
	{
		harray<Source*> sources(this->sources);
		for (Source** it = sources.iterate(); it; it = sources.next())
		{
			if ((*it)->getSound()->getCategory()->getName() == category)
			{
				(*it)->unlock();
				(*it)->stop(fadeTime);
				this->destroySource(*it);
			}
		}
	}

}
<commit_msg>- fixed problems with AudioManager::stopAll and AudioManager::stopCategory<commit_after>/************************************************************************************\
This source file is part of the KS(X) audio library                                  *
For latest info, see http://code.google.com/p/libxal/                                *
**************************************************************************************
Copyright (c) 2010 Kresimir Spes (kreso@cateia.com), Boris Mikic                     *
*                                                                                *
* This program is free software; you can redistribute it and/or modify it under      *
* the terms of the BSD license: http://www.opensource.org/licenses/bsd-license.php   *
\************************************************************************************/
#include <hltypes/hstring.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#ifndef __APPLE__
#include <AL/al.h>
#include <AL/alc.h>
#else
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif

#include "AudioManager.h"
#include "Category.h"
#include "Mutex.h"
#include "SimpleSound.h"
#include "SoundBuffer.h"
#include "Source.h"
#include "StreamSound.h"
#include "Thread.h"
#include "Util.h"

namespace xal
{
/******* GLOBAL ********************************************************/

	AudioManager* mgr;

	void init(chstr deviceName, bool threaded, float updateTime)
	{
		mgr = new AudioManager();
		mgr->init(deviceName, threaded, updateTime);
	}

	void destroy()
	{
		delete mgr;
	}

	void xal_writelog(chstr text)
	{
		printf("%s\n", text.c_str());
	}

	void (*gLogFunction)(chstr) = xal_writelog;
	ALCdevice* gDevice;
	ALCcontext* gContext;

	void setLogFunction(void (*function)(chstr))
	{
		gLogFunction = function;
	}

/******* CONSTRUCT / DESTRUCT ******************************************/

	AudioManager::AudioManager() : deviceName(""), updateTime(0.01f),
		sources(harray<Source*>()), gain(1.0f),
		categories(std::map<hstr, Category*>()),
		sounds(std::map<hstr, SoundBuffer*>()), thread(NULL), mutex(NULL)
	{
	}

	void AudioManager::init(chstr deviceName, bool threaded, float updateTime)
	{
		this->logMessage("Initializing XAL");
		if (deviceName == "nosound")
		{
			this->deviceName = "nosound";
			this->logMessage("- Audio is disabled");
			return;
		}
		this->logMessage("Initializing OpenAL");
		ALCdevice* currentDevice = alcOpenDevice(deviceName.c_str());
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		this->deviceName = alcGetString(currentDevice, ALC_DEVICE_SPECIFIER);
		this->logMessage("Audio device: " + this->deviceName);
		ALCcontext* currentContext = alcCreateContext(currentDevice, NULL);
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		alcMakeContextCurrent(currentContext);
		if (alcGetError(currentDevice) != ALC_NO_ERROR)
		{
			return;
		}
		alGenSources(XAL_MAX_SOURCES, this->sourceIds);
		gDevice = currentDevice;
		gContext = currentContext;
		this->deviceName = deviceName;
		if (threaded)
		{
			this->logMessage("Starting Thread Management");
			this->updateTime = updateTime;
			this->mutex = new Mutex();
			this->thread = new Thread();
			this->thread->start();
		}
	}

	AudioManager::~AudioManager()
	{
		if (this->mutex != NULL)
		{
			this->mutex->lock();
			this->thread->join();
			this->mutex->unlock();
			delete this->thread;
			delete this->mutex;
		}
		for (std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin(); it != this->sounds.end(); it++)
		{
			delete it->second;
		}
		for (std::map<hstr, Category*>::iterator it = this->categories.begin(); it != this->categories.end(); it++)
		{
			delete it->second;
		}
		this->logMessage("Destroying OpenAL");
		if (gDevice)
		{
			while (this->sources.size() > 0)
			{
				this->sources[0]->unlock();
				this->sources[0]->stop();
				this->destroySource(this->sources[0]);
			}
			alDeleteSources(XAL_MAX_SOURCES, this->sourceIds);
			alcMakeContextCurrent(NULL);
			alcDestroyContext(gContext);
			alcCloseDevice(gDevice);
		}
	}

/******* PROPERTIES ****************************************************/

	void AudioManager::logMessage(chstr message)
	{
		gLogFunction(message);
	}

	bool AudioManager::isEnabled()
	{
		return (gDevice != NULL);
	}

	void AudioManager::update()
	{
		this->update(this->updateTime);
	}

	void AudioManager::update(float k)
	{
		if (this->deviceName != "nosound")
		{
			for (Source** it = this->sources.iterate(); it; it = this->sources.next())
			{
				(*it)->update(k);
			}
			harray<Source*> sources(this->sources);
			for (Source** it = sources.iterate(); it; it = sources.next())
			{
				if (!(*it)->isBound())
				{
					(*it)->getSound()->unbindSource(*it);
					this->destroySource(*it);
				}
			}
		}
	}

	unsigned int AudioManager::allocateSourceId()
	{
		harray<unsigned int> allocated;
		unsigned int id = 0;
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			id = (*it)->getSourceId();
			if (id != 0)
			{
				allocated += id;
			}
		}
		harray<unsigned int> unallocated(this->sourceIds, XAL_MAX_SOURCES);
		unallocated -= allocated;
		if (unallocated.size() > 0)
		{
			return unallocated[0];
		}
		this->logMessage("Audio Manager: Unable to allocate audio source!");
		return 0;
	}

	Source* AudioManager::createSource(SoundBuffer* sound)
	{
		Source* source = new Source(sound);
		this->sources += source;
		return source;
	}

	void AudioManager::destroySource(Source* source)
	{
		this->sources -= source;
		delete source;
	}

	Sound* AudioManager::getSound(chstr name)
	{
		if (this->sounds.find(name) == this->sounds.end())
		{
			return NULL;
		}
		return this->sounds[name];
	}

	Sound* AudioManager::createSound(chstr filename, chstr categoryName, chstr prefix)
	{
		Category* category = this->getCategoryByName(categoryName);
		SoundBuffer* sound;
		if (category->isStreamed())
		{
			sound = new StreamSound(filename, categoryName, prefix);
		}
		else
		{
			sound = new SimpleSound(filename, categoryName, prefix);
		}
		if (!sound->load())
		{
			return NULL;
		}
		this->sounds[sound->getName()] = sound;
		return sound;
	}

	harray<hstr> AudioManager::createSoundsFromPath(chstr path, chstr prefix)
	{
		harray<hstr> result;
		hstr category;
		harray<hstr> dirs = getPathDirectories(path);
		for (hstr* it = dirs.iterate(); it; it = dirs.next())
		{
			category = (*it).rsplit("/").pop_back();
			result += this->createSoundsFromPath(hsprintf("%s/%s", path.c_str(), (*it).c_str()), category, prefix);
		}
		return result;
	}

	harray<hstr> AudioManager::createSoundsFromPath(chstr path, chstr category, chstr prefix)
	{
		this->createCategory(category);
		harray<hstr> result;
		harray<hstr> files = getPathFilesRecursive(path);
		SoundBuffer* sound;
		for (hstr* it = files.iterate(); it; it = files.next())
		{
			sound = (SoundBuffer*)this->createSound(hsprintf("%s/%s", path.c_str(), (*it).c_str()), category, prefix);
			if (sound != NULL)
			{
				result += sound->getName();
			}
		}
		return result;
	}

	void AudioManager::destroySound(SoundBuffer* sound)
	{
		std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin();
		for (;it != this->sounds.end(); it++)
		{
			if (it->second == sound)
			{
				this->sounds.erase(it);
				delete it->second;
				break;
			}
		}
	}

	void AudioManager::destroySoundsWithPrefix(chstr prefix)
	{
		harray<hstr> deleteList;
		std::map<hstr, SoundBuffer*>::iterator it = this->sounds.begin();
		for (;it != this->sounds.end(); it++)
		{
			if (it->first.starts_with(prefix))
			{
				delete it->second;
				deleteList.push_back(it->first);
			}
		}
		for (hstr* it = deleteList.iterate(); it ; it = deleteList.next())
		{
			this->sounds.erase(*it);
		}
	}

	void AudioManager::createCategory(chstr name, bool streamed)
	{
		if (this->categories.find(name) == this->categories.end())
		{
			this->categories[name] = new Category(name, streamed);
		}
	}

	Category* AudioManager::getCategoryByName(chstr name)
	{
		if (this->categories.find(name) == this->categories.end())
		{
			throw ("Audio Manager: Category '" + name + "' does not exist!").c_str();
		}
		return this->categories[name];
	}

	void AudioManager::setCategoryGain(chstr name, float gain)
	{
		this->getCategoryByName(name)->setGain(gain);
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			if ((*it)->getSound()->getCategory()->getName() == name)
			{
				alSourcef((*it)->getSourceId(), AL_GAIN, gain * (*it)->getGain() * this->gain);
			}
		}
	}

	void AudioManager::stopAll(float fadeTime)
	{
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			(*it)->unlock();
			(*it)->stop(fadeTime);
		}
	}

	void AudioManager::stopCategory(chstr category, float fadeTime)
	{
		for (Source** it = this->sources.iterate(); it; it = this->sources.next())
		{
			if ((*it)->getSound()->getCategory()->getName() == category)
			{
				(*it)->unlock();
				(*it)->stop(fadeTime);
			}
		}
	}

}
<|endoftext|>


<commit_before>
#include "stdafx.h"
#include "common.h"
#include "util\fixes.h"
#include "python\pythonglobal.h"
#include "python\python_header.h"
#include "obj.h"
#include "d20.h"
#include "gamesystems/objects/objsystem.h"
#include "condition.h"
#include "critter.h"
#include "combat.h"
#include "gamesystems/gamesystems.h"
#include "history.h"
#include "python/python_integration_spells.h"
#include "gamesystems/objects/objevent.h"
#include "gamesystems/particlesystems.h"
#include "damage.h"

void PyPerformTouchAttack_PatchedCallToHitProcessing(D20Actn * pd20A, D20Actn d20A, uint32_t savedesi, uint32_t retaddr, PyObject * pyObjCaller, PyObject * pyTupleArgs);
void enlargeSpellRestoreModelScaleHook(objHndl objHnd);
void enlargeSpellIncreaseModelScaleHook(objHndl objHnd);

class SpellConditionFixes : public TempleFix {
public:
	const char* name() override {
		return "Spell Condition Fixes (for buggy spell effects)";
	}

	void VampiricTouchFix();
	void enlargePersonModelScaleFix(); // fixes ambiguous float point calculations that resulted in cumulative roundoff errors

	static int ImmunityCheckHandler(DispatcherCallbackArgs args);

	static int StinkingCloudObjEvent(DispatcherCallbackArgs args);

	void apply() override {

		VampiricTouchFix();
		enlargePersonModelScaleFix();

		replaceFunction(0x100CAF30, StinkingCloudObjEvent);

		static int (*orgImmunityCheckHandler )(DispatcherCallbackArgs)= replaceFunction<int(__cdecl)(DispatcherCallbackArgs)>(0x100ED650, [](DispatcherCallbackArgs args)
		{
			if (!ImmunityCheckHandler(args))
				return orgImmunityCheckHandler(args);
			return 0;
		});

	}
} spellConditionFixes;

void SpellConditionFixes::VampiricTouchFix()
{
	writeHex(0x102E0A24, "EA 00");
	writeHex(0x102E0A28, "D0 43 0C 10");
	writeHex(0x102E0A2C, "F0 09  2E 10");

	writeHex(0x102E0A8C, "D0 8F 0E 10");

	writeHex(0x102E0AC4, "A6");
	writeHex(0x102E0AC8, "20 76 0D 10");

	writeHex(0x102E0B00, "A6");
	writeHex(0x102E0B04, "B0 BA 0C 10");

	//Perform Touch Attack mod:
	redirectCall(0x100B2CC9, PyPerformTouchAttack_PatchedCallToHitProcessing);
	return;
}

void SpellConditionFixes::enlargePersonModelScaleFix()
{
	redirectCall(0x100CD45C, enlargeSpellIncreaseModelScaleHook);
	redirectCall(0x100D84DE, enlargeSpellRestoreModelScaleHook); // sp152 enlarge
	redirectCall(0x100D9C22, enlargeSpellRestoreModelScaleHook); // sp404 righteous might

}

int SpellConditionFixes::ImmunityCheckHandler(DispatcherCallbackArgs args)
{
	/*
		the dispatch is performed from the functions:
		0x100C3810 CheckSpellResistance
		0x1008D830 PerformCastSpell
	*/
	auto dispIo23 = dispatch.DispIoCheckIoType23(args.dispIO);
	if (dispIo23->returnVal == 1)
		return 1;

	DispIoTypeImmunityTrigger dispIo21;
	dispIo21.condNode = args.subDispNode->condNode;
	auto dispatcher = objSystem->GetObject(args.objHndCaller)->GetDispatcher();
	if (!dispatch.dispatcherValid(dispatcher))
		return 1;

	int immType = 10;
	for (; immType <= 16; immType++){
		dispatch.DispatcherProcessor(dispatcher, dispTypeImmunityTrigger, immType, &dispIo21);
		if (dispIo21.interrupt == 1)
			break;
	}
	if (immType > 16)
		return 1;

	if (immType == 0x10) // immunity special
	{
		if (args.subDispNode->subDispDef->data1 == 0x4) //  necklace of adaptation (NEW!)
		{
			if (dispIo23->spellPkt->spellEnum == 65 || dispIo23->spellPkt->spellEnum == 460) // Cloudkill and Stinking Cloud
			{
				histSys.CreateFromFreeText(fmt::format("{} protected by Necklace of Adaptation!\n",description.getDisplayName(args.objHndCaller )).c_str());
				dispIo23->returnVal = 1;
				return 1;
			}

		}
	}

	if (immType != 10)
		return 0;

	auto immSpellId = conds.CondNodeGetArg(args.subDispNode->condNode, 0);
	SpellPacketBody immSpellPkt;
	if (!spellSys.GetSpellPacketBody(immSpellId, &immSpellPkt))
		return 1;

	if (dispIo23->flag == 0)
		return 0;

	if (immSpellPkt.spellEnum != 368
		&& (immSpellPkt.spellEnum < 370 || immSpellPkt.spellEnum > 372)) {
		// prot from alignment spells
		return 0;
	}
	SpellEntry offendingSpellEntry;
	spellSys.spellRegistryCopy(dispIo23->spellPkt->spellEnum, &offendingSpellEntry);

	if (!(offendingSpellEntry.spellDescriptorBitmask & 0x4000)) //Mind Affecting
		return 0;

	auto caster = dispIo23->spellPkt->caster;

	if (!caster)
		return 0;

	if (!objects.IsCritter(caster))
		return 0;

	if ( offendingSpellEntry.aiTypeBitmask && ( 1<<ai_action_defensive)  )
	{
		if (critterSys.IsFriendly(caster, args.objHndCaller)) // because some spells are actually both ways, such as prayer
			return 1;
	}

	return 0;
}

void PyPerformTouchAttack_PatchedCallToHitProcessing( D20Actn * pd20A, D20Actn d20A, uint32_t savedesi, uint32_t retaddr, PyObject * pyObjCaller, PyObject * pyTupleArgs)
{
	auto tupSize = PyTuple_Size(pyTupleArgs);
	uint32_t shouldPerformMeleeTouchAttack = 0;
	if (tupSize > 1)
	{
		PyObject * pyarg2 = PyTuple_GetItem(pyTupleArgs, 1);
		if (PyType_IsSubtype(pyarg2->ob_type, &PyInt_Type))
		{
			if (PyLong_AsLong( pyarg2) != 0)
			{
				pd20A->d20Caf = D20CAF_TOUCH_ATTACK; // sans D20CAF_RANGED
			}
		}
	}

	d20Sys.ToHitProc(pd20A);
	return;

}

void __cdecl enlargeSpellRestoreModelScaleHook(objHndl objHnd)
{
	// patches for spellRemove function (disgusting hardcoded shit! bah!)
	uint32_t modelScale = objects.getInt32(objHnd, obj_f_model_scale);
	modelScale *= 5;
	modelScale /= 9;
	objects.setInt32(objHnd, obj_f_model_scale, modelScale);
}

void enlargeSpellIncreaseModelScaleHook(objHndl objHnd)
{
	uint32_t modelScale = objects.getInt32(objHnd, obj_f_model_scale);
	modelScale *= 9;
	modelScale /= 5;
	objects.setInt32(objHnd, obj_f_model_scale, modelScale);
}

int SpellConditionFixes::StinkingCloudObjEvent(DispatcherCallbackArgs args)
{
	DispIoObjEvent* dispIo = dispatch.DispIoCheckIoType17(args.dispIO);
	auto condEvtId = args.GetCondArg(2);
	if (dispIo->evtId == condEvtId)	{

		auto spellId = args.GetCondArg(0);
		SpellPacketBody spellPkt(spellId);
		if (!spellPkt.spellId){
			logger->warn("StinkingCloudObjEvent: Unable to fetch spell! ID {}", spellId);
			return 0;
		}

		/*
			AoE Entered;
			 - add the target to the Spell's Target List
			 - Do a saving throw
		*/
		if (args.dispKey == DK_OnEnterAoE && (args.GetData1() == 0))
		{

			pySpellIntegration.SpellSoundPlay(&spellPkt, SpellEvent::SpellStruck);
			pySpellIntegration.SpellTrigger(spellId, SpellEvent::AreaOfEffectHit);

			if (spellSys.CheckSpellResistance(&spellPkt, dispIo->tgt) == 1)
				return 0;

			auto partsysId = gameSystems->GetParticleSys().CreateAtObj("sp-Stinking Cloud Hit", dispIo->tgt);
			spellPkt.AddTarget(dispIo->tgt, partsysId, 1);
			if (damage.SavingThrowSpell(dispIo->tgt, spellPkt.caster, spellPkt.dc, SavingThrowType::Fortitude, 0, spellPkt.spellId ))	{
				/*
					save succeeded; add the "Hit Pre" condition, which will attempt
					to apply the condition in the subsequent turns
				*/
				conds.AddTo(dispIo->tgt, "sp-Stinking Cloud Hit Pre", { static_cast<int>(spellPkt.spellId), spellPkt.durationRemaining, static_cast<int>(dispIo->evtId) });
			} else
			{
				/*
					Save failed; apply the condition
				*/
				conds.AddTo(dispIo->tgt,"sp-Stinking Cloud Hit", { static_cast<int>(spellPkt.spellId), spellPkt.durationRemaining, static_cast<int>(dispIo->evtId), 0 });
			}
		}
		/*
			AoE exited;
			 - If "Hit Pre" (identified by data1 = 223), remove the condition so the character doesn't keep making saves outside the cloud
			 - If "Hit" (identified by data1 = 222), reduce the remaining duration to 1d4+1
		*/
		else if (args.dispKey == DK_OnLeaveAoE)
		{
			if (args.GetData1() == 222) // the sp-Stinking Cloud Hit condition
			{
				args.SetCondArg(3,1);
				auto rollResult = Dice::Roll(1,4,1);
				args.SetCondArg(1, rollResult); // sets the remaining duration to 1d4+1
				histSys.CreateFromFreeText(fmt::format("{} exited Stinking Cloud; Nauseated for {} more rounds.\n", description.getDisplayName(dispIo->tgt), rollResult).c_str());
			}
			else if (args.GetData1() == 223) // the sp-Stinking Cloud Hit Pre condition
			{
				// remove the condition (cloud has been exited)
				// it will get re-added if the target re-enters via this same callback (see above)
				conds.ConditionRemove(dispIo->tgt, args.subDispNode->condNode);
			}

		}

		if (!spellPkt.UpdateSpellsCastRegistry() )	{
			logger->warn("StinkingCloudObjEvent: Unable to save update SpellPacket!");
			return 0;
		}
		pySpellIntegration.UpdateSpell(spellId);
	}
	return 0;

};<commit_msg>Fix for Shocking Grasp doing d8 instead of d6 damage dice<commit_after>
#include "stdafx.h"
#include "common.h"
#include "util\fixes.h"
#include "python\pythonglobal.h"
#include "python\python_header.h"
#include "obj.h"
#include "d20.h"
#include "gamesystems/objects/objsystem.h"
#include "condition.h"
#include "critter.h"
#include "combat.h"
#include "gamesystems/gamesystems.h"
#include "history.h"
#include "python/python_integration_spells.h"
#include "gamesystems/objects/objevent.h"
#include "gamesystems/particlesystems.h"
#include "damage.h"

void PyPerformTouchAttack_PatchedCallToHitProcessing(D20Actn * pd20A, D20Actn d20A, uint32_t savedesi, uint32_t retaddr, PyObject * pyObjCaller, PyObject * pyTupleArgs);
void enlargeSpellRestoreModelScaleHook(objHndl objHnd);
void enlargeSpellIncreaseModelScaleHook(objHndl objHnd);

class SpellConditionFixes : public TempleFix {
public:
	const char* name() override {
		return "Spell Condition Fixes (for buggy spell effects)";
	}

	void VampiricTouchFix();
	void enlargePersonModelScaleFix(); // fixes ambiguous float point calculations that resulted in cumulative roundoff errors

	static int ImmunityCheckHandler(DispatcherCallbackArgs args);

	static int StinkingCloudObjEvent(DispatcherCallbackArgs args);

	void apply() override {

		VampiricTouchFix();
		enlargePersonModelScaleFix();

		replaceFunction(0x100CAF30, StinkingCloudObjEvent);

		static int (*orgImmunityCheckHandler )(DispatcherCallbackArgs)= replaceFunction<int(__cdecl)(DispatcherCallbackArgs)>(0x100ED650, [](DispatcherCallbackArgs args)
		{
			if (!ImmunityCheckHandler(args))
				return orgImmunityCheckHandler(args);
			return 0;
		});

		// Fix for Shocking Grasp doing d8 instead of d6 damage
		char sgWriteVal = 6;
		write(0x100DD9DF + 1, &sgWriteVal, sizeof(char));
	}
} spellConditionFixes;

void SpellConditionFixes::VampiricTouchFix()
{
	writeHex(0x102E0A24, "EA 00");
	writeHex(0x102E0A28, "D0 43 0C 10");
	writeHex(0x102E0A2C, "F0 09  2E 10");

	writeHex(0x102E0A8C, "D0 8F 0E 10");

	writeHex(0x102E0AC4, "A6");
	writeHex(0x102E0AC8, "20 76 0D 10");

	writeHex(0x102E0B00, "A6");
	writeHex(0x102E0B04, "B0 BA 0C 10");

	//Perform Touch Attack mod:
	redirectCall(0x100B2CC9, PyPerformTouchAttack_PatchedCallToHitProcessing);
	return;
}

void SpellConditionFixes::enlargePersonModelScaleFix()
{
	redirectCall(0x100CD45C, enlargeSpellIncreaseModelScaleHook);
	redirectCall(0x100D84DE, enlargeSpellRestoreModelScaleHook); // sp152 enlarge
	redirectCall(0x100D9C22, enlargeSpellRestoreModelScaleHook); // sp404 righteous might

}

int SpellConditionFixes::ImmunityCheckHandler(DispatcherCallbackArgs args)
{
	/*
		the dispatch is performed from the functions:
		0x100C3810 CheckSpellResistance
		0x1008D830 PerformCastSpell
	*/
	auto dispIo23 = dispatch.DispIoCheckIoType23(args.dispIO);
	if (dispIo23->returnVal == 1)
		return 1;

	DispIoTypeImmunityTrigger dispIo21;
	dispIo21.condNode = args.subDispNode->condNode;
	auto dispatcher = objSystem->GetObject(args.objHndCaller)->GetDispatcher();
	if (!dispatch.dispatcherValid(dispatcher))
		return 1;

	int immType = 10;
	for (; immType <= 16; immType++){
		dispatch.DispatcherProcessor(dispatcher, dispTypeImmunityTrigger, immType, &dispIo21);
		if (dispIo21.interrupt == 1)
			break;
	}
	if (immType > 16)
		return 1;

	if (immType == 0x10) // immunity special
	{
		if (args.subDispNode->subDispDef->data1 == 0x4) //  necklace of adaptation (NEW!)
		{
			if (dispIo23->spellPkt->spellEnum == 65 || dispIo23->spellPkt->spellEnum == 460) // Cloudkill and Stinking Cloud
			{
				histSys.CreateFromFreeText(fmt::format("{} protected by Necklace of Adaptation!\n",description.getDisplayName(args.objHndCaller )).c_str());
				dispIo23->returnVal = 1;
				return 1;
			}

		}
	}

	if (immType != 10)
		return 0;

	auto immSpellId = conds.CondNodeGetArg(args.subDispNode->condNode, 0);
	SpellPacketBody immSpellPkt;
	if (!spellSys.GetSpellPacketBody(immSpellId, &immSpellPkt))
		return 1;

	if (dispIo23->flag == 0)
		return 0;

	if (immSpellPkt.spellEnum != 368
		&& (immSpellPkt.spellEnum < 370 || immSpellPkt.spellEnum > 372)) {
		// prot from alignment spells
		return 0;
	}
	SpellEntry offendingSpellEntry;
	spellSys.spellRegistryCopy(dispIo23->spellPkt->spellEnum, &offendingSpellEntry);

	if (!(offendingSpellEntry.spellDescriptorBitmask & 0x4000)) //Mind Affecting
		return 0;

	auto caster = dispIo23->spellPkt->caster;

	if (!caster)
		return 0;

	if (!objects.IsCritter(caster))
		return 0;

	if ( offendingSpellEntry.aiTypeBitmask && ( 1<<ai_action_defensive)  )
	{
		if (critterSys.IsFriendly(caster, args.objHndCaller)) // because some spells are actually both ways, such as prayer
			return 1;
	}

	return 0;
}

void PyPerformTouchAttack_PatchedCallToHitProcessing( D20Actn * pd20A, D20Actn d20A, uint32_t savedesi, uint32_t retaddr, PyObject * pyObjCaller, PyObject * pyTupleArgs)
{
	auto tupSize = PyTuple_Size(pyTupleArgs);
	uint32_t shouldPerformMeleeTouchAttack = 0;
	if (tupSize > 1)
	{
		PyObject * pyarg2 = PyTuple_GetItem(pyTupleArgs, 1);
		if (PyType_IsSubtype(pyarg2->ob_type, &PyInt_Type))
		{
			if (PyLong_AsLong( pyarg2) != 0)
			{
				pd20A->d20Caf = D20CAF_TOUCH_ATTACK; // sans D20CAF_RANGED
			}
		}
	}

	d20Sys.ToHitProc(pd20A);
	return;

}

void __cdecl enlargeSpellRestoreModelScaleHook(objHndl objHnd)
{
	// patches for spellRemove function (disgusting hardcoded shit! bah!)
	uint32_t modelScale = objects.getInt32(objHnd, obj_f_model_scale);
	modelScale *= 5;
	modelScale /= 9;
	objects.setInt32(objHnd, obj_f_model_scale, modelScale);
}

void enlargeSpellIncreaseModelScaleHook(objHndl objHnd)
{
	uint32_t modelScale = objects.getInt32(objHnd, obj_f_model_scale);
	modelScale *= 9;
	modelScale /= 5;
	objects.setInt32(objHnd, obj_f_model_scale, modelScale);
}

int SpellConditionFixes::StinkingCloudObjEvent(DispatcherCallbackArgs args)
{
	DispIoObjEvent* dispIo = dispatch.DispIoCheckIoType17(args.dispIO);
	auto condEvtId = args.GetCondArg(2);
	if (dispIo->evtId == condEvtId)	{

		auto spellId = args.GetCondArg(0);
		SpellPacketBody spellPkt(spellId);
		if (!spellPkt.spellId){
			logger->warn("StinkingCloudObjEvent: Unable to fetch spell! ID {}", spellId);
			return 0;
		}

		/*
			AoE Entered;
			 - add the target to the Spell's Target List
			 - Do a saving throw
		*/
		if (args.dispKey == DK_OnEnterAoE && (args.GetData1() == 0))
		{

			pySpellIntegration.SpellSoundPlay(&spellPkt, SpellEvent::SpellStruck);
			pySpellIntegration.SpellTrigger(spellId, SpellEvent::AreaOfEffectHit);

			if (spellSys.CheckSpellResistance(&spellPkt, dispIo->tgt) == 1)
				return 0;

			auto partsysId = gameSystems->GetParticleSys().CreateAtObj("sp-Stinking Cloud Hit", dispIo->tgt);
			spellPkt.AddTarget(dispIo->tgt, partsysId, 1);
			if (damage.SavingThrowSpell(dispIo->tgt, spellPkt.caster, spellPkt.dc, SavingThrowType::Fortitude, 0, spellPkt.spellId ))	{
				/*
					save succeeded; add the "Hit Pre" condition, which will attempt
					to apply the condition in the subsequent turns
				*/
				conds.AddTo(dispIo->tgt, "sp-Stinking Cloud Hit Pre", { static_cast<int>(spellPkt.spellId), spellPkt.durationRemaining, static_cast<int>(dispIo->evtId) });
			} else
			{
				/*
					Save failed; apply the condition
				*/
				conds.AddTo(dispIo->tgt,"sp-Stinking Cloud Hit", { static_cast<int>(spellPkt.spellId), spellPkt.durationRemaining, static_cast<int>(dispIo->evtId), 0 });
			}
		}
		/*
			AoE exited;
			 - If "Hit Pre" (identified by data1 = 223), remove the condition so the character doesn't keep making saves outside the cloud
			 - If "Hit" (identified by data1 = 222), reduce the remaining duration to 1d4+1
		*/
		else if (args.dispKey == DK_OnLeaveAoE)
		{
			if (args.GetData1() == 222) // the sp-Stinking Cloud Hit condition
			{
				args.SetCondArg(3,1);
				auto rollResult = Dice::Roll(1,4,1);
				args.SetCondArg(1, rollResult); // sets the remaining duration to 1d4+1
				histSys.CreateFromFreeText(fmt::format("{} exited Stinking Cloud; Nauseated for {} more rounds.\n", description.getDisplayName(dispIo->tgt), rollResult).c_str());
			}
			else if (args.GetData1() == 223) // the sp-Stinking Cloud Hit Pre condition
			{
				// remove the condition (cloud has been exited)
				// it will get re-added if the target re-enters via this same callback (see above)
				conds.ConditionRemove(dispIo->tgt, args.subDispNode->condNode);
			}

		}

		if (!spellPkt.UpdateSpellsCastRegistry() )	{
			logger->warn("StinkingCloudObjEvent: Unable to save update SpellPacket!");
			return 0;
		}
		pySpellIntegration.UpdateSpell(spellId);
	}
	return 0;

};<|endoftext|>