<commit_before>#include <string>
#include <vector>

using namespace std;

#define ADD_VECTOR_HEADERS(t, tn) \
    typedef vector< t > vector_##t; \
    vector_##t * create_std_vector##tn(); \
    vector_##t * carray_to_std_vector##tn( t * a, size_t len ); \
    t * std_vector##tn##_to_carray( vector_##t * v ); \
    size_t std_vector##tn##_length( vector_##t * v); \
    void destroy_std_vector##tn( vector_##t * v); \
    void delete_std_vector##tn( vector_##t * v);

#define ADD_VECTOR_IMPL(t, tn) \
    vector_##t * create_std_vector##tn() { \
        return new vector_##t;\
    }\
    \
    vector_##t * carray_to_std_vector##tn( t * a, size_t len ) {\
        vector_##t * v = new vector_##t;\
        for(size_t i = 0; i < len; i++) \
            v->push_back(a[i]);\
        return v;\
    }\
    \
    t * std_vector##tn##_to_carray( vector_##t * v ) {\
        return v->data();\
    }\
    \
    size_t std_vector##tn##_length( vector_##t * v) { \
        return v->size();\
    } \
    void destroy_std_vector##tn( vector_##t * v) { \
        v->~vector_##t();\
    }\
    void delete_std_vector##tn( vector_##t * v) { \
        delete v;\
    }\

extern "C" {
string* create_std_string();
string* cstring_to_std_string(char* s, size_t len);
const char*   std_string_to_cstring(string* s);

ADD_VECTOR_HEADERS(int, i);
ADD_VECTOR_HEADERS(float, f);
ADD_VECTOR_HEADERS(double, d);

}
<commit_msg>Fixed some formatting.<commit_after>#include <string>
#include <vector>

using namespace std;

#define ADD_VECTOR_HEADERS(t, tn) \
    typedef vector< t > vector_##t; \
    vector_##t * create_std_vector##tn(); \
    vector_##t * carray_to_std_vector##tn( t * a, size_t len ); \
    t * std_vector##tn##_to_carray( vector_##t * v ); \
    size_t std_vector##tn##_length( vector_##t * v); \
    void destroy_std_vector##tn( vector_##t * v); \
    void delete_std_vector##tn( vector_##t * v);

#define ADD_VECTOR_IMPL(t, tn) \
    vector_##t * create_std_vector##tn() { \
        return new vector_##t;\
    }\
    vector_##t * carray_to_std_vector##tn( t * a, size_t len ) {\
        vector_##t * v = new vector_##t;\
        for(size_t i = 0; i < len; i++) \
            v->push_back(a[i]);\
        return v;\
    }\
    t * std_vector##tn##_to_carray( vector_##t * v ) {\
        return v->data();\
    }\
    size_t std_vector##tn##_length( vector_##t * v) { \
        return v->size();\
    } \
    void destroy_std_vector##tn( vector_##t * v) { \
        v->~vector_##t();\
    }\
    void delete_std_vector##tn( vector_##t * v) { \
        delete v;\
    }\

extern "C" {
string* create_std_string();
string* cstring_to_std_string(char* s, size_t len);
const char*   std_string_to_cstring(string* s);

ADD_VECTOR_HEADERS(int, i);
ADD_VECTOR_HEADERS(float, f);
ADD_VECTOR_HEADERS(double, d);

}
<|endoftext|>


<commit_before>#include <Arduino.h>
#include "buffer.h"

const char welcome[] = "\nVOLTAHELLO\n{\"sps\":5000}\nDATASTART\n";

uint16_t sensorValue = 0;
CircularBuffer<uint16_t, 128> buff;

#define BAUD 230400
#define TIMER_PRELOAD 65536-F_CPU/64/10000

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#include <util/setbaud.h>

void uart_init(void) {
        UBRR0H = UBRRH_VALUE;
        UBRR0L = UBRRL_VALUE;

#if USE_2X
        UCSR0A |= _BV(U2X0);
#else
        UCSR0A &= ~(_BV(U2X0));
#endif

        UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
        UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
}

void inline uart_putchar(char c) {
        loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
        UDR0 = c;
}

void inline uart_write_string(const char* buff) {
        for (int i = 0; buff[i]; i++) {
                uart_putchar(buff[i]);
        }
}

void inline uart_write_bytes(const char* buff, int len) {
        for (int i = 0; i<len; i++) {
                uart_putchar(buff[i]);
        }
}

void inline uart_write(unsigned long val) {
        uart_write_bytes((const char*)&val, sizeof(val));
}

void inline uart_write(uint16_t val) {
        uart_write_bytes((const char*)&val, sizeof(val));
}

void inline trigger_analog() {
        sbi(ADCSRA, ADSC);
}

uint16_t inline analog()
{
        while (bit_is_set(ADCSRA, ADSC)) ;
        uint16_t value = ADCW;
        return value;
}

void setup() {
        pinMode(A0, INPUT);
        // set up the ADC input and ref
        ADMUX = 0x40;
        // set prescale to 16
        sbi(ADCSRA,ADPS2);
        cbi(ADCSRA,ADPS1);
        cbi(ADCSRA,ADPS0);

        // set up UART and send welcome message
        uart_init();
        uart_write_string(welcome);

        // initialize timer1
        noInterrupts();
        TCCR1A = 0;
        TCCR1B = 0;

        TCNT1 = TIMER_PRELOAD;

        // 64 prescaler
        sbi(TCCR1B, CS10);
        sbi(TCCR1B, CS11);
        sbi(TIMSK1, TOIE1); // enable timer overflow interrupt
        interrupts();
}

ISR(TIMER1_OVF_vect) {
        TCNT1 = TIMER_PRELOAD;
        sensorValue = analog();
        trigger_analog();
        buff.push(sensorValue);
}

void loop() {
        if(buff.remain()){
                uart_write(buff.pop());
        }
}
<commit_msg>report correct samplerate<commit_after>#include <Arduino.h>
#include "buffer.h"

const char welcome[] = "\nVOLTAHELLO\n{\"sps\":10000}\nDATASTART\n";

uint16_t sensorValue = 0;
CircularBuffer<uint16_t, 128> buff;

#define BAUD 230400
#define TIMER_PRELOAD 65536-F_CPU/64/10000

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#include <util/setbaud.h>

void uart_init(void) {
        UBRR0H = UBRRH_VALUE;
        UBRR0L = UBRRL_VALUE;

#if USE_2X
        UCSR0A |= _BV(U2X0);
#else
        UCSR0A &= ~(_BV(U2X0));
#endif

        UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
        UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
}

void inline uart_putchar(char c) {
        loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
        UDR0 = c;
}

void inline uart_write_string(const char* buff) {
        for (int i = 0; buff[i]; i++) {
                uart_putchar(buff[i]);
        }
}

void inline uart_write_bytes(const char* buff, int len) {
        for (int i = 0; i<len; i++) {
                uart_putchar(buff[i]);
        }
}

void inline uart_write(unsigned long val) {
        uart_write_bytes((const char*)&val, sizeof(val));
}

void inline uart_write(uint16_t val) {
        uart_write_bytes((const char*)&val, sizeof(val));
}

void inline trigger_analog() {
        sbi(ADCSRA, ADSC);
}

uint16_t inline analog()
{
        while (bit_is_set(ADCSRA, ADSC)) ;
        uint16_t value = ADCW;
        return value;
}

void setup() {
        pinMode(A0, INPUT);
        // set up the ADC input and ref
        ADMUX = 0x40;
        // set prescale to 16
        sbi(ADCSRA,ADPS2);
        cbi(ADCSRA,ADPS1);
        cbi(ADCSRA,ADPS0);

        // set up UART and send welcome message
        uart_init();
        uart_write_string(welcome);

        // initialize timer1
        noInterrupts();
        TCCR1A = 0;
        TCCR1B = 0;

        TCNT1 = TIMER_PRELOAD;

        // 64 prescaler
        sbi(TCCR1B, CS10);
        sbi(TCCR1B, CS11);
        sbi(TIMSK1, TOIE1); // enable timer overflow interrupt
        interrupts();
}

ISR(TIMER1_OVF_vect) {
        TCNT1 = TIMER_PRELOAD;
        sensorValue = analog();
        trigger_analog();
        buff.push(sensorValue);
}

void loop() {
        if(buff.remain()){
                uart_write(buff.pop());
        }
}
<|endoftext|>


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

#include "../Precompiled.h"

#include "../Core/Context.h"
#include "../IO/Log.h"
#include "../Scene/Node.h"
#include "../Scene/Scene.h"
#include "../Urho2D/CollisionShape2D.h"
#include "../Urho2D/PhysicsUtils2D.h"
#include "../Urho2D/RigidBody2D.h"

#include "../DebugNew.h"

namespace Urho3D
{

CollisionShape2D::CollisionShape2D(Context* context) :
    Component(context),
    fixture_(0),
    cachedWorldScale_(Vector3::ONE)
{

}

CollisionShape2D::~CollisionShape2D()
{
    if (rigidBody_)
        rigidBody_->RemoveCollisionShape2D(this);

    ReleaseFixture();
}

void CollisionShape2D::RegisterObject(Context* context)
{
    URHO3D_ACCESSOR_ATTRIBUTE("Trigger", IsTrigger, SetTrigger, bool, false, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Category Bits", GetCategoryBits, SetCategoryBits, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Mask Bits", GetMaskBits, SetMaskBits, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Group Index", GetGroupIndex, SetGroupIndex, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Density", GetDensity, SetDensity, float, 0.0f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, 0.2f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, 0.0f, AM_DEFAULT);
}

void CollisionShape2D::OnSetEnabled()
{
    if (IsEnabledEffective())
    {
        CreateFixture();
        if (rigidBody_)
            rigidBody_->AddCollisionShape2D(this);
    }
    else
    {
        if (rigidBody_)
            rigidBody_->RemoveCollisionShape2D(this);
        ReleaseFixture();
    }
}

void CollisionShape2D::SetTrigger(bool trigger)
{
    if (fixtureDef_.isSensor == trigger)
        return;

    fixtureDef_.isSensor = trigger;

    if (fixture_)
        fixture_->SetSensor(trigger);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetCategoryBits(int categoryBits)
{
    if (fixtureDef_.filter.categoryBits == categoryBits)
        return;

    fixtureDef_.filter.categoryBits = (uint16)categoryBits;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetMaskBits(int maskBits)
{
    if (fixtureDef_.filter.maskBits == maskBits)
        return;

    fixtureDef_.filter.maskBits = (uint16)maskBits;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetGroupIndex(int groupIndex)
{
    if (fixtureDef_.filter.groupIndex == groupIndex)
        return;

    fixtureDef_.filter.groupIndex = (int16)groupIndex;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetDensity(float density)
{
    if (fixtureDef_.density == density)
        return;

    fixtureDef_.density = density;

    if (fixture_)
    {
        // This will not automatically adjust the mass of the body
        fixture_->SetDensity(density);

        if (rigidBody_->GetUseFixtureMass())
            rigidBody_->GetBody()->ResetMassData();
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::SetFriction(float friction)
{
    if (fixtureDef_.friction == friction)
        return;

    fixtureDef_.friction = friction;

    if (fixture_)
    {
        // This will not change the friction of existing contacts
        fixture_->SetFriction(friction);

        b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
        while (contractEdge)
        {
            b2Contact* contact = contractEdge->contact;
            if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
                contractEdge->contact->ResetFriction();

            contractEdge = contractEdge->next;
        }
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::SetRestitution(float restitution)
{
    if (fixtureDef_.restitution == restitution)
        return;

    fixtureDef_.restitution = restitution;

    if (fixture_)
    {
        // This will not change the restitution of existing contacts
        fixture_->SetRestitution(restitution);

        b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
        while (contractEdge)
        {
            b2Contact* contact = contractEdge->contact;
            if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
                contractEdge->contact->ResetRestitution();

            contractEdge = contractEdge->next;
        }
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::CreateFixture()
{
    if (fixture_)
        return;

    if (!fixtureDef_.shape)
        return;

    if (!rigidBody_)
    {
        rigidBody_ = node_->GetComponent<RigidBody2D>(); // RigidBody2D can be created after CollisionShape2D
        if (!rigidBody_)
            return;
    }

    b2Body* body = rigidBody_->GetBody();
    if (!body)
        return;

    // Chain shape must have atleast two vertices before creating fixture
    if (fixtureDef_.shape->m_type != b2Shape::e_chain || static_cast<const b2ChainShape*>(fixtureDef_.shape)->m_count >= 2)
    {
        fixture_ = body->CreateFixture(&fixtureDef_);
        fixture_->SetUserData(this);
    }
}

void CollisionShape2D::ReleaseFixture()
{
    if (!fixture_)
        return;

    if (!rigidBody_)
        return;

    b2Body* body = rigidBody_->GetBody();
    if (!body)
        return;

    body->DestroyFixture(fixture_);
    fixture_ = 0;
}

float CollisionShape2D::GetMass() const
{
    if (!fixture_)
        return 0.0f;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return massData.mass;
}

float CollisionShape2D::GetInertia() const
{
    if (!fixture_)
        return 0.0f;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return massData.I;
}

Vector2 CollisionShape2D::GetMassCenter() const
{
    if (!fixture_)
        return Vector2::ZERO;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return ToVector2(massData.center);
}

void CollisionShape2D::OnNodeSet(Node* node)
{
    Component::OnNodeSet(node);

    if (node)
    {
        node->AddListener(this);
        rigidBody_ = node->GetComponent<RigidBody2D>();
        if (rigidBody_)
        {
            CreateFixture();
            rigidBody_->AddCollisionShape2D(this);
        }
    }
}

void CollisionShape2D::OnMarkedDirty(Node* node)
{
    Vector3 newWorldScale = node_->GetWorldScale();

    Vector3 delta = newWorldScale - cachedWorldScale_;
    if (delta.DotProduct(delta) < 0.01f)
        return;

    // Physics operations are not safe from worker threads
    Scene* scene = GetScene();
    if (scene && scene->IsThreadedUpdate())
    {
        scene->DelayedMarkedDirty(this);
        return;
    }

    cachedWorldScale_ = newWorldScale;

    if (fixture_)
        ApplyNodeWorldScale();
}

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

#include "../Precompiled.h"

#include "../Core/Context.h"
#include "../IO/Log.h"
#include "../Scene/Node.h"
#include "../Scene/Scene.h"
#include "../Urho2D/CollisionShape2D.h"
#include "../Urho2D/PhysicsUtils2D.h"
#include "../Urho2D/RigidBody2D.h"

#include "../DebugNew.h"

namespace Urho3D
{

CollisionShape2D::CollisionShape2D(Context* context) :
    Component(context),
    fixture_(0),
    cachedWorldScale_(Vector3::ONE)
{

}

CollisionShape2D::~CollisionShape2D()
{
    if (rigidBody_)
        rigidBody_->RemoveCollisionShape2D(this);

    ReleaseFixture();
}

void CollisionShape2D::RegisterObject(Context* context)
{
    URHO3D_ACCESSOR_ATTRIBUTE("Trigger", IsTrigger, SetTrigger, bool, false, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Category Bits", GetCategoryBits, SetCategoryBits, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Mask Bits", GetMaskBits, SetMaskBits, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Group Index", GetGroupIndex, SetGroupIndex, int, 0, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Density", GetDensity, SetDensity, float, 0.0f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, 0.2f, AM_DEFAULT);
    URHO3D_ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, 0.0f, AM_DEFAULT);
}

void CollisionShape2D::OnSetEnabled()
{
    if (IsEnabledEffective())
    {
        CreateFixture();
        if (rigidBody_)
            rigidBody_->AddCollisionShape2D(this);
    }
    else
    {
        if (rigidBody_)
            rigidBody_->RemoveCollisionShape2D(this);
        ReleaseFixture();
    }
}

void CollisionShape2D::SetTrigger(bool trigger)
{
    if (fixtureDef_.isSensor == trigger)
        return;

    fixtureDef_.isSensor = trigger;

    if (fixture_)
        fixture_->SetSensor(trigger);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetCategoryBits(int categoryBits)
{
    if (fixtureDef_.filter.categoryBits == categoryBits)
        return;

    fixtureDef_.filter.categoryBits = (uint16)categoryBits;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetMaskBits(int maskBits)
{
    if (fixtureDef_.filter.maskBits == maskBits)
        return;

    fixtureDef_.filter.maskBits = (uint16)maskBits;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetGroupIndex(int groupIndex)
{
    if (fixtureDef_.filter.groupIndex == groupIndex)
        return;

    fixtureDef_.filter.groupIndex = (int16)groupIndex;

    if (fixture_)
        fixture_->SetFilterData(fixtureDef_.filter);

    MarkNetworkUpdate();
}

void CollisionShape2D::SetDensity(float density)
{
    if (fixtureDef_.density == density)
        return;

    fixtureDef_.density = density;

    if (fixture_)
    {
        // This will not automatically adjust the mass of the body
        fixture_->SetDensity(density);

        if (rigidBody_->GetUseFixtureMass())
            rigidBody_->GetBody()->ResetMassData();
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::SetFriction(float friction)
{
    if (fixtureDef_.friction == friction)
        return;

    fixtureDef_.friction = friction;

    if (fixture_)
    {
        // This will not change the friction of existing contacts
        fixture_->SetFriction(friction);

        b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
        while (contractEdge)
        {
            b2Contact* contact = contractEdge->contact;
            if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
                contractEdge->contact->ResetFriction();

            contractEdge = contractEdge->next;
        }
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::SetRestitution(float restitution)
{
    if (fixtureDef_.restitution == restitution)
        return;

    fixtureDef_.restitution = restitution;

    if (fixture_)
    {
        // This will not change the restitution of existing contacts
        fixture_->SetRestitution(restitution);

        b2ContactEdge* contractEdge = rigidBody_->GetBody()->GetContactList();
        while (contractEdge)
        {
            b2Contact* contact = contractEdge->contact;
            if (contact->GetFixtureA() == fixture_ || contact->GetFixtureB() == fixture_)
                contractEdge->contact->ResetRestitution();

            contractEdge = contractEdge->next;
        }
    }

    MarkNetworkUpdate();
}

void CollisionShape2D::CreateFixture()
{
    if (fixture_)
        return;

    if (!fixtureDef_.shape)
        return;

    if (!rigidBody_)
    {
        rigidBody_ = node_->GetComponent<RigidBody2D>(); // RigidBody2D can be created after CollisionShape2D
        if (!rigidBody_)
            return;
    }

    b2Body* body = rigidBody_->GetBody();
    if (!body)
        return;

    // Chain shape must have atleast two vertices before creating fixture
    if (fixtureDef_.shape->m_type != b2Shape::e_chain || static_cast<const b2ChainShape*>(fixtureDef_.shape)->m_count >= 2)
    {
        b2MassData massData;
        body->GetMassData(&massData);
        fixture_ = body->CreateFixture(&fixtureDef_);
        if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in CreateFixture().
            body->SetMassData(&massData);
        fixture_->SetUserData(this);
    }
}

void CollisionShape2D::ReleaseFixture()
{
    if (!fixture_)
        return;

    if (!rigidBody_)
        return;

    b2Body* body = rigidBody_->GetBody();
    if (!body)
        return;

    b2MassData massData;
    body->GetMassData(&massData);
    body->DestroyFixture(fixture_);
    if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in DestroyFixture().
        body->SetMassData(&massData);
    fixture_ = 0;
}

float CollisionShape2D::GetMass() const
{
    if (!fixture_)
        return 0.0f;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return massData.mass;
}

float CollisionShape2D::GetInertia() const
{
    if (!fixture_)
        return 0.0f;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return massData.I;
}

Vector2 CollisionShape2D::GetMassCenter() const
{
    if (!fixture_)
        return Vector2::ZERO;

    b2MassData massData;
    fixture_->GetMassData(&massData);

    return ToVector2(massData.center);
}

void CollisionShape2D::OnNodeSet(Node* node)
{
    Component::OnNodeSet(node);

    if (node)
    {
        node->AddListener(this);
        rigidBody_ = node->GetComponent<RigidBody2D>();
        if (rigidBody_)
        {
            CreateFixture();
            rigidBody_->AddCollisionShape2D(this);
        }
    }
}

void CollisionShape2D::OnMarkedDirty(Node* node)
{
    Vector3 newWorldScale = node_->GetWorldScale();

    Vector3 delta = newWorldScale - cachedWorldScale_;
    if (delta.DotProduct(delta) < 0.01f)
        return;

    // Physics operations are not safe from worker threads
    Scene* scene = GetScene();
    if (scene && scene->IsThreadedUpdate())
    {
        scene->DelayedMarkedDirty(this);
        return;
    }

    cachedWorldScale_ = newWorldScale;

    if (fixture_)
        ApplyNodeWorldScale();
}

}
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * PNG files reading.
  */

#include <thcrap.h>
#include <png.h>
#include "./png.h"

struct file_buffer_t {
	BYTE *buffer;
	size_t size;
};

void read_bytes(png_structp png_ptr, png_bytep out, png_size_t out_size)
{
	file_buffer_t *in = (file_buffer_t*)png_get_io_ptr(png_ptr);
	if (out_size > in->size) {
		log_print("Error: file truncated!\n");
		longjmp(png_jmpbuf(png_ptr), 1);
	}
	memcpy(out, in->buffer, out_size);
	in->buffer += out_size;
	in->size   -= out_size;
}

void png_warning_callback(png_structp png_ptr, png_const_charp msg)
{
	log_printf("Warning: %s\n", msg);
}

void png_error_callback(png_structp png_ptr, png_const_charp msg)
{
	log_printf("Error: %s!\n", msg);
	longjmp(png_jmpbuf(png_ptr), 1);
}

// PNG reading core is adapted from http://www.libpng.org/pub/png/book/chapter13.html
BYTE **png_image_read(const char *fn, uint32_t *width, uint32_t *height, uint8_t *bpp)
{
	stack_chain_iterate_t sci = { 0 };
	BYTE *file_buffer = nullptr;
	file_buffer_t file = { 0 };

	json_t *chain = resolve_chain_game(fn);

	if (json_array_size(chain)) {
		log_printf("(PNG) Resolving %s...", json_array_get_string(chain, 0));
		while (file_buffer == nullptr && stack_chain_iterate(&sci, chain, SCI_BACKWARDS) != 0) {
			file_buffer = (BYTE*)patch_file_load(sci.patch_info, sci.fn, &file.size);
		}
	}
	json_decref(chain);
	if (!file_buffer) {
		log_print("not found\n");
		return nullptr;
	}
	patch_print_fn(sci.patch_info, fn);
	log_print("\n");
	file.buffer = file_buffer;

	if (!png_check_sig(file.buffer, 8)) {
		log_print("Bad PNG signature!\n");
		free(file_buffer);
		return nullptr;
	}
	file.buffer += 8;
	file.size -= 8;

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_callback, png_warning_callback);
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		free(file_buffer);
		return nullptr;
	}
	png_set_read_fn(png_ptr, &file, read_bytes);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);

	int bit_depth, color_type;
	png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth, &color_type, NULL, NULL, NULL);
	*bpp = (color_type & PNG_COLOR_MASK_ALPHA) ? 32 : 24;

	if (color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(png_ptr);
	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
		png_set_expand(png_ptr);
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
		png_set_expand(png_ptr);
	if (bit_depth == 16)
		png_set_strip_16(png_ptr);
	if (color_type == PNG_COLOR_TYPE_GRAY ||
		color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
		png_set_gray_to_rgb(png_ptr);
	png_read_update_info(png_ptr, info_ptr);

	uint32_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
	BYTE **row_pointers = (BYTE**)malloc(sizeof(BYTE*) * *height + rowbytes * *height);
	BYTE* image_data = ((BYTE*)row_pointers) + sizeof(BYTE*) * *height;
	for (uint32_t i = 0; i < *height; i++)
		row_pointers[i] = image_data + i * rowbytes;
	png_read_image(png_ptr, row_pointers);

	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	free(file_buffer);
	return row_pointers;
}

bool png_image_get_IHDR(const char *fn, uint32_t *width, uint32_t *height, uint8_t *bpp)
{
	stack_chain_iterate_t sci = { 0 };
	BYTE *file_buffer = nullptr;
	file_buffer_t file = { 0 };

	json_t *chain = resolve_chain_game(fn);

	if (json_array_size(chain)) {
		log_printf("(PNG) Resolving %s...", json_array_get_string(chain, 0));
		while (file_buffer == nullptr && stack_chain_iterate(&sci, chain, SCI_BACKWARDS) != 0) {
			file_buffer = (BYTE*)patch_file_load(sci.patch_info, sci.fn, &file.size);
		}
	}
	json_decref(chain);
	if (!file_buffer) {
		log_print("not found\n");
		return false;
	}
	patch_print_fn(sci.patch_info, fn);
	log_print("\n");
	file.buffer = file_buffer;

	if (!png_check_sig(file.buffer, 8)) {
		log_print("Bad PNG signature!\n");
		free(file_buffer);
		return false;
	}
	file.buffer += 8;
	file.size -= 8;

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_callback, png_warning_callback);
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		free(file_buffer);
		return false;
	}
	png_set_read_fn(png_ptr, &file, read_bytes);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);

	int color_type;
	png_get_IHDR(png_ptr, info_ptr, width, height, NULL, &color_type, NULL, NULL, NULL);
	if (bpp) {
		*bpp = (color_type & PNG_COLOR_MASK_ALPHA) ? 32 : 24;
	}

	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	free(file_buffer);
	return true;
}
<commit_msg>thcrap_tasofro: png: display the correct file name in the logs.<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * PNG files reading.
  */

#include <thcrap.h>
#include <png.h>
#include "./png.h"

struct file_buffer_t {
	BYTE *buffer;
	size_t size;
};

void read_bytes(png_structp png_ptr, png_bytep out, png_size_t out_size)
{
	file_buffer_t *in = (file_buffer_t*)png_get_io_ptr(png_ptr);
	if (out_size > in->size) {
		log_print("Error: file truncated!\n");
		longjmp(png_jmpbuf(png_ptr), 1);
	}
	memcpy(out, in->buffer, out_size);
	in->buffer += out_size;
	in->size   -= out_size;
}

void png_warning_callback(png_structp png_ptr, png_const_charp msg)
{
	log_printf("Warning: %s\n", msg);
}

void png_error_callback(png_structp png_ptr, png_const_charp msg)
{
	log_printf("Error: %s!\n", msg);
	longjmp(png_jmpbuf(png_ptr), 1);
}

// PNG reading core is adapted from http://www.libpng.org/pub/png/book/chapter13.html
BYTE **png_image_read(const char *fn, uint32_t *width, uint32_t *height, uint8_t *bpp)
{
	stack_chain_iterate_t sci = { 0 };
	BYTE *file_buffer = nullptr;
	file_buffer_t file = { 0 };

	json_t *chain = resolve_chain_game(fn);

	if (json_array_size(chain)) {
		log_printf("(PNG) Resolving %s...", json_array_get_string(chain, 0));
		while (file_buffer == nullptr && stack_chain_iterate(&sci, chain, SCI_BACKWARDS) != 0) {
			file_buffer = (BYTE*)patch_file_load(sci.patch_info, sci.fn, &file.size);
		}
	}
	json_decref(chain);
	if (!file_buffer) {
		log_print("not found\n");
		return nullptr;
	}
	patch_print_fn(sci.patch_info, sci.fn);
	log_print("\n");
	file.buffer = file_buffer;

	if (!png_check_sig(file.buffer, 8)) {
		log_print("Bad PNG signature!\n");
		free(file_buffer);
		return nullptr;
	}
	file.buffer += 8;
	file.size -= 8;

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_callback, png_warning_callback);
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		free(file_buffer);
		return nullptr;
	}
	png_set_read_fn(png_ptr, &file, read_bytes);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);

	int bit_depth, color_type;
	png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth, &color_type, NULL, NULL, NULL);
	*bpp = (color_type & PNG_COLOR_MASK_ALPHA) ? 32 : 24;

	if (color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(png_ptr);
	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
		png_set_expand(png_ptr);
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
		png_set_expand(png_ptr);
	if (bit_depth == 16)
		png_set_strip_16(png_ptr);
	if (color_type == PNG_COLOR_TYPE_GRAY ||
		color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
		png_set_gray_to_rgb(png_ptr);
	png_read_update_info(png_ptr, info_ptr);

	uint32_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
	BYTE **row_pointers = (BYTE**)malloc(sizeof(BYTE*) * *height + rowbytes * *height);
	BYTE* image_data = ((BYTE*)row_pointers) + sizeof(BYTE*) * *height;
	for (uint32_t i = 0; i < *height; i++)
		row_pointers[i] = image_data + i * rowbytes;
	png_read_image(png_ptr, row_pointers);

	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	free(file_buffer);
	return row_pointers;
}

bool png_image_get_IHDR(const char *fn, uint32_t *width, uint32_t *height, uint8_t *bpp)
{
	stack_chain_iterate_t sci = { 0 };
	BYTE *file_buffer = nullptr;
	file_buffer_t file = { 0 };

	json_t *chain = resolve_chain_game(fn);

	if (json_array_size(chain)) {
		log_printf("(PNG) Resolving %s...", json_array_get_string(chain, 0));
		while (file_buffer == nullptr && stack_chain_iterate(&sci, chain, SCI_BACKWARDS) != 0) {
			file_buffer = (BYTE*)patch_file_load(sci.patch_info, sci.fn, &file.size);
		}
	}
	json_decref(chain);
	if (!file_buffer) {
		log_print("not found\n");
		return false;
	}
	patch_print_fn(sci.patch_info, fn);
	log_print("\n");
	file.buffer = file_buffer;

	if (!png_check_sig(file.buffer, 8)) {
		log_print("Bad PNG signature!\n");
		free(file_buffer);
		return false;
	}
	file.buffer += 8;
	file.size -= 8;

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_callback, png_warning_callback);
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		free(file_buffer);
		return false;
	}
	png_set_read_fn(png_ptr, &file, read_bytes);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);

	int color_type;
	png_get_IHDR(png_ptr, info_ptr, width, height, NULL, &color_type, NULL, NULL, NULL);
	if (bpp) {
		*bpp = (color_type & PNG_COLOR_MASK_ALPHA) ? 32 : 24;
	}

	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	free(file_buffer);
	return true;
}
<|endoftext|>


<commit_before>#pragma once

#include <cstdint>
#include <vector>
#include <string>

#if __BIG_ENDIAN
#define RTP_HEADER(port, subclass, ack, sfs)                                 \
    (((port & 0x0F) << 4) | ((subclass & 0x03) << 2) | ((ack & 0x01) << 1) | \
     (sfs & 0x01))
#else
#define RTP_HEADER(port, subclass, ack, sfs)                                \
    (((sfs & 0x01) << 7) | ((ack & 0x01) << 6) | ((subclass & 0x03) << 4) | \
     (port & 0x0F))
#endif

#define BASE_STATION_ADDR (1)
#define LOOPBACK_ADDR (127)

namespace rtp {

///
static const unsigned int MAX_DATA_SZ = 120;

/**
 * @brief      { port enumerations for different communication protocols }
 */
enum port {
    SINK = 0x00,
    LINK = 0x01,
    CONTROL = 0x02,
    SETPOINT = 0x03,
    GSTROBE = 0x04,
    DISCOVER = 0x05,
    LOGGER = 0x06,
    TCP = 0x07,
    LEGACY = 0x0E
};

namespace {
// type of data field used in all packet classes
// - must be 8 bits, (typedef used for eaiser compiler
// type error debugging)
typedef uint8_t data_t;
}

class header_data {
public:
    enum type { control, tuning, ota, misc };

    header_data() : t(control), address(0), port_fields(0){};

    size_t size() const { return 2; }

    type t;
    data_t address;

    friend class payload_data;

    // common header byte - union so it's easier to put into vector
    data_t port_fields;
    struct {
#if __BIG_ENDIAN
        data_t sfs : 1, ack : 1, subclass : 2, port : 4;
#else
        data_t port : 4, subclass : 2, ack : 1, sfs : 1;
#endif
    } __attribute__((packed));
};

class payload_data {
public:
    payload_data(){};

    std::vector<data_t> d;

    size_t size() const { return d.size(); }

    template <class T>
    void fill(const std::vector<T>& v) {
        d = v;
    }
    void fill(const std::string& str) {
        for (const char& c : str) d.push_back(c);
        d.push_back('\0');
    }
};

/**
 * @brief      { Real-Time packet definition }
 */
class packet {
public:
    rtp::header_data header;
    rtp::payload_data payload;

    packet(){};
    packet(const std::string& s) { payload.fill(s); }

    template <class T>
    packet(const std::vector<T>& v) {
        payload.fill(v);
    }

    size_t size(bool includeHeader = true) {
        if (includeHeader)
            return payload.size() + header.size();
        else
            return payload.size();
    }

    int port() const { return static_cast<int>(header.port); }
    template <class T>
    void port(T p) {
        header.port = static_cast<unsigned int>(p);
    }

    int subclass() { return header.subclass; }
    template <class T>
    void subclass(T c) {
        header.subclass = static_cast<unsigned int>(c);
    }

    bool ack() { return header.ack; }
    void ack(bool b) { header.ack = b; }

    bool sfs() { return header.sfs; }
    void sfs(bool b) { header.sfs = b; }

    int address() { return header.address; }
    void address(int a) { header.address = static_cast<unsigned int>(a); }

    template <class T>
    void recv(const std::vector<T>& v) {
        payload.fill(v);
        // sort out header
    }

    void pack(std::vector<data_t>* buffer, bool includeHeader = false) const {
        // first byte is total size (excluding the size byte)
        const uint8_t total_size =
            payload.size() + (includeHeader ? header.size() : 0);
        buffer->reserve(total_size + 1);
        buffer->push_back(total_size);

        // header data
        if (includeHeader) {
            buffer->push_back(header.address);
            buffer->push_back(header.port_fields);
        }

        // payload
        buffer->insert(buffer->end(), payload.d.begin(), payload.d.end());
    }
};
}
<commit_msg>port identifiers are no longer a bitmask<commit_after>#pragma once

#include <cstdint>
#include <vector>
#include <string>

#if __BIG_ENDIAN
#define RTP_HEADER(port, subclass, ack, sfs)                                 \
    (((port & 0x0F) << 4) | ((subclass & 0x03) << 2) | ((ack & 0x01) << 1) | \
     (sfs & 0x01))
#else
#define RTP_HEADER(port, subclass, ack, sfs)                                \
    (((sfs & 0x01) << 7) | ((ack & 0x01) << 6) | ((subclass & 0x03) << 4) | \
     (port & 0x0F))
#endif

#define BASE_STATION_ADDR (1)
#define LOOPBACK_ADDR (127)

namespace rtp {

///
static const unsigned int MAX_DATA_SZ = 120;

/**
 * @brief      { port enumerations for different communication protocols }
 */
enum port {
    SINK = 0,
    LINK,
    CONTROL,
    SETPOINT,
    GSTROBE,
    DISCOVER,
    LOGGER,
    TCP,
    LEGACY
};

namespace {
// type of data field used in all packet classes
// - must be 8 bits, (typedef used for eaiser compiler
// type error debugging)
typedef uint8_t data_t;
}

class header_data {
public:
    enum type { control, tuning, ota, misc };

    header_data() : t(control), address(0), port_fields(0){};

    size_t size() const { return 2; }

    type t;
    data_t address;

    friend class payload_data;

    // common header byte - union so it's easier to put into vector
    data_t port_fields;
    struct {
#if __BIG_ENDIAN
        data_t sfs : 1, ack : 1, subclass : 2, port : 4;
#else
        data_t port : 4, subclass : 2, ack : 1, sfs : 1;
#endif
    } __attribute__((packed));
};

class payload_data {
public:
    payload_data(){};

    std::vector<data_t> d;

    size_t size() const { return d.size(); }

    template <class T>
    void fill(const std::vector<T>& v) {
        d = v;
    }
    void fill(const std::string& str) {
        for (const char& c : str) d.push_back(c);
        d.push_back('\0');
    }
};

/**
 * @brief      { Real-Time packet definition }
 */
class packet {
public:
    rtp::header_data header;
    rtp::payload_data payload;

    packet(){};
    packet(const std::string& s) { payload.fill(s); }

    template <class T>
    packet(const std::vector<T>& v) {
        payload.fill(v);
    }

    size_t size(bool includeHeader = true) {
        if (includeHeader)
            return payload.size() + header.size();
        else
            return payload.size();
    }

    int port() const { return static_cast<int>(header.port); }
    template <class T>
    void port(T p) {
        header.port = static_cast<unsigned int>(p);
    }

    int subclass() { return header.subclass; }
    template <class T>
    void subclass(T c) {
        header.subclass = static_cast<unsigned int>(c);
    }

    bool ack() { return header.ack; }
    void ack(bool b) { header.ack = b; }

    bool sfs() { return header.sfs; }
    void sfs(bool b) { header.sfs = b; }

    int address() { return header.address; }
    void address(int a) { header.address = static_cast<unsigned int>(a); }

    template <class T>
    void recv(const std::vector<T>& v) {
        payload.fill(v);
        // sort out header
    }

    void pack(std::vector<data_t>* buffer, bool includeHeader = false) const {
        // first byte is total size (excluding the size byte)
        const uint8_t total_size =
            payload.size() + (includeHeader ? header.size() : 0);
        buffer->reserve(total_size + 1);
        buffer->push_back(total_size);

        // header data
        if (includeHeader) {
            buffer->push_back(header.address);
            buffer->push_back(header.port_fields);
        }

        // payload
        buffer->insert(buffer->end(), payload.d.begin(), payload.d.end());
    }
};
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2021, Cinesite VFX Ltd. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#ifndef GAFFER_SIGNALS_INL
#define GAFFER_SIGNALS_INL

#include "IECore/MessageHandler.h"

#include "boost/function.hpp"
#include "boost/iterator/iterator_facade.hpp"
#include "boost/visit_each.hpp"

namespace Gaffer::Signals
{

//////////////////////////////////////////////////////////////////////////
// Connection
//////////////////////////////////////////////////////////////////////////

inline Connection::Connection( const Private::SlotBase::Ptr &slot )
	:	m_slot( slot )
{
}

inline void Connection::block( bool shouldBlock )
{
	if( m_slot )
	{
		m_slot->blocked = shouldBlock;
	}
}

inline void Connection::unblock()
{
	if( m_slot )
	{
		m_slot->blocked = false;
	}
}

inline bool Connection::blocked() const
{
	return m_slot && m_slot->blocked;
};

inline void Connection::disconnect()
{
	if( !m_slot )
	{
		return;
	}
	m_slot->disconnect();
	m_slot = nullptr;
}

inline bool Connection::connected() const
{
	return m_slot && m_slot->previous;
}

//////////////////////////////////////////////////////////////////////////
// DefaultCombiner
//////////////////////////////////////////////////////////////////////////

template<typename T>
struct DefaultCombiner
{

	template<typename InputIterator>
	T operator()( InputIterator first, InputIterator last ) const
	{
		if constexpr( std::is_void_v<T> )
		{
			while( first != last )
			{
				*first;
				++first;
			}
		}
		else
		{
			T r = T();
			while( first != last )
			{
				r = *first;
				++first;
			}
			return r;
		}
	}

};

//////////////////////////////////////////////////////////////////////////
// Signal
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
Signal<Result( Args... ), Combiner>::Signal( const Combiner &combiner )
	:	m_lastSlotAndCombiner( nullptr, combiner )
{
}

template<typename Result, typename... Args, typename Combiner>
Signal<Result( Args... ), Combiner>::~Signal()
{
	disconnectAllSlots();
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connect( const SlotFunctor &slot )
{
	return connectInternal( slot, /* front = */ false );
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connectFront( const SlotFunctor &slot )
{
	return connectInternal( slot, /* front = */ true );
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connectInternal( const SlotFunctor &slot, bool front )
{
	if( !lastSlot() )
	{
		assert( !m_firstSlot );
		lastSlot() = new Slot( m_firstSlot );
	}

	Private::SlotBase::Ptr s = new Slot(
		front ? m_firstSlot : *(lastSlot()->previous), slot
	);

	const Connection result = Connection( s );
	Trackable::trackConnection( slot, result );
	return result;
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
void Signal<Result( Args... ), Combiner>::disconnect( const SlotFunctor &slotFunctor )
{
	Private::SlotBase::Ptr slot = m_firstSlot;
	while( slot != lastSlot() )
	{
		if( static_cast<Slot *>( slot.get() )->function == slotFunctor )
		{
			slot->disconnect();
		}
		slot = slot->next;
	}
};

template<typename Result, typename... Args, typename Combiner>
Result Signal<Result( Args... ), Combiner>::operator() ( Args... args ) const
{
	ArgsTuple argsTuple( args... ); /// \todo : Capture by reference? Or forward_as_tuple?
	return combiner()(
		SlotCallIterator( m_firstSlot, lastSlot(), argsTuple ),
		SlotCallIterator( lastSlot(), lastSlot(), argsTuple )
	);
}

template<typename Result, typename... Args, typename Combiner>
void Signal<Result( Args... ), Combiner>::disconnectAllSlots()
{
	while( m_firstSlot != lastSlot() )
	{
		m_firstSlot->disconnect();
	}
	assert( m_firstSlot == lastSlot() );
};

template<typename Result, typename... Args, typename Combiner>
size_t Signal<Result( Args... ), Combiner>::numSlots() const
{
	size_t result = 0;
	const Private::SlotBase *s = m_firstSlot.get();
	while( s != lastSlot().get() )
	{
		result++;
		s = s->next.get();
	}
	return result;
};

template<typename Result, typename... Args, typename Combiner>
bool Signal<Result( Args... ), Combiner>::empty() const
{
	return m_firstSlot == lastSlot();
}

template<typename Result, typename... Args, typename Combiner>
Private::SlotBase::Ptr &Signal<Result( Args... ), Combiner>::lastSlot()
{
	return m_lastSlotAndCombiner.first();
}

template<typename Result, typename... Args, typename Combiner>
const Private::SlotBase::Ptr &Signal<Result( Args... ), Combiner>::lastSlot() const
{
	return m_lastSlotAndCombiner.first();
}

template<typename Result, typename... Args, typename Combiner>
const Combiner &Signal<Result( Args... ), Combiner>::combiner() const
{
	return m_lastSlotAndCombiner.second();
}

//////////////////////////////////////////////////////////////////////////
// Signal::Slot
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
struct Signal<Result( Args... ), Combiner>::Slot : public Private::SlotBase
{

	/// \todo We'd like to use `std::function` here, but it doesn't support
	/// equality comparison, which makes implementing
	/// `Signal::disconnect( const SlotFunctor & )` hard to implement. Perhaps
	/// it's achievable using `function::target_type` and `function::target`?
	using FunctionType = boost::function<Result( Args... )>;

	Slot( Private::SlotBase::Ptr &previous, const FunctionType &function = FunctionType() )
		: 	SlotBase( previous ), function( function )
	{
	}

	void disconnect() override
	{
		// `SlotBase::disconnect()` might (or might not) destroy us.
		// Extend our lifetime to the end of this function.
		const Ptr lifePreserver( this );
		const bool wasConnected = previous;
		Private::SlotBase::disconnect();
		if( wasConnected && !calling )
		{
			// Clear our function, and any resources that are bound inside it.
			// Note : this can cause arbitrary code to run, including the
			// destruction of ScopedConnections for this slot, causing reentrant
			// calls to `this->disconnect()`. We use `wasConnected` to protect
			// against the double-clear this could otherwise cause.
			function.clear();
		}
	}

	Result operator()( Args... args )
	{
		CallScope callScope( *this );
		return function( args... );
	}

	struct CallScope : private boost::noncopyable
	{
		CallScope( Slot &slot )
			:	slot( slot )
		{
			// Slot can't be called if not connected
			assert( slot.previous );
			slot.calling = true;
		}
		~CallScope()
		{
			slot.calling = false;
			if( !slot.previous )
			{
				// Slot was disconnected during call, and we couldn't
				// clear the function while it was being called. Clear
				// it now instead.
				slot.function.clear();
			}
		}
		Slot &slot;
	};

	FunctionType function;

};

//////////////////////////////////////////////////////////////////////////
// Signal::SlotCallIterator
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
class Signal<Result( Args... ), Combiner>::SlotCallIterator : public boost::iterator_facade<
	SlotCallIterator,
	SlotCallIteratorValueType,
	boost::single_pass_traversal_tag
>
{

	public:

		SlotCallIterator( const Private::SlotBase::Ptr &slot, const Private::SlotBase::Ptr &end, const ArgsTuple &args )
			:	m_slot( slot ), m_end( end ), m_args( args )
		{
			skipBlocked();
		}

	private :

		friend class boost::iterator_core_access;

		SlotCallIteratorValueType &dereference() const
		{
			if( !m_value )
			{
				if constexpr( std::is_void_v<Result> )
				{
					std::apply( static_cast<Slot &>( *m_slot ), m_args );
					m_value = true;
				}
				else
				{
					m_value = std::apply( static_cast<Slot &>( *m_slot ), m_args );
				}
			}
			return *m_value;
		}

		void increment()
		{
			m_slot = m_slot->next;
			m_value.reset();
			skipBlocked();
		}

		bool equal( const SlotCallIterator &other ) const
		{
			return m_slot == other.m_slot;
		}

		void skipBlocked()
		{
			while( m_slot && m_slot != m_end && static_cast<Slot *>( m_slot.get() )->blocked )
			{
				m_slot = m_slot->next;
			}
		}

		Private::SlotBase::Ptr m_slot;
		Private::SlotBase::Ptr m_end;
		const ArgsTuple &m_args;

		mutable std::optional<SlotCallIteratorValueType> m_value;

};

//////////////////////////////////////////////////////////////////////////
// CatchingCombiner
//////////////////////////////////////////////////////////////////////////

template<typename T>
struct CatchingCombiner
{

	template<typename InputIterator>
	T operator()( InputIterator first, InputIterator last ) const
	{
		if constexpr( std::is_void_v<T> )
		{
			while( first != last )
			{
				try
				{
					*first;
				}
				catch( const std::exception &e )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", e.what() );
				}
				catch( ... )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", "Unknown error" );
				}
				++first;
			}
		}
		else
		{
			T r = T();
			while( first != last )
			{
				try
				{
					r = *first;
				}
				catch( const std::exception &e )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", e.what() );
				}
				catch( ... )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", "Unknown error" );
				}
				++first;
			}
			return r;
		}
	}

};

//////////////////////////////////////////////////////////////////////////
// Trackable
//////////////////////////////////////////////////////////////////////////

inline Trackable::~Trackable()
{
	if( m_connections )
	{
		for( auto &c : *m_connections )
		{
			c.disconnect();
		}
	}
}

struct Trackable::TrackableVisitor
{

	TrackableVisitor( const Connection &connection ) : connection( connection ) {}

	template<typename T>
	void operator()( T &x ) const
	{
		if constexpr( std::is_base_of_v<Trackable, T> )
		{
			const Trackable &trackable = static_cast<const Trackable &>( x );
			if( !trackable.m_connections )
			{
				trackable.m_connections = std::make_unique<std::vector<Connection>>();
			}
			trackable.m_connections->push_back( connection );
		}
		else if constexpr( std::is_pointer_v<T> )
		{
			this->operator()( *x );
		}
	}

	const Connection &connection;

};

template<typename SlotFunctor>
void Trackable::trackConnection( const SlotFunctor &slotFunctor, const Connection &connection )
{
	/// \todo Should we purge expired connections periodically?

	// Rather than call `boost::visit_each()` explicitly, we must bring it into
	// scope with `using` and then just call `visit_each()`. This allows
	// argument-dependent-lookup to find specialised versions of `visit_each()`
	// for types in other namespaces.
	using boost::visit_each;

	// `visit_each` allows us to discover Trackable objects inside callables
	// such as `boost::bind()`. Note that `std::bind()` is _not_ supported.
	TrackableVisitor visitor( connection );
	visit_each(
		visitor,
		slotFunctor,
		0
	);
}

//////////////////////////////////////////////////////////////////////////
// ScopedConnection
//////////////////////////////////////////////////////////////////////////

inline ScopedConnection::ScopedConnection( const Connection &connection )
	:	Connection( connection )
{
}

inline ScopedConnection::~ScopedConnection()
{
	disconnect();
}

inline ScopedConnection &ScopedConnection::operator=( const Connection &connection )
{
	disconnect();
	Connection::operator=( connection );
	return *this;
}

//////////////////////////////////////////////////////////////////////////
// BlockedConnection
//////////////////////////////////////////////////////////////////////////

inline BlockedConnection::BlockedConnection( Signals::Connection &connection, bool block )
	:	m_connection( nullptr )
{
	if( block && connection.connected() )
	{
		m_connection = &connection;
		m_connection->block();
	}
}

inline BlockedConnection::~BlockedConnection()
{
	if( m_connection )
	{
		m_connection->unblock();
	}
}

} // namespace Gaffer::Signals

#endif // GAFFER_SIGNALS_INL
<commit_msg>SlotCallIterator : Don't store `m_end`<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2021, Cinesite VFX Ltd. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#ifndef GAFFER_SIGNALS_INL
#define GAFFER_SIGNALS_INL

#include "IECore/MessageHandler.h"

#include "boost/function.hpp"
#include "boost/iterator/iterator_facade.hpp"
#include "boost/visit_each.hpp"

namespace Gaffer::Signals
{

//////////////////////////////////////////////////////////////////////////
// Connection
//////////////////////////////////////////////////////////////////////////

inline Connection::Connection( const Private::SlotBase::Ptr &slot )
	:	m_slot( slot )
{
}

inline void Connection::block( bool shouldBlock )
{
	if( m_slot )
	{
		m_slot->blocked = shouldBlock;
	}
}

inline void Connection::unblock()
{
	if( m_slot )
	{
		m_slot->blocked = false;
	}
}

inline bool Connection::blocked() const
{
	return m_slot && m_slot->blocked;
};

inline void Connection::disconnect()
{
	if( !m_slot )
	{
		return;
	}
	m_slot->disconnect();
	m_slot = nullptr;
}

inline bool Connection::connected() const
{
	return m_slot && m_slot->previous;
}

//////////////////////////////////////////////////////////////////////////
// DefaultCombiner
//////////////////////////////////////////////////////////////////////////

template<typename T>
struct DefaultCombiner
{

	template<typename InputIterator>
	T operator()( InputIterator first, InputIterator last ) const
	{
		if constexpr( std::is_void_v<T> )
		{
			while( first != last )
			{
				*first;
				++first;
			}
		}
		else
		{
			T r = T();
			while( first != last )
			{
				r = *first;
				++first;
			}
			return r;
		}
	}

};

//////////////////////////////////////////////////////////////////////////
// Signal
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
Signal<Result( Args... ), Combiner>::Signal( const Combiner &combiner )
	:	m_lastSlotAndCombiner( nullptr, combiner )
{
}

template<typename Result, typename... Args, typename Combiner>
Signal<Result( Args... ), Combiner>::~Signal()
{
	disconnectAllSlots();
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connect( const SlotFunctor &slot )
{
	return connectInternal( slot, /* front = */ false );
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connectFront( const SlotFunctor &slot )
{
	return connectInternal( slot, /* front = */ true );
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
Connection Signal<Result( Args... ), Combiner>::connectInternal( const SlotFunctor &slot, bool front )
{
	if( !lastSlot() )
	{
		assert( !m_firstSlot );
		lastSlot() = new Slot( m_firstSlot );
	}

	Private::SlotBase::Ptr s = new Slot(
		front ? m_firstSlot : *(lastSlot()->previous), slot
	);

	const Connection result = Connection( s );
	Trackable::trackConnection( slot, result );
	return result;
}

template<typename Result, typename... Args, typename Combiner>
template<typename SlotFunctor>
void Signal<Result( Args... ), Combiner>::disconnect( const SlotFunctor &slotFunctor )
{
	Private::SlotBase::Ptr slot = m_firstSlot;
	while( slot != lastSlot() )
	{
		if( static_cast<Slot *>( slot.get() )->function == slotFunctor )
		{
			slot->disconnect();
		}
		slot = slot->next;
	}
};

template<typename Result, typename... Args, typename Combiner>
Result Signal<Result( Args... ), Combiner>::operator() ( Args... args ) const
{
	ArgsTuple argsTuple( args... ); /// \todo : Capture by reference? Or forward_as_tuple?
	return combiner()(
		SlotCallIterator( m_firstSlot, argsTuple ),
		SlotCallIterator( nullptr, argsTuple )
	);
}

template<typename Result, typename... Args, typename Combiner>
void Signal<Result( Args... ), Combiner>::disconnectAllSlots()
{
	while( m_firstSlot != lastSlot() )
	{
		m_firstSlot->disconnect();
	}
	assert( m_firstSlot == lastSlot() );
};

template<typename Result, typename... Args, typename Combiner>
size_t Signal<Result( Args... ), Combiner>::numSlots() const
{
	size_t result = 0;
	const Private::SlotBase *s = m_firstSlot.get();
	while( s != lastSlot().get() )
	{
		result++;
		s = s->next.get();
	}
	return result;
};

template<typename Result, typename... Args, typename Combiner>
bool Signal<Result( Args... ), Combiner>::empty() const
{
	return m_firstSlot == lastSlot();
}

template<typename Result, typename... Args, typename Combiner>
Private::SlotBase::Ptr &Signal<Result( Args... ), Combiner>::lastSlot()
{
	return m_lastSlotAndCombiner.first();
}

template<typename Result, typename... Args, typename Combiner>
const Private::SlotBase::Ptr &Signal<Result( Args... ), Combiner>::lastSlot() const
{
	return m_lastSlotAndCombiner.first();
}

template<typename Result, typename... Args, typename Combiner>
const Combiner &Signal<Result( Args... ), Combiner>::combiner() const
{
	return m_lastSlotAndCombiner.second();
}

//////////////////////////////////////////////////////////////////////////
// Signal::Slot
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
struct Signal<Result( Args... ), Combiner>::Slot : public Private::SlotBase
{

	/// \todo We'd like to use `std::function` here, but it doesn't support
	/// equality comparison, which makes implementing
	/// `Signal::disconnect( const SlotFunctor & )` hard to implement. Perhaps
	/// it's achievable using `function::target_type` and `function::target`?
	using FunctionType = boost::function<Result( Args... )>;

	Slot( Private::SlotBase::Ptr &previous, const FunctionType &function = FunctionType() )
		: 	SlotBase( previous ), function( function )
	{
	}

	void disconnect() override
	{
		// `SlotBase::disconnect()` might (or might not) destroy us.
		// Extend our lifetime to the end of this function.
		const Ptr lifePreserver( this );
		const bool wasConnected = previous;
		Private::SlotBase::disconnect();
		if( wasConnected && !calling )
		{
			// Clear our function, and any resources that are bound inside it.
			// Note : this can cause arbitrary code to run, including the
			// destruction of ScopedConnections for this slot, causing reentrant
			// calls to `this->disconnect()`. We use `wasConnected` to protect
			// against the double-clear this could otherwise cause.
			function.clear();
		}
	}

	Result operator()( Args... args )
	{
		CallScope callScope( *this );
		return function( args... );
	}

	struct CallScope : private boost::noncopyable
	{
		CallScope( Slot &slot )
			:	slot( slot )
		{
			// Slot can't be called if not connected
			assert( slot.previous );
			slot.calling = true;
		}
		~CallScope()
		{
			slot.calling = false;
			if( !slot.previous )
			{
				// Slot was disconnected during call, and we couldn't
				// clear the function while it was being called. Clear
				// it now instead.
				slot.function.clear();
			}
		}
		Slot &slot;
	};

	FunctionType function;

};

//////////////////////////////////////////////////////////////////////////
// Signal::SlotCallIterator
//////////////////////////////////////////////////////////////////////////

template<typename Result, typename... Args, typename Combiner>
class Signal<Result( Args... ), Combiner>::SlotCallIterator : public boost::iterator_facade<
	SlotCallIterator,
	SlotCallIteratorValueType,
	boost::single_pass_traversal_tag
>
{

	public :

		SlotCallIterator( const Private::SlotBase::Ptr &slot, const ArgsTuple &args )
			:	m_slot( slot ), m_args( args )
		{
			skipBlocked();
		}

	private :

		friend class boost::iterator_core_access;

		SlotCallIteratorValueType &dereference() const
		{
			if( !m_value )
			{
				if constexpr( std::is_void_v<Result> )
				{
					std::apply( static_cast<Slot &>( *m_slot ), m_args );
					m_value = true;
				}
				else
				{
					m_value = std::apply( static_cast<Slot &>( *m_slot ), m_args );
				}
			}
			return *m_value;
		}

		void increment()
		{
			assert( !atEnd() );
			m_slot = m_slot->next;
			m_value.reset();
			skipBlocked();
		}

		bool atEnd() const
		{
			return !m_slot || !m_slot->next;
		}

		bool equal( const SlotCallIterator &other ) const
		{
			if( atEnd() )
			{
				return other.atEnd();
			}
			else
			{
				return m_slot == other.m_slot;
			}
		}

		void skipBlocked()
		{
			while( !atEnd() && static_cast<Slot *>( m_slot.get() )->blocked )
			{
				m_slot = m_slot->next;
			}
		}

		Private::SlotBase::Ptr m_slot;
		const ArgsTuple &m_args;

		mutable std::optional<SlotCallIteratorValueType> m_value;

};

//////////////////////////////////////////////////////////////////////////
// CatchingCombiner
//////////////////////////////////////////////////////////////////////////

template<typename T>
struct CatchingCombiner
{

	template<typename InputIterator>
	T operator()( InputIterator first, InputIterator last ) const
	{
		if constexpr( std::is_void_v<T> )
		{
			while( first != last )
			{
				try
				{
					*first;
				}
				catch( const std::exception &e )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", e.what() );
				}
				catch( ... )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", "Unknown error" );
				}
				++first;
			}
		}
		else
		{
			T r = T();
			while( first != last )
			{
				try
				{
					r = *first;
				}
				catch( const std::exception &e )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", e.what() );
				}
				catch( ... )
				{
					IECore::msg( IECore::Msg::Error, "Emitting signal", "Unknown error" );
				}
				++first;
			}
			return r;
		}
	}

};

//////////////////////////////////////////////////////////////////////////
// Trackable
//////////////////////////////////////////////////////////////////////////

inline Trackable::~Trackable()
{
	if( m_connections )
	{
		for( auto &c : *m_connections )
		{
			c.disconnect();
		}
	}
}

struct Trackable::TrackableVisitor
{

	TrackableVisitor( const Connection &connection ) : connection( connection ) {}

	template<typename T>
	void operator()( T &x ) const
	{
		if constexpr( std::is_base_of_v<Trackable, T> )
		{
			const Trackable &trackable = static_cast<const Trackable &>( x );
			if( !trackable.m_connections )
			{
				trackable.m_connections = std::make_unique<std::vector<Connection>>();
			}
			trackable.m_connections->push_back( connection );
		}
		else if constexpr( std::is_pointer_v<T> )
		{
			this->operator()( *x );
		}
	}

	const Connection &connection;

};

template<typename SlotFunctor>
void Trackable::trackConnection( const SlotFunctor &slotFunctor, const Connection &connection )
{
	/// \todo Should we purge expired connections periodically?

	// Rather than call `boost::visit_each()` explicitly, we must bring it into
	// scope with `using` and then just call `visit_each()`. This allows
	// argument-dependent-lookup to find specialised versions of `visit_each()`
	// for types in other namespaces.
	using boost::visit_each;

	// `visit_each` allows us to discover Trackable objects inside callables
	// such as `boost::bind()`. Note that `std::bind()` is _not_ supported.
	TrackableVisitor visitor( connection );
	visit_each(
		visitor,
		slotFunctor,
		0
	);
}

//////////////////////////////////////////////////////////////////////////
// ScopedConnection
//////////////////////////////////////////////////////////////////////////

inline ScopedConnection::ScopedConnection( const Connection &connection )
	:	Connection( connection )
{
}

inline ScopedConnection::~ScopedConnection()
{
	disconnect();
}

inline ScopedConnection &ScopedConnection::operator=( const Connection &connection )
{
	disconnect();
	Connection::operator=( connection );
	return *this;
}

//////////////////////////////////////////////////////////////////////////
// BlockedConnection
//////////////////////////////////////////////////////////////////////////

inline BlockedConnection::BlockedConnection( Signals::Connection &connection, bool block )
	:	m_connection( nullptr )
{
	if( block && connection.connected() )
	{
		m_connection = &connection;
		m_connection->block();
	}
}

inline BlockedConnection::~BlockedConnection()
{
	if( m_connection )
	{
		m_connection->unblock();
	}
}

} // namespace Gaffer::Signals

#endif // GAFFER_SIGNALS_INL
<|endoftext|>


<commit_before>// file PWScore.cpp
//-----------------------------------------------------------------------------

#if (defined(_MSC_VER) && (_MSC_VER >= 1300))
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
#else
#include <fstream.h> // for WritePlaintextFile
#include <vector.h>
#include <iostream.h>
#endif

#include "PWScore.h"
#include "BlowFish.h"

PWScore::PWScore() : m_currfile(_T("")), m_changed(false),
		     m_usedefuser(false), m_defusername(_T("")),
		     m_ReadFileVersion(PWSfile::UNKNOWN_VERSION),
		     m_passkey(NULL), m_passkey_len(0)
{
  int i;

  srand((unsigned)time(NULL));
  CItemData::SetSessionKey(); // per-session initialization
  for (i = 0; i < sizeof(m_session_key); i++)
    m_session_key[i] = newrand();
  for (i = 0; i < sizeof(m_session_salt); i++)
    m_session_salt[i] = newrand();

}

PWScore::~PWScore()
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }
}

void
PWScore::ClearData(void)
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
    m_passkey_len = 0;
  }
  //Composed of ciphertext, so doesn't need to be overwritten
  m_pwlist.RemoveAll();
}

void
PWScore::NewFile(const CMyString &passkey)
{
   ClearData();
   SetPassKey(passkey);
   m_changed = false;
}

int
PWScore::WriteFile(const CMyString &filename, PWSfile::VERSION version)
{
  PWSfile out(filename, GetPassKey());

  int status;

  status = out.OpenWriteFile(version);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      out.WriteRecord(temp);
      m_pwlist.GetNext(listPos);
    }
  out.CloseFile();

  m_changed = FALSE;
  m_ReadFileVersion = version; // needed when saving a V17 as V20 1st time [871893]

  return SUCCESS;
}

int
PWScore::WritePlaintextFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      of << temp.GetPlaintext('\t') << endl;
      m_pwlist.GetNext(listPos);
    }
  of.close();

  return SUCCESS;
}

/*
int
PWScore::WriteXMLFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  of << "<?xml version=\"1.0\">" << endl;
  of << "<passwordsafe>" << endl;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      CItemData temp = m_pwlist.GetAt(listPos);

      of << "  <entry>" << endl;
      // TODO: need to handle entity escaping of values.
      of << "    <group>" << temp.GetGroup() << "</group>" << endl;
      of << "    <title>" << temp.GetTitle() << "</title>" << endl;
      of << "    <username>" << temp.GetUser() << "</username>" << endl;
      of << "    <password>" << temp.GetPassword() << "</password>" << endl;
      of << "    <notes>" << temp.GetNotes() << "</notes>" << endl;
      of << "  </entry>" << endl;

      m_pwlist.GetNext(listPos);
    }
  of << "</passwordsafe>" << endl;
  of.close();

  return SUCCESS;
}
*/

int
PWScore::ImportPlaintextFile(const CMyString &filename)
{
    static const char *ImportedPrefix = { "Imported" };
    ifstream ifs(filename);

    if (!ifs)
        return CANT_OPEN_FILE;

    for (;;)
    {
        // read a single line.
        string linebuf;
        if (!getline(ifs, linebuf, '\n')) break;

        // remove MS-DOS linebreaks, if needed.
        if (!linebuf.empty() && *(linebuf.end() - 1) == '\r') {
            linebuf.resize(linebuf.size() - 1);
        }

        // tokenize into separate elements
        vector<string> tokens;
        for (int startpos = 0; ; ) {
            int nextchar = linebuf.find_first_of('\t', startpos);
            if (nextchar >= 0 && tokens.size() < 3) {
                tokens.push_back(linebuf.substr(startpos, nextchar - startpos));
                startpos = nextchar + 1;
            } else {
                tokens.push_back(linebuf.substr(startpos));
                break;
            }
        }
        if (tokens.size() != 4) {
            ASSERT(0 && "did not find expected number of tokens");
            break;
        }

        // Start initializing the new record.
        CItemData temp;
        temp.CreateUUID();
        temp.SetUser(CMyString(tokens[1].c_str()));
        temp.SetPassword(CMyString(tokens[2].c_str()));

        // The group and title field are concatenated.
        const string &grouptitle = tokens[0];
        int lastdot = grouptitle.find_last_of('.');
        if (lastdot > 0) {
            CMyString newgroup(ImportedPrefix);
            newgroup += ".";
            newgroup += grouptitle.substr(0, lastdot).c_str();
            temp.SetGroup(newgroup);
            temp.SetTitle(grouptitle.substr(lastdot + 1).c_str());
        } else {
            temp.SetGroup(ImportedPrefix);
            temp.SetTitle(grouptitle.c_str());
        }

        // The notes field begins and ends with a double-quote, with
        // no special escaping of any other internal characters.
        string quotedNotes = tokens[3];
        if (!quotedNotes.empty() &&
            *quotedNotes.begin() == '\"' &&
            *(quotedNotes.end() - 1) == '\"')
        {
            quotedNotes = quotedNotes.substr(1, quotedNotes.size() - 2);
            temp.SetNotes(CMyString(quotedNotes.c_str()));
        }

        AddEntryToTail(temp);
    }
    ifs.close();

    // TODO: maybe return an error if the full end of the file was not reached?

    return SUCCESS;
}

int PWScore::CheckPassword(const CMyString &filename, CMyString& passkey)
{
  PWSfile f(filename, passkey);

  int status = f.CheckPassword();

  switch (status) {
  case PWSfile::SUCCESS:
    return SUCCESS;
  case PWSfile::CANT_OPEN_FILE:
    return CANT_OPEN_FILE;
  case PWSfile::WRONG_PASSWORD:
    return WRONG_PASSWORD;
  default:
    ASSERT(0);
    return status; // should never happen
  }
}

int
PWScore::ReadFile(const CMyString &a_filename,
                   const CMyString &a_passkey)
{
   //That passkey had better be the same one that came from CheckPassword(...)

   PWSfile in(a_filename, a_passkey);

  int status;

  m_ReadFileVersion = in.GetFileVersion();

  if (m_ReadFileVersion == PWSfile::UNKNOWN_VERSION)
    return UNKNOWN_VERSION;

  status = in.OpenReadFile(m_ReadFileVersion);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  // prepare handling of pre-2.0 DEFUSERCHR conversion
  if (m_ReadFileVersion == PWSfile::V17)
    in.SetDefUsername(m_defusername);

   ClearData(); //Before overwriting old data, but after opening the file...

   SetPassKey(a_passkey);

   CItemData temp;

   status = in.ReadRecord(temp);

   while (status == PWSfile::SUCCESS)
   {
      m_pwlist.AddTail(temp);
      status = in.ReadRecord(temp);
   }

   in.CloseFile();

   return SUCCESS;
}

int PWScore::RenameFile(const CMyString &oldname, const CMyString &newname)
{
  return PWSfile::RenameFile(oldname, newname);
}

void PWScore::ChangePassword(const CMyString &newPassword)
{

  SetPassKey(newPassword);
  m_changed = TRUE;
}

// Finds stuff based on title & user fields only
POSITION
PWScore::Find(const CMyString &a_title, const CMyString &a_user)
{
   POSITION listPos = m_pwlist.GetHeadPosition();
   CMyString title, user;

   while (listPos != NULL)
   {
      title = m_pwlist.GetAt(listPos).GetTitle();
      user = m_pwlist.GetAt(listPos).GetUser();
      if (title == a_title && user == a_user)
         break;
      else
         m_pwlist.GetNext(listPos);
   }

   return listPos;
}

void PWScore::EncryptPassword(const unsigned char *plaintext, int len,
			      unsigned char *ciphertext) const
{
  // ciphertext is ((len +7)/8)*8 bytes long
  BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				     m_session_salt, sizeof(m_session_salt));
  int BlockLength = ((len + 7)/8)*8;
  unsigned char curblock[8];

  for (int x=0;x<BlockLength;x+=8) {
    int i;
    if ((len == 0) ||
	((len%8 != 0) && (len - x < 8))) {
      //This is for an uneven last block
      memset(curblock, 0, 8);
      for (i = 0; i < len %8; i++)
	curblock[i] = plaintext[x + i];
      } else
	for (i = 0; i < 8; i++)
	  curblock[i] = plaintext[x + i];
      Algorithm->Encrypt(curblock, curblock);
      memcpy(ciphertext + x, curblock, 8);
   }
   trashMemory(curblock, 8);
  delete Algorithm;
}

void PWScore::SetPassKey(const CMyString &new_passkey)
{
  // if changing, clear old
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }

  m_passkey_len = new_passkey.GetLength();

  int BlockLength = ((m_passkey_len + 7)/8)*8;
  m_passkey = new unsigned char[BlockLength];
  LPCTSTR plaintext = LPCTSTR(new_passkey);
  EncryptPassword((const unsigned char *)plaintext, m_passkey_len,
		  m_passkey);
}

CMyString PWScore::GetPassKey() const
{
  CMyString retval(_T(""));
  if (m_passkey_len > 0) {
    unsigned int BlockLength = ((m_passkey_len + 7)/8)*8;
    BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				       m_session_salt, sizeof(m_session_salt));
    unsigned char curblock[8];

    for (unsigned int x = 0; x < BlockLength; x += 8) {
      unsigned int i;
      for (i = 0; i < 8; i++)
	curblock[i] = m_passkey[x + i];
      Algorithm->Decrypt(curblock, curblock);
      for (i = 0; i < 8; i++)
	if (x + i < m_passkey_len)
	  retval += curblock[i];
    }
    trashMemory(curblock, 8);
    delete Algorithm;
  }
  return retval;
}
<commit_msg>Compiles (with warnings!) under VC6.0<commit_after>// file PWScore.cpp
//-----------------------------------------------------------------------------

#include <fstream> // for WritePlaintextFile
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include "PWScore.h"
#include "BlowFish.h"

PWScore::PWScore() : m_currfile(_T("")), m_changed(false),
		     m_usedefuser(false), m_defusername(_T("")),
		     m_ReadFileVersion(PWSfile::UNKNOWN_VERSION),
		     m_passkey(NULL), m_passkey_len(0)
{
  int i;

  srand((unsigned)time(NULL));
  CItemData::SetSessionKey(); // per-session initialization
  for (i = 0; i < sizeof(m_session_key); i++)
    m_session_key[i] = newrand();
  for (i = 0; i < sizeof(m_session_salt); i++)
    m_session_salt[i] = newrand();

}

PWScore::~PWScore()
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }
}

void
PWScore::ClearData(void)
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
    m_passkey_len = 0;
  }
  //Composed of ciphertext, so doesn't need to be overwritten
  m_pwlist.RemoveAll();
}

void
PWScore::NewFile(const CMyString &passkey)
{
   ClearData();
   SetPassKey(passkey);
   m_changed = false;
}

int
PWScore::WriteFile(const CMyString &filename, PWSfile::VERSION version)
{
  PWSfile out(filename, GetPassKey());

  int status;

  status = out.OpenWriteFile(version);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      out.WriteRecord(temp);
      m_pwlist.GetNext(listPos);
    }
  out.CloseFile();

  m_changed = FALSE;
  m_ReadFileVersion = version; // needed when saving a V17 as V20 1st time [871893]

  return SUCCESS;
}

int
PWScore::WritePlaintextFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      of << temp.GetPlaintext('\t') << endl;
      m_pwlist.GetNext(listPos);
    }
  of.close();

  return SUCCESS;
}

/*
int
PWScore::WriteXMLFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  of << "<?xml version=\"1.0\">" << endl;
  of << "<passwordsafe>" << endl;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      CItemData temp = m_pwlist.GetAt(listPos);

      of << "  <entry>" << endl;
      // TODO: need to handle entity escaping of values.
      of << "    <group>" << temp.GetGroup() << "</group>" << endl;
      of << "    <title>" << temp.GetTitle() << "</title>" << endl;
      of << "    <username>" << temp.GetUser() << "</username>" << endl;
      of << "    <password>" << temp.GetPassword() << "</password>" << endl;
      of << "    <notes>" << temp.GetNotes() << "</notes>" << endl;
      of << "  </entry>" << endl;

      m_pwlist.GetNext(listPos);
    }
  of << "</passwordsafe>" << endl;
  of.close();

  return SUCCESS;
}
*/

int
PWScore::ImportPlaintextFile(const CMyString &filename)
{
    static const char *ImportedPrefix = { "Imported" };
    ifstream ifs(filename);

    if (!ifs)
        return CANT_OPEN_FILE;

    for (;;)
    {
        // read a single line.
        string linebuf;
        if (!getline(ifs, linebuf, '\n')) break;

        // remove MS-DOS linebreaks, if needed.
        if (!linebuf.empty() && *(linebuf.end() - 1) == '\r') {
            linebuf.resize(linebuf.size() - 1);
        }

        // tokenize into separate elements
        vector<string> tokens;
        for (int startpos = 0; ; ) {
            int nextchar = linebuf.find_first_of('\t', startpos);
            if (nextchar >= 0 && tokens.size() < 3) {
                tokens.push_back(linebuf.substr(startpos, nextchar - startpos));
                startpos = nextchar + 1;
            } else {
                tokens.push_back(linebuf.substr(startpos));
                break;
            }
        }
        if (tokens.size() != 4) {
            ASSERT(0 && "did not find expected number of tokens");
            break;
        }

        // Start initializing the new record.
        CItemData temp;
        temp.CreateUUID();
        temp.SetUser(CMyString(tokens[1].c_str()));
        temp.SetPassword(CMyString(tokens[2].c_str()));

        // The group and title field are concatenated.
        const string &grouptitle = tokens[0];
        int lastdot = grouptitle.find_last_of('.');
        if (lastdot > 0) {
            CMyString newgroup(ImportedPrefix);
            newgroup += ".";
            newgroup += grouptitle.substr(0, lastdot).c_str();
            temp.SetGroup(newgroup);
            temp.SetTitle(grouptitle.substr(lastdot + 1).c_str());
        } else {
            temp.SetGroup(ImportedPrefix);
            temp.SetTitle(grouptitle.c_str());
        }

        // The notes field begins and ends with a double-quote, with
        // no special escaping of any other internal characters.
        string quotedNotes = tokens[3];
        if (!quotedNotes.empty() &&
            *quotedNotes.begin() == '\"' &&
            *(quotedNotes.end() - 1) == '\"')
        {
            quotedNotes = quotedNotes.substr(1, quotedNotes.size() - 2);
            temp.SetNotes(CMyString(quotedNotes.c_str()));
        }

        AddEntryToTail(temp);
    }
    ifs.close();

    // TODO: maybe return an error if the full end of the file was not reached?

    return SUCCESS;
}

int PWScore::CheckPassword(const CMyString &filename, CMyString& passkey)
{
  PWSfile f(filename, passkey);

  int status = f.CheckPassword();

  switch (status) {
  case PWSfile::SUCCESS:
    return SUCCESS;
  case PWSfile::CANT_OPEN_FILE:
    return CANT_OPEN_FILE;
  case PWSfile::WRONG_PASSWORD:
    return WRONG_PASSWORD;
  default:
    ASSERT(0);
    return status; // should never happen
  }
}

int
PWScore::ReadFile(const CMyString &a_filename,
                   const CMyString &a_passkey)
{
   //That passkey had better be the same one that came from CheckPassword(...)

   PWSfile in(a_filename, a_passkey);

  int status;

  m_ReadFileVersion = in.GetFileVersion();

  if (m_ReadFileVersion == PWSfile::UNKNOWN_VERSION)
    return UNKNOWN_VERSION;

  status = in.OpenReadFile(m_ReadFileVersion);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  // prepare handling of pre-2.0 DEFUSERCHR conversion
  if (m_ReadFileVersion == PWSfile::V17)
    in.SetDefUsername(m_defusername);

   ClearData(); //Before overwriting old data, but after opening the file...

   SetPassKey(a_passkey);

   CItemData temp;

   status = in.ReadRecord(temp);

   while (status == PWSfile::SUCCESS)
   {
      m_pwlist.AddTail(temp);
      status = in.ReadRecord(temp);
   }

   in.CloseFile();

   return SUCCESS;
}

int PWScore::RenameFile(const CMyString &oldname, const CMyString &newname)
{
  return PWSfile::RenameFile(oldname, newname);
}

void PWScore::ChangePassword(const CMyString &newPassword)
{

  SetPassKey(newPassword);
  m_changed = TRUE;
}

// Finds stuff based on title & user fields only
POSITION
PWScore::Find(const CMyString &a_title, const CMyString &a_user)
{
   POSITION listPos = m_pwlist.GetHeadPosition();
   CMyString title, user;

   while (listPos != NULL)
   {
      title = m_pwlist.GetAt(listPos).GetTitle();
      user = m_pwlist.GetAt(listPos).GetUser();
      if (title == a_title && user == a_user)
         break;
      else
         m_pwlist.GetNext(listPos);
   }

   return listPos;
}

void PWScore::EncryptPassword(const unsigned char *plaintext, int len,
			      unsigned char *ciphertext) const
{
  // ciphertext is ((len +7)/8)*8 bytes long
  BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				     m_session_salt, sizeof(m_session_salt));
  int BlockLength = ((len + 7)/8)*8;
  unsigned char curblock[8];

  for (int x=0;x<BlockLength;x+=8) {
    int i;
    if ((len == 0) ||
	((len%8 != 0) && (len - x < 8))) {
      //This is for an uneven last block
      memset(curblock, 0, 8);
      for (i = 0; i < len %8; i++)
	curblock[i] = plaintext[x + i];
      } else
	for (i = 0; i < 8; i++)
	  curblock[i] = plaintext[x + i];
      Algorithm->Encrypt(curblock, curblock);
      memcpy(ciphertext + x, curblock, 8);
   }
   trashMemory(curblock, 8);
  delete Algorithm;
}

void PWScore::SetPassKey(const CMyString &new_passkey)
{
  // if changing, clear old
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }

  m_passkey_len = new_passkey.GetLength();

  int BlockLength = ((m_passkey_len + 7)/8)*8;
  m_passkey = new unsigned char[BlockLength];
  LPCTSTR plaintext = LPCTSTR(new_passkey);
  EncryptPassword((const unsigned char *)plaintext, m_passkey_len,
		  m_passkey);
}

CMyString PWScore::GetPassKey() const
{
  CMyString retval(_T(""));
  if (m_passkey_len > 0) {
    unsigned int BlockLength = ((m_passkey_len + 7)/8)*8;
    BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				       m_session_salt, sizeof(m_session_salt));
    unsigned char curblock[8];

    for (unsigned int x = 0; x < BlockLength; x += 8) {
      unsigned int i;
      for (i = 0; i < 8; i++)
	curblock[i] = m_passkey[x + i];
      Algorithm->Decrypt(curblock, curblock);
      for (i = 0; i < 8; i++)
	if (x + i < m_passkey_len)
	  retval += curblock[i];
    }
    trashMemory(curblock, 8);
    delete Algorithm;
  }
  return retval;
}
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Team Shanghai Alice support plugin
  *
  * ----
  *
  * Translation of generic plaintext with a fixed number of lines.
  */

#include <thcrap.h>

typedef struct {
	const json_t *file;
	size_t key_len;
	char *key;
	size_t line;
} gentext_cache_t;

THREAD_LOCAL(gentext_cache_t, gc_tls, nullptr, nullptr);

int gentext_cache_key_set(gentext_cache_t *gc, const char *key, size_t key_len)
{
	if(gc->key && !strncmp(gc->key, key, gc->key_len)) {
		return 1;
	}
	if(key_len > gc->key_len) {
		char *key_realloc = (char *)realloc(gc->key, key_len);
		if(key_realloc) {
			gc->key = key_realloc;
			gc->key_len = key_len;
		} else {
			SAFE_FREE(gc->key);
			gc->key_len = 0;
		}
	}
	if(gc->key) {
		strncpy(gc->key, key, key_len);
	}
	gc->line = 0;
	return 0;
}

int BP_gentext(x86_reg_t *regs, json_t *bp_info)
{
	gentext_cache_t *gc = gc_tls_get();

	// Parameters
	// ----------
	json_t *strs = json_object_get(bp_info, "str");
	const char *file = json_object_get_string(bp_info, "file");
	json_t *ids = json_object_get(bp_info, "ids");
	json_t *line_obj = json_object_get(bp_info, "line");
	// ----------
	if(file) {
		gc->file = jsondata_game_get(file);
	}
	if(ids) {
		const size_t value_len = DECIMAL_DIGITS_BOUND(size_t);
		size_t key_new_len = value_len * json_flex_array_size(ids) + 1;
		VLA(char, key_new, key_new_len);
		char *p = key_new;
		json_t *id;
		json_flex_array_foreach_scoped(size_t, i, ids, id) {
			char id_str[value_len + 1];
			const char *q = id_str;
			size_t id_val = json_immediate_value(id, regs);
			snprintf(id_str, sizeof(id_str), "%u", id_val);
			if(i > 0) {
				*p++ = '_';
			}
			while(*p++ = *q++);
			p--;
		}
		gentext_cache_key_set(gc, key_new, key_new_len);
		VLA_FREE(key_new);
	}
	if(line_obj) {
		gc->line = json_immediate_value(line_obj, regs);
	}
	if(strs) {
		json_t *id_val = json_object_get(gc->file, gc->key);
		json_t *str;

		// More straightforward if we ensure that everything below is valid.
		if(json_flex_array_size(id_val) == 0) {
			return 1;
		}

		json_flex_array_foreach_scoped(size_t, i, strs, str) {
			const char **target = (const char **)json_pointer_value(str, regs);
			const char *line = json_flex_array_get_string_safe(id_val, gc->line++);
			assert(line);
			*target = line;
		}

		if (size_t eip_jump_dist = json_object_get_immediate(bp_info, regs, "eip_jump_dist")) {
			regs->retaddr += eip_jump_dist;
			return 0;
		}

		regs->esp += json_object_get_immediate(bp_info, regs, "stack_clear_size");

		return breakpoint_cave_exec_flag_eval(regs, bp_info);
	}
	return 1;
}

int gentext_mod_init(void)
{
	// Resolve all necessary files in advance
	const char *prefix = "gentext";
	size_t prefix_len = strlen(prefix);
	json_t *breakpoints = json_object_get(runconfig_json_get(), "breakpoints");
	const char *key;
	json_t *val;
	json_object_foreach_fast(breakpoints, key, val) {
		if(!strncmp(key, prefix, prefix_len)) {
			const char *file = json_object_get_string(val, "file");
			if(!jsondata_game_get(file)) {
				jsondata_game_add(file);
			}
		}
	}
	return 0;
}
<commit_msg>TSA: allow negative numbers in gentext<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Team Shanghai Alice support plugin
  *
  * ----
  *
  * Translation of generic plaintext with a fixed number of lines.
  */

#include <thcrap.h>

typedef struct {
	const json_t *file;
	size_t key_len;
	char *key;
	size_t line;
} gentext_cache_t;

THREAD_LOCAL(gentext_cache_t, gc_tls, nullptr, nullptr);

int gentext_cache_key_set(gentext_cache_t *gc, const char *key, size_t key_len)
{
	if(gc->key && !strncmp(gc->key, key, gc->key_len)) {
		return 1;
	}
	if(key_len > gc->key_len) {
		char *key_realloc = (char *)realloc(gc->key, key_len);
		if(key_realloc) {
			gc->key = key_realloc;
			gc->key_len = key_len;
		} else {
			SAFE_FREE(gc->key);
			gc->key_len = 0;
		}
	}
	if(gc->key) {
		strncpy(gc->key, key, key_len);
	}
	gc->line = 0;
	return 0;
}

int BP_gentext(x86_reg_t *regs, json_t *bp_info)
{
	gentext_cache_t *gc = gc_tls_get();

	// Parameters
	// ----------
	json_t *strs = json_object_get(bp_info, "str");
	const char *file = json_object_get_string(bp_info, "file");
	json_t *ids = json_object_get(bp_info, "ids");
	json_t *line_obj = json_object_get(bp_info, "line");
	// ----------
	if(file) {
		gc->file = jsondata_game_get(file);
	}
	if(ids) {
		const size_t value_len = DECIMAL_DIGITS_BOUND(size_t);
		size_t key_new_len = value_len * json_flex_array_size(ids) + 1;
		VLA(char, key_new, key_new_len);
		char *p = key_new;
		json_t *id;
		json_flex_array_foreach_scoped(size_t, i, ids, id) {
			char id_str[value_len + 1];
			const char *q = id_str;
			size_t id_val = json_immediate_value(id, regs);
			snprintf(id_str, sizeof(id_str), "%d", id_val);
			if(i > 0) {
				*p++ = '_';
			}
			while(*p++ = *q++);
			p--;
		}
		gentext_cache_key_set(gc, key_new, key_new_len);
		VLA_FREE(key_new);
	}
	if(line_obj) {
		gc->line = json_immediate_value(line_obj, regs);
	}
	if(strs) {
		json_t *id_val = json_object_get(gc->file, gc->key);
		json_t *str;

		// More straightforward if we ensure that everything below is valid.
		if(json_flex_array_size(id_val) == 0) {
			return 1;
		}

		json_flex_array_foreach_scoped(size_t, i, strs, str) {
			const char **target = (const char **)json_pointer_value(str, regs);
			const char *line = json_flex_array_get_string_safe(id_val, gc->line++);
			assert(line);
			*target = line;
		}

		if (size_t eip_jump_dist = json_object_get_immediate(bp_info, regs, "eip_jump_dist")) {
			regs->retaddr += eip_jump_dist;
			return 0;
		}

		regs->esp += json_object_get_immediate(bp_info, regs, "stack_clear_size");

		return breakpoint_cave_exec_flag_eval(regs, bp_info);
	}
	return 1;
}

int gentext_mod_init(void)
{
	// Resolve all necessary files in advance
	const char *prefix = "gentext";
	size_t prefix_len = strlen(prefix);
	json_t *breakpoints = json_object_get(runconfig_json_get(), "breakpoints");
	const char *key;
	json_t *val;
	json_object_foreach_fast(breakpoints, key, val) {
		if(!strncmp(key, prefix, prefix_len)) {
			const char *file = json_object_get_string(val, "file");
			if(!jsondata_game_get(file)) {
				jsondata_game_add(file);
			}
		}
	}
	return 0;
}
<|endoftext|>