<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * Driver for OTV0P2BASE_SensorAmbientLightOccupancy tests.
 */

#include <stdint.h>
#include <gtest/gtest.h>
#include <OTV0p2Base.h>
#include "OTV0P2BASE_SensorAmbientLightOccupancy.h"

// Sanity test.
TEST(AmbientLightOccupancyDetection,SanityTest)
{
    EXPECT_EQ(42, 42);
}

// Basic test of update() behaviour.
TEST(AmbientLightOccupancyDetection,updateBasics)
{
    // Check that initial update never indicates occupancy.
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
    EXPECT_FALSE(ds1.update(0)) << "no initial update should imply occupancy";
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds2;
    EXPECT_FALSE(ds2.update(255)) << "no initial update should imply occupancy";
    // Check that update from 0 to max does force occupancy indication (but steady does not).
    EXPECT_TRUE(ds1.update(255)) << "update from 0 to 255 (max) illumination should signal occupancy";
    EXPECT_FALSE(ds2.update(255)) << "unchanged 255 (max) light level should not imply occupancy";
}

class ALDataSample
    {
    public:
        const uint8_t d, H, M, L, expected;

		// Day/hour/minute and light level and expected result.
		// An expected result of 0 means no particular result expected from this (anything is acceptable).
		// An expected result of 1 means occupancy should NOT be reported for this sample.
		// An expected result of 2+ means occupancy should be reported for this sample.
	    ALDataSample(uint8_t dayOfMonth, uint8_t hour24, uint8_t minute, uint8_t lightLevel, uint8_t expectedResult = 0)
			: d(dayOfMonth), H(hour24), M(minute), L(lightLevel), expected(expectedResult)
			{ }

        // Create/mark a terminating entry; all input values invalid.
	    ALDataSample() : d(255), H(255), M(255), L(255), expected(0) { }

        // Compute current minute for this record.
        long currentMinute() const { return((((d * 24L) + H) * 60L) + M); }

	    // True for empty/termination data record.
	    bool isEnd() const { return(d > 31); }
    };

// Trivial sample, testing initial reaction to start transient.
static const ALDataSample trivialSample1[] =
    {
{ 0, 0, 0, 254, 1 }, // Should NOT predict occupancy on first tick.
{ 0, 0, 1, 0, 1 }, // Should NOT predict occupancy on falling level.
{ 0, 0, 5, 0 }, // Should NOT predict occupancy on falling level.
{ 0, 0, 9, 254, 2 }, // Should predict occupancy on level rising to (near) max.
{ }
    };

// Do a simple run over the supplied data, one call per simulated minute until the terminating record is found.
// Must be supplied in ascending order with a terminating (empty) entry.
// Repeated rows with the same light value and expected result can be omitted
// as they will be synthesised by this routine for each virtual minute until the next supplied item.
// Ensures that any required predictions/detections in either direction are met.
// Uses only the update() call.
void simpleDataSampleRun(const ALDataSample *const data, OTV0P2BASE::SensorAmbientLightOccupancyDetectorInterface *const detector)
    {
    ASSERT_TRUE(NULL != data);
    ASSERT_TRUE(NULL != detector);
    ASSERT_FALSE(data->isEnd()) << "do not pass in empty data set";
    for(const ALDataSample *dp = data; !dp->isEnd(); ++dp)
        {
    	long currentMinute = dp->currentMinute();
    	do  {
//            fprintf(stderr, "Mins: %ld\n", currentMinute);
            const bool prediction = detector->update(dp->L);
            const uint8_t expected = dp->expected;
            if(0 != expected)
                {
                // If a particular outcome was expected, test against it.
                const bool expectedOccupancy = (expected > 1);
                EXPECT_EQ(expectedOccupancy, prediction) << " @ " << int(dp->H) << ":" << int(dp->M) << " + " << (currentMinute - dp->currentMinute());
                }
            ++currentMinute;
    	    } while((!(dp+1)->isEnd()) && (currentMinute < (dp+1)->currentMinute()));
        }
	}

// Basic test of update() behaviour.
TEST(AmbientLightOccupancyDetection,simpleDataSampleRun)
{
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
	simpleDataSampleRun(trivialSample1, &ds1);
}

// "3l" 2016/10/08 test set with tough occupancy to detect in the evening up to 21:00Z and in the morning from 07:09Z then  06:37Z.
static const ALDataSample sample3lHard[] =
    {
{8,0,1,1, 1}, // Definitely not occupied.
{8,0,17,1, 1}, // Definitely not occupied.
//...
{8,6,21,1},
{8,6,29,2, 1}, // Not enough rise to indicate occupation.
{8,6,33,2},
{8,6,45,2},
{8,6,57,2},
{8,7,9,14},  // Occupied: curtains drawn?
{8,7,17,35},
{8,7,21,38},
{8,7,33,84},
{8,7,37,95},
{8,7,49,97, 1}, // Not enough rise to be occupation.
{8,7,57,93, 1}, // Fall is not indicative of occupation.
{8,8,5,98},
{8,8,13,98},
{8,8,17,93},
{8,8,25,79},
{8,8,33,103},
{8,8,41,118},
{8,8,49,106},
{8,8,53,92},
{8,8,57,103},
{8,9,5,104},
{8,9,21,138},
{8,9,29,132},
{8,9,33,134},
{8,9,45,121},
{8,9,53,125},
{8,10,5,140},
{8,10,9,114},
{8,10,17,121},
{8,10,21,126},
{8,10,25,114},
{8,10,29,107},
{8,10,41,169},
{8,10,49,177},
{8,10,57,126},
{8,11,1,117},
{8,11,5,114},
{8,11,13,111},
{8,11,17,132},
{8,11,21,157},
{8,11,29,177},
{8,11,33,176},
{8,11,45,174},
{8,11,49,181},
{8,11,57,182},
{8,12,9,181},
{8,12,13,182},
{8,12,29,175},
{8,12,45,161},
{8,12,53,169},
{8,13,1,176},
{8,13,5,177},
{8,13,9,178},
{8,13,25,158},
{8,13,29,135},
{8,13,37,30},
{8,13,45,37},
{8,13,49,45},
{8,14,5,61},
{8,14,17,117},
{8,14,29,175},
{8,14,33,171},
{8,14,37,148},
{8,14,45,141},
{8,14,53,173},
{8,15,5,125},
{8,15,13,119},
{8,15,21,107},
{8,15,29,58},
{8,15,37,62},
{8,15,45,54},
{8,15,53,47},
{8,16,1,35},
{8,16,9,48},
{8,16,25,50},
{8,16,37,39},
{8,16,41,34},
{8,16,49,34},
{8,16,57,28},
{8,17,5,20},
{8,17,13,7},
{8,17,25,4},
{8,17,37,44, 2}, // Occupied (light on?).
    {8,17,38,44},
{8,17,49,42},
{8,18,1,42},
{8,18,9,40},
{8,18,13,42, 1}, // Not enough rise to be occupation.
{8,18,25,40},
{8,18,37,40},
{8,18,41,42},
{8,18,49,42},
{8,18,57,41},
{8,19,1,40},
{8,19,13,41},
{8,19,21,39},
{8,19,25,41},
{8,19,41,41},
{8,19,52,42},
{8,19,57,40},
{8,20,5,40},
{8,20,9,42},
{8,20,17,42},
{8,20,23,40},
{8,20,29,40},
{8,20,33,40},
{8,20,37,41},
{8,20,41,42},
{8,20,49,40},
{8,21,5,1, 1}, // Definitely not occupied.
{8,21,13,1, 1}, // Definitely not occupied.
// ...
{9,5,57,1, 1}, // Definitely not occupied.
{9,6,13,1, 1}, // Definitely not occupied.
{9,6,21,2, 1}, // Not enough rise to indicate occupation.
{9,6,33,2},
{9,6,37,24, 2}, // Curtains drawn: occupied.
    {9,6,38,24},
{9,6,45,32},
{9,6,53,31},
{9,7,5,30},
{9,7,17,41},
{9,7,25,54},
{9,7,33,63},
{9,7,41,73},
{9,7,45,77},
{ }
    };

// Test with real data set.
TEST(AmbientLightOccupancyDetection,sample3lHard)
{
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
	simpleDataSampleRun(sample3lHard, &ds1);
}
<commit_msg>TODO-996: comments<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * Driver for OTV0P2BASE_SensorAmbientLightOccupancy tests.
 */

#include <stdint.h>
#include <gtest/gtest.h>
#include <OTV0p2Base.h>
#include "OTV0P2BASE_SensorAmbientLightOccupancy.h"

// Sanity test.
TEST(AmbientLightOccupancyDetection,SanityTest)
{
    EXPECT_EQ(42, 42);
}

// Basic test of update() behaviour.
TEST(AmbientLightOccupancyDetection,updateBasics)
{
    // Check that initial update never indicates occupancy.
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
    EXPECT_FALSE(ds1.update(0)) << "no initial update should imply occupancy";
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds2;
    EXPECT_FALSE(ds2.update(255)) << "no initial update should imply occupancy";
    // Check that update from 0 to max does force occupancy indication (but steady does not).
    EXPECT_TRUE(ds1.update(255)) << "update from 0 to 255 (max) illumination should signal occupancy";
    EXPECT_FALSE(ds2.update(255)) << "unchanged 255 (max) light level should not imply occupancy";
}

class ALDataSample
    {
    public:
        const uint8_t d, H, M, L, expected;

		// Day/hour/minute and light level and expected result.
		// An expected result of 0 means no particular result expected from this (anything is acceptable).
		// An expected result of 1 means occupancy should NOT be reported for this sample.
		// An expected result of 2+ means occupancy should be reported for this sample.
	    ALDataSample(uint8_t dayOfMonth, uint8_t hour24, uint8_t minute, uint8_t lightLevel, uint8_t expectedResult = 0)
			: d(dayOfMonth), H(hour24), M(minute), L(lightLevel), expected(expectedResult)
			{ }

        // Create/mark a terminating entry; all input values invalid.
	    ALDataSample() : d(255), H(255), M(255), L(255), expected(0) { }

        // Compute current minute for this record.
        long currentMinute() const { return((((d * 24L) + H) * 60L) + M); }

	    // True for empty/termination data record.
	    bool isEnd() const { return(d > 31); }
    };

// Trivial sample, testing initial reaction to start transient.
static const ALDataSample trivialSample1[] =
    {
{ 0, 0, 0, 254, 1 }, // Should NOT predict occupancy on first tick.
{ 0, 0, 1, 0, 1 }, // Should NOT predict occupancy on falling level.
{ 0, 0, 5, 0 }, // Should NOT predict occupancy on falling level.
{ 0, 0, 9, 254, 2 }, // Should predict occupancy on level rising to (near) max.
{ }
    };

// Do a simple run over the supplied data, one call per simulated minute until the terminating record is found.
// Must be supplied in ascending order with a terminating (empty) entry.
// Repeated rows with the same light value and expected result can be omitted
// as they will be synthesised by this routine for each virtual minute until the next supplied item.
// Ensures that any required predictions/detections in either direction are met.
// Uses only the update() call.
void simpleDataSampleRun(const ALDataSample *const data, OTV0P2BASE::SensorAmbientLightOccupancyDetectorInterface *const detector)
    {
    ASSERT_TRUE(NULL != data);
    ASSERT_TRUE(NULL != detector);
    ASSERT_FALSE(data->isEnd()) << "do not pass in empty data set";
    for(const ALDataSample *dp = data; !dp->isEnd(); ++dp)
        {
    	long currentMinute = dp->currentMinute();
    	do  {
//            fprintf(stderr, "Mins: %ld\n", currentMinute);
            const bool prediction = detector->update(dp->L);
            const uint8_t expected = dp->expected;
            if(0 != expected)
                {
                // If a particular outcome was expected, test against it.
                const bool expectedOccupancy = (expected > 1);
                EXPECT_EQ(expectedOccupancy, prediction) << " @ " << int(dp->H) << ":" << int(dp->M) << " + " << (currentMinute - dp->currentMinute());
                }
            ++currentMinute;
    	    } while((!(dp+1)->isEnd()) && (currentMinute < (dp+1)->currentMinute()));
        }
	}

// Basic test of update() behaviour.
TEST(AmbientLightOccupancyDetection,simpleDataSampleRun)
{
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
	simpleDataSampleRun(trivialSample1, &ds1);
}

// "3l" 2016/10/08 test set with tough occupancy to detect in the evening up to 21:00Z and in the morning from 07:09Z then  06:37Z.
static const ALDataSample sample3lHard[] =
    {
{8,0,1,1, 1}, // Definitely not occupied.
{8,0,17,1, 1}, // Definitely not occupied.
//...
{8,6,21,1},
{8,6,29,2, 1}, // Not enough rise to indicate occupation.
{8,6,33,2},
{8,6,45,2},
{8,6,57,2},
{8,7,9,14},  // OCCUPIED: curtains drawn?
{8,7,17,35},
{8,7,21,38},
{8,7,33,84},
{8,7,37,95},
{8,7,49,97, 1}, // Not enough rise to be occupation.
{8,7,57,93, 1}, // Fall is not indicative of occupation.
{8,8,5,98},
{8,8,13,98},
{8,8,17,93},
{8,8,25,79},
{8,8,33,103},
{8,8,41,118},
{8,8,49,106},
{8,8,53,92},
{8,8,57,103},
{8,9,5,104},
{8,9,21,138},
{8,9,29,132},
{8,9,33,134},
{8,9,45,121},
{8,9,53,125},
{8,10,5,140},
{8,10,9,114},
{8,10,17,121},
{8,10,21,126},
{8,10,25,114},
{8,10,29,107},
{8,10,41,169},
{8,10,49,177},
{8,10,57,126},
{8,11,1,117},
{8,11,5,114},
{8,11,13,111},
{8,11,17,132},
{8,11,21,157},
{8,11,29,177},
{8,11,33,176},
{8,11,45,174},
{8,11,49,181},
{8,11,57,182},
{8,12,9,181},
{8,12,13,182},
{8,12,29,175},
{8,12,45,161},
{8,12,53,169},
{8,13,1,176},
{8,13,5,177},
{8,13,9,178},
{8,13,25,158},
{8,13,29,135},
{8,13,37,30},
{8,13,45,37},
{8,13,49,45},
{8,14,5,61},
{8,14,17,117},
{8,14,29,175},
{8,14,33,171},
{8,14,37,148},
{8,14,45,141},
{8,14,53,173},
{8,15,5,125},
{8,15,13,119},
{8,15,21,107},
{8,15,29,58},
{8,15,37,62},
{8,15,45,54},
{8,15,53,47},
{8,16,1,35},
{8,16,9,48},
{8,16,25,50},
{8,16,37,39},
{8,16,41,34},
{8,16,49,34},
{8,16,57,28},
{8,17,5,20},
{8,17,13,7},
{8,17,25,4},
{8,17,37,44, 2}, // OCCUPIED (light on?).
    {8,17,38,44},
{8,17,49,42},
{8,18,1,42},
{8,18,9,40},
{8,18,13,42, 1}, // Not enough rise to be occupation.
{8,18,25,40},
{8,18,37,40},
{8,18,41,42},
{8,18,49,42},
{8,18,57,41},
{8,19,1,40},
{8,19,13,41},
{8,19,21,39},
{8,19,25,41},
{8,19,41,41},
{8,19,52,42},
{8,19,57,40},
{8,20,5,40},
{8,20,9,42},
{8,20,17,42},
{8,20,23,40},
{8,20,29,40},
{8,20,33,40},
{8,20,37,41},
{8,20,41,42},
{8,20,49,40},
{8,21,5,1, 1}, // Definitely not occupied.
{8,21,13,1, 1}, // Definitely not occupied.
// ...
{9,5,57,1, 1}, // Definitely not occupied.
{9,6,13,1, 1}, // Definitely not occupied.
{9,6,21,2, 1}, // Not enough rise to indicate occupation.
{9,6,33,2},
{9,6,37,24, 2}, // Curtains drawn: occupied.
    {9,6,38,24},
{9,6,45,32},
{9,6,53,31},
{9,7,5,30},
{9,7,17,41},
{9,7,25,54},
{9,7,33,63},
{9,7,41,73},
{9,7,45,77},
{ }
    };

// Test with real data set.
TEST(AmbientLightOccupancyDetection,sample3lHard)
{
	OTV0P2BASE::SensorAmbientLightOccupancyDetectorSimple ds1;
	simpleDataSampleRun(sample3lHard, &ds1);
}
<|endoftext|>


<commit_before>#include "oddlib/compressiontype6ae.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"
#include <vector>
#include <cassert>

namespace Oddlib
{
    static Uint8 ReadNibble(IStream& stream, bool readLo, Uint8& srcByte)
    {
        if (readLo)
        {
            return srcByte >> 4;
        }
        else
        {
            srcByte = ReadUInt8(stream);
            return srcByte & 0xF;
        }
    }

    // Function 0x0040A8A0 in AE
    std::vector<Uint8> CompressionType6Ae::Decompress(IStream& stream, Uint32 finalW, Uint32 w, Uint32 h, Uint32 /*dataSize*/)
    {
        std::vector<Uint8> out(finalW*h);

        Uint8 srcByte; // edx@2

        Sint32 byteCounter; // eax@3
        bool bReadLowNibble = false; // ebp@5
        Sint32 bits_and_byte_count; // eax@7
        Sint32 cnt; // ecx@7
        Sint32 bitCount; // eax@16

        union intUni
        {
            Uint8 b[4];
            int i;
        } dstByte;

        Sint32 heightCounter; // [sp+Ch] [bp-8h]@2
        Uint8 srcByteBits; // [sp+1Ch] [bp+8h]@5
        Uint8 srcByteBits2; // [sp+1Ch] [bp+8h]@13

        const Sint32 width = w;
        bool bReadLow = false;
        const Sint32 height = h;
        Sint32 bBitsWriten = 0;

        Uint32 dstPos = 0;

        if (height > 0)
        {
            srcByte = 0;
            heightCounter = height;
            do
            {
                byteCounter = 0;
                while (byteCounter < width)
                {
                    srcByteBits = ReadNibble(stream, bReadLow, srcByte);
                    bReadLowNibble = !bReadLow;

                    cnt = srcByteBits;
                    bits_and_byte_count = srcByteBits + byteCounter;
                    if (srcByteBits > 0)
                    {
                        do
                        {
                            if (bBitsWriten)
                            {
                                dstPos++;
                                bBitsWriten = 0;
                            }
                            else
                            {
                                out[dstPos] = 0;
                                bBitsWriten = 1;
                            }
                            --cnt;
                        } while (cnt);
                    }

                    srcByteBits2 = ReadNibble(stream, bReadLowNibble, srcByte);
                    bReadLow = !bReadLowNibble;

                    byteCounter = srcByteBits2 + bits_and_byte_count;
                    if (srcByteBits2 > 0)
                    {
                        bitCount = srcByteBits2;
                        do
                        {
                            dstByte.b[0] = ReadNibble(stream, bReadLow, srcByte);
                            bReadLow = !bReadLow;

                            if (bBitsWriten)
                            {
                                out[dstPos++] |= 16 * dstByte.b[0];
                                bBitsWriten = 0;
                            }
                            else
                            {
                                out[dstPos] = dstByte.b[0];
                                bBitsWriten = 1;
                            }
                        } while (--bitCount);
                    }
                }

                for (; byteCounter & 7; ++byteCounter)
                {
                    if (bBitsWriten)
                    {
                        dstPos++;
                        bBitsWriten = 0;
                    }
                    else
                    {
                        out[dstPos] = 0;
                        bBitsWriten = 1;
                    }
                }

            } while (heightCounter-- != 1);
        }
        return out;
    }
}
<commit_msg>reduce flags<commit_after>#include "oddlib/compressiontype6ae.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"
#include <vector>
#include <cassert>

namespace Oddlib
{
    static Uint8 ReadNibble(IStream& stream, bool readLo, Uint8& srcByte)
    {
        if (readLo)
        {
            return srcByte >> 4;
        }
        else
        {
            srcByte = ReadUInt8(stream);
            return srcByte & 0xF;
        }
    }

    // Function 0x0040A8A0 in AE
    std::vector<Uint8> CompressionType6Ae::Decompress(IStream& stream, Uint32 finalW, Uint32 w, Uint32 h, Uint32 /*dataSize*/)
    {
        std::vector<Uint8> out(finalW*h);

        Uint8 srcByte; // edx@2

        Uint32 byteCounter; // eax@3
        bool bNibbleToRead = false; // ebp@5
        Sint32 bits_and_byte_count; // eax@7
        Sint32 cnt; // ecx@7
        Sint32 bitCount; // eax@16

        union intUni
        {
            Uint8 b[4];
            int i;
        } dstByte;

        const Sint32 height = h;
        bool bBitsWriten = false;

        Uint32 dstPos = 0;

        if (height > 0)
        {
            srcByte = 0;
            Sint32 heightCounter = height;
            do
            {
                byteCounter = 0;
                while (byteCounter < w)
                {
                    Uint8 nibble = ReadNibble(stream, bNibbleToRead, srcByte);
                    bNibbleToRead = !bNibbleToRead;

                    cnt = nibble;
                    bits_and_byte_count = nibble + byteCounter;
                    if (nibble > 0)
                    {
                        do
                        {
                            if (bBitsWriten)
                            {
                                dstPos++;
                            }
                            else
                            {
                                out[dstPos] = 0;
                            }
                            bBitsWriten = !bBitsWriten;
                            --cnt;
                        } while (cnt);
                    }

                    nibble = ReadNibble(stream, bNibbleToRead, srcByte);
                    bNibbleToRead = !bNibbleToRead;

                    byteCounter = nibble + bits_and_byte_count;
                    if (nibble > 0)
                    {
                        bitCount = nibble;
                        do
                        {
                            dstByte.b[0] = ReadNibble(stream, bNibbleToRead, srcByte);
                            bNibbleToRead = !bNibbleToRead;

                            if (bBitsWriten)
                            {
                                out[dstPos++] |= 16 * dstByte.b[0];
                                bBitsWriten = 0;
                            }
                            else
                            {
                                out[dstPos] = dstByte.b[0];
                                bBitsWriten = 1;
                            }
                        } while (--bitCount);
                    }
                }

                for (; byteCounter & 7; ++byteCounter)
                {
                    if (bBitsWriten)
                    {
                        dstPos++;
                    }
                    else
                    {
                        out[dstPos] = 0;
                    }
                    bBitsWriten = !bBitsWriten;
                }

            } while (heightCounter-- != 1);
        }
        return out;
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2010-2014 Joshua Boyce.
// See the file COPYING for copying permission.

#include "input.hpp"

#include <cstdint>

#include <windows.h>
#include <hadesmem/detail/warning_disable_prefix.hpp>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <hadesmem/detail/warning_disable_suffix.hpp>

#include <hadesmem/config.hpp>
#include <hadesmem/detail/detour_ref_counter.hpp>
#include <hadesmem/detail/last_error_preserver.hpp>
#include <hadesmem/patcher.hpp>

#include "callbacks.hpp"
#include "helpers.hpp"
#include "main.hpp"

namespace
{

struct WindowInfo
{
  HWND old_hwnd_{nullptr};
  WNDPROC old_wndproc_{nullptr};
  bool hooked_{false};
};

WindowInfo& GetWindowInfo() HADESMEM_DETAIL_NOEXCEPT
{
  static WindowInfo window_info;
  return window_info;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetDirectInput8CreateDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInput8ACreateDeviceDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInput8WCreateDeviceDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::pair<void*, SIZE_T>& GetDirectInput8Module() HADESMEM_DETAIL_NOEXCEPT
{
  static std::pair<void*, SIZE_T> module{nullptr, 0};
  return module;
}

hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnWndProcMsgCallback>&
  GetOnWndProcMsgCallbacks()
{
  static hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnWndProcMsgCallback>
    callbacks;
  return callbacks;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
  HADESMEM_DETAIL_NOEXCEPT
{
  auto const& callbacks = GetOnWndProcMsgCallbacks();
  bool handled = false;
  callbacks.Run(hwnd, msg, wparam, lparam, handled);

  if (handled)
  {
    return 0;
  }

  WindowInfo& window_info = GetWindowInfo();
  return window_info.old_wndproc_
           ? ::CallWindowProcW(
               window_info.old_wndproc_, hwnd, msg, wparam, lparam)
           : ::DefWindowProcW(hwnd, msg, wparam, lparam);
}

HRESULT WINAPI
  IDirectInput8ACreateDeviceDetour(IDirectInput8A* input,
                                   REFGUID rguid,
                                   LPDIRECTINPUTDEVICE* direct_input_device,
                                   LPUNKNOWN unk_outer)
{
  auto& detour = GetIDirectInput8ACreateDeviceDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A("Args: [%p] [%p] [%p] [%p].",
                                 input,
                                 &rguid,
                                 direct_input_device,
                                 unk_outer);

  auto const create_device =
    detour->GetTrampoline<decltype(&IDirectInput8ACreateDeviceDetour)>();
  last_error_preserver.Revert();
  auto const ret = create_device(input, rguid, direct_input_device, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  return ret;
}

HRESULT WINAPI
  IDirectInput8WCreateDeviceDetour(IDirectInput8W* input,
                                   REFGUID rguid,
                                   LPDIRECTINPUTDEVICE* direct_input_device,
                                   LPUNKNOWN unk_outer)
{
  auto& detour = GetIDirectInput8WCreateDeviceDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A("Args: [%p] [%p] [%p] [%p].",
                                 input,
                                 &rguid,
                                 direct_input_device,
                                 unk_outer);

  auto const create_device =
    detour->GetTrampoline<decltype(&IDirectInput8WCreateDeviceDetour)>();
  last_error_preserver.Revert();
  auto const ret = create_device(input, rguid, direct_input_device, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  return ret;
}

void DetourDirectInput8(REFIID riid, void** direct_input)
{
  HADESMEM_DETAIL_TRACE_A("Detouring IDirectInput8.");

  try
  {
    auto const& process = hadesmem::cerberus::GetThisProcess();

    if (riid == IID_IDirectInput8A)
    {
      hadesmem::cerberus::DetourFunc(process,
                                     L"IDirectInput8A::CreateDevice",
                                     *direct_input,
                                     3,
                                     GetIDirectInput8ACreateDeviceDetour(),
                                     IDirectInput8ACreateDeviceDetour);
    }
    else if (riid == IID_IDirectInput8W)
    {
      hadesmem::cerberus::DetourFunc(process,
                                     L"IDirectInput8W::CreateDevice",
                                     *direct_input,
                                     3,
                                     GetIDirectInput8WCreateDeviceDetour(),
                                     IDirectInput8WCreateDeviceDetour);
    }
    else
    {
      HADESMEM_DETAIL_TRACE_A("WARNING! Unknown IID.");
      HADESMEM_DETAIL_ASSERT(false);
    }
  }
  catch (...)
  {
    HADESMEM_DETAIL_TRACE_A(
      boost::current_exception_diagnostic_information().c_str());
    HADESMEM_DETAIL_ASSERT(false);
  }
}

HRESULT WINAPI
  DirectInput8CreateDetour(HINSTANCE hinst,
                           DWORD version,
                           REFIID riid,
                           LPVOID* out,
                           LPUNKNOWN unk_outer) HADESMEM_DETAIL_NOEXCEPT
{
  auto& detour = GetDirectInput8CreateDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A(
    "Args: [%p] [%u] [%p] [%p] [%p].", hinst, version, &riid, out, unk_outer);
  auto const direct_input_8_create =
    detour->GetTrampoline<decltype(&DirectInput8CreateDetour)>();
  last_error_preserver.Revert();
  auto const ret = direct_input_8_create(hinst, version, riid, out, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  if (FAILED(ret))
  {
    HADESMEM_DETAIL_TRACE_A("Failed.");
    return ret;
  }

  HADESMEM_DETAIL_TRACE_A("Succeeded.");

  DetourDirectInput8(riid, out);

  return ret;
}

class InputImpl : public hadesmem::cerberus::InputInterface
{
public:
  ~InputImpl()
  {
    try
    {
      hadesmem::cerberus::HandleWindowChange(nullptr);
    }
    catch (...)
    {
      HADESMEM_DETAIL_TRACE_A(
        boost::current_exception_diagnostic_information().c_str());
      HADESMEM_DETAIL_ASSERT(false);
    }
  }

  virtual std::size_t RegisterOnWndProcMsg(std::function<
    hadesmem::cerberus::OnWndProcMsgCallback> const& callback) final
  {
    return hadesmem::cerberus::RegisterOnWndProcMsgCallback(callback);
  }

  virtual void UnregisterOnWndProcMsg(std::size_t id) final
  {
    hadesmem::cerberus::UnregisterOnWndProcMsgCallback(id);
  }
};
}

namespace hadesmem
{

namespace cerberus
{

InputInterface& GetInputInterface() HADESMEM_DETAIL_NOEXCEPT
{
  static InputImpl input_impl;
  return input_impl;
}

void InitializeInput()
{
  InitializeSupportForModule(L"dinput8",
                             DetourDirectInput8,
                             UndetourDirectInput8,
                             GetDirectInput8Module);
}

void DetourDirectInput8(HMODULE base)
{
  auto const& process = GetThisProcess();
  auto& module = GetDirectInput8Module();
  if (CommonDetourModule(process, L"dinput8", base, module))
  {
    DetourFunc(process,
               base,
               "DirectInput8Create",
               GetDirectInput8CreateDetour(),
               DirectInput8CreateDetour);
  }
}

void UndetourDirectInput8(bool remove)
{
  auto& module = GetDirectInput8Module();
  if (CommonUndetourModule(L"dinput8", module))
  {
    UndetourFunc(L"DirectInput8Create", GetDirectInput8CreateDetour(), remove);

    module = std::make_pair(nullptr, 0);
  }
}

std::size_t RegisterOnWndProcMsgCallback(
  std::function<OnWndProcMsgCallback> const& callback)
{
  auto& callbacks = GetOnWndProcMsgCallbacks();
  return callbacks.Register(callback);
}

void UnregisterOnWndProcMsgCallback(std::size_t id)
{
  auto& callbacks = GetOnWndProcMsgCallbacks();
  return callbacks.Unregister(id);
}

void HandleWindowChange(HWND wnd)
{
  WindowInfo& window_info = GetWindowInfo();

  if (wnd == nullptr)
  {
    if (window_info.hooked_ && window_info.old_wndproc_ != nullptr)
    {
      ::SetWindowLongPtrW(window_info.old_hwnd_,
                          GWLP_WNDPROC,
                          reinterpret_cast<LONG_PTR>(window_info.old_wndproc_));
      HADESMEM_DETAIL_TRACE_FORMAT_A("Reset window procedure located at %p.",
                                     window_info.old_wndproc_);
    }

    HADESMEM_DETAIL_TRACE_A("Clearing window hook data.");

    window_info.old_hwnd_ = nullptr;
    window_info.old_wndproc_ = nullptr;
    window_info.hooked_ = false;

    return;
  }

  if (!window_info.hooked_)
  {
    window_info.old_hwnd_ = wnd;
    ::SetLastError(0);
    window_info.old_wndproc_ = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
      wnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&WindowProc)));
    if (!window_info.old_wndproc_)
    {
      DWORD const last_error = ::GetLastError();
      if (last_error)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(
          Error{} << ErrorString{"SetWindowLongPtrW failed."}
                  << ErrorCodeWinLast{last_error});
      }
    }
    window_info.hooked_ = true;
    HADESMEM_DETAIL_TRACE_FORMAT_A("Replaced window procedure located at %p.",
                                   window_info.old_wndproc_);
  }
  else
  {
    HADESMEM_DETAIL_TRACE_A("Window is already hooked. Skipping hook request.");
  }
}

bool IsWindowHooked() HADESMEM_DETAIL_NOEXCEPT
{
  return GetWindowInfo().hooked_;
}

HWND GetCurrentWindow() HADESMEM_DETAIL_NOEXCEPT
{
  return GetWindowInfo().old_hwnd_;
}
}
}
<commit_msg>* [Cerberus] Unhook direct input properly. * [Cerberus] Add hooks for GetDeviceData.<commit_after>// Copyright (C) 2010-2014 Joshua Boyce.
// See the file COPYING for copying permission.

#include "input.hpp"

#include <cstdint>

#include <windows.h>
#include <hadesmem/detail/warning_disable_prefix.hpp>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <hadesmem/detail/warning_disable_suffix.hpp>

#include <hadesmem/config.hpp>
#include <hadesmem/detail/detour_ref_counter.hpp>
#include <hadesmem/detail/last_error_preserver.hpp>
#include <hadesmem/patcher.hpp>

#include "callbacks.hpp"
#include "helpers.hpp"
#include "main.hpp"

namespace
{

struct WindowInfo
{
  HWND old_hwnd_{nullptr};
  WNDPROC old_wndproc_{nullptr};
  bool hooked_{false};
};

WindowInfo& GetWindowInfo() HADESMEM_DETAIL_NOEXCEPT
{
  static WindowInfo window_info;
  return window_info;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetDirectInput8CreateDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInput8ACreateDeviceDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInput8WCreateDeviceDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInputDevice8AGetDeviceDataDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::unique_ptr<hadesmem::PatchDetour>&
  GetIDirectInputDevice8WGetDeviceDataDetour() HADESMEM_DETAIL_NOEXCEPT
{
  static std::unique_ptr<hadesmem::PatchDetour> detour;
  return detour;
}

std::pair<void*, SIZE_T>& GetDirectInput8Module() HADESMEM_DETAIL_NOEXCEPT
{
  static std::pair<void*, SIZE_T> module{nullptr, 0};
  return module;
}

hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnWndProcMsgCallback>&
  GetOnWndProcMsgCallbacks()
{
  static hadesmem::cerberus::Callbacks<hadesmem::cerberus::OnWndProcMsgCallback>
    callbacks;
  return callbacks;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
  HADESMEM_DETAIL_NOEXCEPT
{
  auto const& callbacks = GetOnWndProcMsgCallbacks();
  bool handled = false;
  callbacks.Run(hwnd, msg, wparam, lparam, handled);

  if (handled)
  {
    return 0;
  }

  WindowInfo& window_info = GetWindowInfo();
  return window_info.old_wndproc_
           ? ::CallWindowProcW(
               window_info.old_wndproc_, hwnd, msg, wparam, lparam)
           : ::DefWindowProcW(hwnd, msg, wparam, lparam);
}

HRESULT WINAPI
  IDirectInputDevice8AGetDeviceDataDetour(IDirectInputDeviceA* device,
                                          DWORD cb_object_data,
                                          LPDIDEVICEOBJECTDATA dev_obj_data,
                                          LPDWORD in_out,
                                          DWORD flags)
{
  auto& detour = GetIDirectInputDevice8AGetDeviceDataDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  auto const get_device_data =
    detour->GetTrampoline<decltype(&IDirectInputDevice8AGetDeviceDataDetour)>();
  last_error_preserver.Revert();
  auto const ret =
    get_device_data(device, cb_object_data, dev_obj_data, in_out, flags);
  last_error_preserver.Update();

  return ret;
}

HRESULT WINAPI
  IDirectInputDevice8WGetDeviceDataDetour(IDirectInputDeviceW* device,
                                          DWORD cb_object_data,
                                          LPDIDEVICEOBJECTDATA dev_obj_data,
                                          LPDWORD in_out,
                                          DWORD flags)
{
  auto& detour = GetIDirectInputDevice8WGetDeviceDataDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  auto const get_device_data =
    detour->GetTrampoline<decltype(&IDirectInputDevice8WGetDeviceDataDetour)>();
  last_error_preserver.Revert();
  auto const ret =
    get_device_data(device, cb_object_data, dev_obj_data, in_out, flags);
  last_error_preserver.Update();

  return ret;
}

void DetourDirectInputDevice8(REFIID riid, void* device)
{
  HADESMEM_DETAIL_TRACE_A("Detouring IDirectInputDevice8.");

  try
  {
    auto const& process = hadesmem::cerberus::GetThisProcess();

    if (riid == IID_IDirectInputDevice8A)
    {
      hadesmem::cerberus::DetourFunc(
        process,
        L"IDirectInputDevice8A::GetDeviceData",
        device,
        10,
        GetIDirectInputDevice8AGetDeviceDataDetour(),
        IDirectInputDevice8AGetDeviceDataDetour);
    }
    else if (riid == IID_IDirectInputDevice8W)
    {
      hadesmem::cerberus::DetourFunc(
        process,
        L"IDirectInputDevice8W::GetDeviceData",
        device,
        10,
        GetIDirectInputDevice8WGetDeviceDataDetour(),
        IDirectInputDevice8WGetDeviceDataDetour);
    }
    else
    {
      HADESMEM_DETAIL_TRACE_A("WARNING! Unknown IID.");
      HADESMEM_DETAIL_ASSERT(false);
    }
  }
  catch (...)
  {
    HADESMEM_DETAIL_TRACE_A(
      boost::current_exception_diagnostic_information().c_str());
    HADESMEM_DETAIL_ASSERT(false);
  }
}

HRESULT WINAPI
  IDirectInput8ACreateDeviceDetour(IDirectInput8A* input,
                                   REFGUID rguid,
                                   IDirectInputDeviceA** direct_input_device,
                                   LPUNKNOWN unk_outer)
{
  auto& detour = GetIDirectInput8ACreateDeviceDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A("Args: [%p] [%p] [%p] [%p].",
                                 input,
                                 &rguid,
                                 direct_input_device,
                                 unk_outer);

  auto const create_device =
    detour->GetTrampoline<decltype(&IDirectInput8ACreateDeviceDetour)>();
  last_error_preserver.Revert();
  auto const ret = create_device(input, rguid, direct_input_device, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  if (FAILED(ret))
  {
    HADESMEM_DETAIL_TRACE_A("Failed.");
    return ret;
  }

  HADESMEM_DETAIL_TRACE_A("Succeeded.");

  DetourDirectInputDevice8(IID_IDirectInputDevice8A, *direct_input_device);

  return ret;
}

HRESULT WINAPI
  IDirectInput8WCreateDeviceDetour(IDirectInput8W* input,
                                   REFGUID rguid,
                                   IDirectInputDeviceW** direct_input_device,
                                   LPUNKNOWN unk_outer)
{
  auto& detour = GetIDirectInput8WCreateDeviceDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A("Args: [%p] [%p] [%p] [%p].",
                                 input,
                                 &rguid,
                                 direct_input_device,
                                 unk_outer);

  auto const create_device =
    detour->GetTrampoline<decltype(&IDirectInput8WCreateDeviceDetour)>();
  last_error_preserver.Revert();
  auto const ret = create_device(input, rguid, direct_input_device, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  if (FAILED(ret))
  {
    HADESMEM_DETAIL_TRACE_A("Failed.");
    return ret;
  }

  HADESMEM_DETAIL_TRACE_A("Succeeded.");

  DetourDirectInputDevice8(IID_IDirectInputDevice8W, *direct_input_device);

  return ret;
}

void DetourDirectInput8(REFIID riid, void** direct_input)
{
  HADESMEM_DETAIL_TRACE_A("Detouring IDirectInput8.");

  try
  {
    auto const& process = hadesmem::cerberus::GetThisProcess();

    if (riid == IID_IDirectInput8A)
    {
      hadesmem::cerberus::DetourFunc(process,
                                     L"IDirectInput8A::CreateDevice",
                                     *direct_input,
                                     3,
                                     GetIDirectInput8ACreateDeviceDetour(),
                                     IDirectInput8ACreateDeviceDetour);
    }
    else if (riid == IID_IDirectInput8W)
    {
      hadesmem::cerberus::DetourFunc(process,
                                     L"IDirectInput8W::CreateDevice",
                                     *direct_input,
                                     3,
                                     GetIDirectInput8WCreateDeviceDetour(),
                                     IDirectInput8WCreateDeviceDetour);
    }
    else
    {
      HADESMEM_DETAIL_TRACE_A("WARNING! Unknown IID.");
      HADESMEM_DETAIL_ASSERT(false);
    }
  }
  catch (...)
  {
    HADESMEM_DETAIL_TRACE_A(
      boost::current_exception_diagnostic_information().c_str());
    HADESMEM_DETAIL_ASSERT(false);
  }
}

HRESULT WINAPI
  DirectInput8CreateDetour(HINSTANCE hinst,
                           DWORD version,
                           REFIID riid,
                           LPVOID* out,
                           LPUNKNOWN unk_outer) HADESMEM_DETAIL_NOEXCEPT
{
  auto& detour = GetDirectInput8CreateDetour();
  hadesmem::detail::DetourRefCounter ref_count{detour->GetRefCount()};
  hadesmem::detail::LastErrorPreserver last_error_preserver;

  HADESMEM_DETAIL_TRACE_FORMAT_A(
    "Args: [%p] [%u] [%p] [%p] [%p].", hinst, version, &riid, out, unk_outer);
  auto const direct_input_8_create =
    detour->GetTrampoline<decltype(&DirectInput8CreateDetour)>();
  last_error_preserver.Revert();
  auto const ret = direct_input_8_create(hinst, version, riid, out, unk_outer);
  last_error_preserver.Update();
  HADESMEM_DETAIL_TRACE_FORMAT_A("Ret: [%ld].", ret);

  if (FAILED(ret))
  {
    HADESMEM_DETAIL_TRACE_A("Failed.");
    return ret;
  }

  HADESMEM_DETAIL_TRACE_A("Succeeded.");

  DetourDirectInput8(riid, out);

  return ret;
}

class InputImpl : public hadesmem::cerberus::InputInterface
{
public:
  ~InputImpl()
  {
    try
    {
      hadesmem::cerberus::HandleWindowChange(nullptr);
    }
    catch (...)
    {
      HADESMEM_DETAIL_TRACE_A(
        boost::current_exception_diagnostic_information().c_str());
      HADESMEM_DETAIL_ASSERT(false);
    }
  }

  virtual std::size_t RegisterOnWndProcMsg(
    std::function<hadesmem::cerberus::OnWndProcMsgCallback> const& callback)
    final
  {
    return hadesmem::cerberus::RegisterOnWndProcMsgCallback(callback);
  }

  virtual void UnregisterOnWndProcMsg(std::size_t id) final
  {
    hadesmem::cerberus::UnregisterOnWndProcMsgCallback(id);
  }
};
}

namespace hadesmem
{

namespace cerberus
{

InputInterface& GetInputInterface() HADESMEM_DETAIL_NOEXCEPT
{
  static InputImpl input_impl;
  return input_impl;
}

void InitializeInput()
{
  InitializeSupportForModule(L"dinput8",
                             DetourDirectInput8,
                             UndetourDirectInput8,
                             GetDirectInput8Module);
}

void DetourDirectInput8(HMODULE base)
{
  auto const& process = GetThisProcess();
  auto& module = GetDirectInput8Module();
  if (CommonDetourModule(process, L"dinput8", base, module))
  {
    DetourFunc(process,
               base,
               "DirectInput8Create",
               GetDirectInput8CreateDetour(),
               DirectInput8CreateDetour);
  }
}

void UndetourDirectInput8(bool remove)
{
  auto& module = GetDirectInput8Module();
  if (CommonUndetourModule(L"dinput8", module))
  {
    UndetourFunc(L"DirectInput8Create", GetDirectInput8CreateDetour(), remove);
    UndetourFunc(L"IDirectInput8A::CreateDevice",
                 GetIDirectInput8ACreateDeviceDetour(),
                 remove);
    UndetourFunc(L"IDirectInput8W::CreateDevice",
                 GetIDirectInput8WCreateDeviceDetour(),
                 remove);
    UndetourFunc(L"IDirectInputDevice8A::GetDeviceData",
                 GetIDirectInputDevice8AGetDeviceDataDetour(),
                 remove);
    UndetourFunc(L"IDirectInputDevice8W::GetDeviceData",
                 GetIDirectInputDevice8WGetDeviceDataDetour(),
                 remove);

    module = std::make_pair(nullptr, 0);
  }
}

std::size_t RegisterOnWndProcMsgCallback(
  std::function<OnWndProcMsgCallback> const& callback)
{
  auto& callbacks = GetOnWndProcMsgCallbacks();
  return callbacks.Register(callback);
}

void UnregisterOnWndProcMsgCallback(std::size_t id)
{
  auto& callbacks = GetOnWndProcMsgCallbacks();
  return callbacks.Unregister(id);
}

void HandleWindowChange(HWND wnd)
{
  WindowInfo& window_info = GetWindowInfo();

  if (wnd == nullptr)
  {
    if (window_info.hooked_ && window_info.old_wndproc_ != nullptr)
    {
      ::SetWindowLongPtrW(window_info.old_hwnd_,
                          GWLP_WNDPROC,
                          reinterpret_cast<LONG_PTR>(window_info.old_wndproc_));
      HADESMEM_DETAIL_TRACE_FORMAT_A("Reset window procedure located at %p.",
                                     window_info.old_wndproc_);
    }

    HADESMEM_DETAIL_TRACE_A("Clearing window hook data.");

    window_info.old_hwnd_ = nullptr;
    window_info.old_wndproc_ = nullptr;
    window_info.hooked_ = false;

    return;
  }

  if (!window_info.hooked_)
  {
    window_info.old_hwnd_ = wnd;
    ::SetLastError(0);
    window_info.old_wndproc_ = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
      wnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&WindowProc)));
    if (!window_info.old_wndproc_)
    {
      DWORD const last_error = ::GetLastError();
      if (last_error)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(
          Error{} << ErrorString{"SetWindowLongPtrW failed."}
                  << ErrorCodeWinLast{last_error});
      }
    }
    window_info.hooked_ = true;
    HADESMEM_DETAIL_TRACE_FORMAT_A("Replaced window procedure located at %p.",
                                   window_info.old_wndproc_);
  }
  else
  {
    HADESMEM_DETAIL_TRACE_A("Window is already hooked. Skipping hook request.");
  }
}

bool IsWindowHooked() HADESMEM_DETAIL_NOEXCEPT
{
  return GetWindowInfo().hooked_;
}

HWND GetCurrentWindow() HADESMEM_DETAIL_NOEXCEPT
{
  return GetWindowInfo().old_hwnd_;
}
}
}
<|endoftext|>


<commit_before>#include <common/list.h>
#include <common/map.h>
#include <common/log.h>
#include <common/string.h>
#include <gui/gui.h>
#include <gdb/gdb.h>
#include <gdb/result.h>
#include <gdb/memory.h>
#include <gdb/variable.h>
#include <user_cmd/cmd.h>
#include <user_cmd/subcmd.hash.h>
#include <map>

using namespace std;

/* macros */
#define CTOI(c) (unsigned int)((c) - ((c) >= 'a' ? 87 : 48))
#define ALIGN(val, base) ((val) & (~(base - 1)))

/* static variables */
static gdb_memory_t* mem_lst = 0;
static map<unsigned int, gdb_memory_t*> line_map;

/* global functions */
int cmd_memory_exec(int argc, char** argv){
	const struct user_subcmd_t* scmd;
	FILE* fp;
	gdb_memory_t* mem;
	map<unsigned int, gdb_memory_t*>::iterator it;

	if(argc < 2){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_memory_help(1, argv);
		return 0;
	}

	mem = 0;
	scmd = user_subcmd::lookup(argv[1], strlen(argv[1]));

	if(scmd == 0){
		USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[1], argv[0]);
		return 0;
	}

	if(((scmd->id == DELETE || scmd->id == GET) && argc < 3) || ((scmd->id == ADD || scmd->id == SET) && argc < 4)){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_memory_help(2, argv);
		return 0;
	}

	switch(scmd->id){
	case ADD:
		if(gdb->mi_issue_cmd((char*)"data-read-memory-bytes", RC_DONE, gdb_memory_t::result_to_memory, (void**)&mem, "%ss %d", argv + 2, argc - 2) != 0)
			return -1;

		list_add_tail(&mem_lst, mem);

		USER("add memory dump at \"%s\" of size %u\n", mem->begin, mem->length);
		cmd_memory_update();
		break;

	case DELETE:
		mem = MAP_LOOKUP(line_map, atoi(argv[2]));

		if(mem == 0){
			USER("no memory memment at line %s\n", argv[2]);
			return 0;
		}

		list_rm(&mem_lst, mem);
		delete mem;

		USER("delete memory memment\n");

		cmd_memory_update();
		break;

	case SET:
		if(gdb->mi_issue_cmd((char*)"data-write-memory-bytes", RC_DONE, 0, 0, "%ss %d", argv + 2, argc - 2) != 0)
			return -1;

		gdb_variable_t::get_changed();

		cmd_memory_update();
		cmd_var_print();
		cmd_callstack_print();
		break;

	case GET:
		fp = fopen(argv[2], "w");

		if(fp == 0)
			return -1;

		for(it=line_map.begin(); it!=line_map.end(); it++)
			fprintf(fp, "%d\\n", it->first);

		fclose(fp);
		break;

	case VIEW:
		cmd_memory_update();
		break;

	default:
		USER("unhandled sub command \"%s\" to \"%s\"\n", argv[1], argv[0]);
	};

	return 0;

}

void cmd_memory_help(int argc, char** argv){
}

int cmd_memory_update(){
	static unsigned int len = 17;	// 8 byte + 0-byte + 8 byte for potential escape chars
	static char* ascii = new char[len];
	int win_id;
	char c;
	char* content_old;
	unsigned int i, j;
	long long addr, displ;
	gdb_memory_t* mem;

	win_id = ui->win_getid("memory");

	if(win_id < 0)
		return 0;

	line_map.clear();

	ui->atomic(true);
	ui->win_clear(win_id);

	list_for_each(mem_lst, mem){
		addr = strtoll(mem->begin, 0, 16);

		// TODO handle update highlighting
		/* safe old content value */
		content_old = mem->content;
		mem->content = 0;

		/* get memory content */
		if(gdb->mi_issue_cmd((char*)"data-read-memory-bytes", RC_DONE, gdb_memory_t::result_to_memory, (void**)&mem, "%s %u", mem->begin, mem->length) != 0)
			break;

		/* print header */
		ui->win_print(win_id, "memory dump: %#0*p (%u bytes)\n", sizeof(void*) * 2 + 2, addr, mem->length);

		/* print preceding bytes, that are not part of content, to align the output to 8 bytes per line */
		j = 0;
		displ = ALIGN(addr, 8);

		ui->win_print(win_id, " %#0*p    ", sizeof(void*) * 2 + 2, displ);

		for(; displ<addr; displ++){
			ui->win_print(win_id, " ??");
			ascii[j++] = ' ';
		}

		/* print actual memory content */
		for(i=0; i<mem->length; i++, addr++){
			ui->win_print(win_id, "%c%2.2s", (memcmp(content_old + i * 2, mem->content + i * 2, 2) == 0 ? ' ' : '`'), mem->content + i * 2);

			// update ascii string
			c = (char)(CTOI(mem->content[i * 2]) * 16 + CTOI(mem->content[i * 2 + 1]));
			ascii[j++] = c == '\0' ? ' ' : c;

			// print ascii string once reaching 8 bytes boundary
			if(addr + 1 == ALIGN(addr + 8, 8)){
				ascii[j] = 0;
				j = 0;
				ui->win_print(win_id, "    %s\n", strescape(ascii, &ascii, &len));

				if(i + 1 < mem->length)
					ui->win_print(win_id, " %#0*p    ", sizeof(void*) * 2 + 2, 10 + 1);
			}
		}

		/* print trailing bytes, that are not part of content, to fill line */
		if(ALIGN(addr, 8) != addr){
			for(displ=ALIGN(addr + 8, 8); addr<displ; addr++){
				ui->win_print(win_id, " ??");
				ascii[j++] = ' ';
			}
		}

		ascii[j] = 0;
		ui->win_print(win_id, "    %s\n", strescape(ascii, &ascii, &len));

		if(j != 0)
			ui->win_print(win_id, "\n");
	}

	ui->atomic(false);

	return 0;
}
<commit_msg>[user-command: memory]<commit_after>#include <common/list.h>
#include <common/map.h>
#include <common/log.h>
#include <common/string.h>
#include <gui/gui.h>
#include <gdb/gdb.h>
#include <gdb/result.h>
#include <gdb/memory.h>
#include <gdb/variable.h>
#include <user_cmd/cmd.h>
#include <user_cmd/subcmd.hash.h>
#include <map>

using namespace std;

/* macros */
#define CTOI(c) (unsigned int)((c) - ((c) >= 'a' ? 87 : 48))
#define ALIGN(val, base) ((val) & (~(base - 1)))

/* static variables */
static gdb_memory_t* mem_lst = 0;
static map<unsigned int, gdb_memory_t*> line_map;

/* global functions */
int cmd_memory_exec(int argc, char** argv){
	const struct user_subcmd_t* scmd;
	FILE* fp;
	gdb_memory_t* mem;
	map<unsigned int, gdb_memory_t*>::iterator it;

	if(argc < 2){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_memory_help(1, argv);
		return 0;
	}

	mem = 0;
	scmd = user_subcmd::lookup(argv[1], strlen(argv[1]));

	if(scmd == 0){
		USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[1], argv[0]);
		return 0;
	}

	if(((scmd->id == DELETE || scmd->id == GET) && argc < 3) || ((scmd->id == ADD || scmd->id == SET) && argc < 4)){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_memory_help(2, argv);
		return 0;
	}

	switch(scmd->id){
	case ADD:
		if(gdb->mi_issue_cmd((char*)"data-read-memory-bytes", RC_DONE, gdb_memory_t::result_to_memory, (void**)&mem, "%ss %d", argv + 2, argc - 2) != 0)
			return -1;

		list_add_tail(&mem_lst, mem);

		USER("add memory dump at \"%s\" of size %u\n", mem->begin, mem->length);
		cmd_memory_update();
		break;

	case DELETE:
		mem = MAP_LOOKUP(line_map, atoi(argv[2]));

		if(mem == 0){
			USER("no memory segment at line %s\n", argv[2]);
			return 0;
		}

		list_rm(&mem_lst, mem);
		delete mem;

		USER("delete memory segment\n");

		cmd_memory_update();
		break;

	case SET:
		if(gdb->mi_issue_cmd((char*)"data-write-memory-bytes", RC_DONE, 0, 0, "%ss %d", argv + 2, argc - 2) != 0)
			return -1;

		gdb_variable_t::get_changed();

		cmd_memory_update();
		cmd_var_print();
		cmd_callstack_print();
		break;

	case GET:
		fp = fopen(argv[2], "w");

		if(fp == 0)
			return -1;

		for(it=line_map.begin(); it!=line_map.end(); it++)
			fprintf(fp, "%d\\n", it->first);

		fclose(fp);
		break;

	case VIEW:
		cmd_memory_update();
		break;

	default:
		USER("unhandled sub command \"%s\" to \"%s\"\n", argv[1], argv[0]);
	};

	return 0;

}

void cmd_memory_help(int argc, char** argv){
}

int cmd_memory_update(){
	static unsigned int len = 17;	// 8 byte + 0-byte + 8 byte for potential escape chars
	static char* ascii = new char[len];
	int win_id;
	char c;
	char* content_old;
	unsigned int i, j, line;
	long long addr, displ;
	gdb_memory_t* mem;

	win_id = ui->win_getid("memory");

	if(win_id < 0)
		return 0;

	line_map.clear();
	line = 1;

	ui->atomic(true);
	ui->win_clear(win_id);

	list_for_each(mem_lst, mem){
		addr = strtoll(mem->begin, 0, 16);

		// TODO handle update highlighting
		/* safe old content value */
		content_old = mem->content;
		mem->content = 0;

		/* get memory content */
		if(gdb->mi_issue_cmd((char*)"data-read-memory-bytes", RC_DONE, gdb_memory_t::result_to_memory, (void**)&mem, "%s %u", mem->begin, mem->length) != 0)
			break;

		/* print header */
		ui->win_print(win_id, "memory dump: %#0*p (%u bytes)\n", sizeof(void*) * 2 + 2, addr, mem->length);
		line_map[line++] = mem;

		/* print preceding bytes, that are not part of content, to align the output to 8 bytes per line */
		j = 0;
		displ = ALIGN(addr, 8);

		ui->win_print(win_id, " %#0*p    ", sizeof(void*) * 2 + 2, displ);

		for(; displ<addr; displ++){
			ui->win_print(win_id, " ??");
			ascii[j++] = ' ';
		}

		/* print actual memory content */
		for(i=0; i<mem->length; i++, addr++){
			ui->win_print(win_id, "%c%2.2s", (memcmp(content_old + i * 2, mem->content + i * 2, 2) == 0 ? ' ' : '`'), mem->content + i * 2);

			// update ascii string
			c = (char)(CTOI(mem->content[i * 2]) * 16 + CTOI(mem->content[i * 2 + 1]));
			ascii[j++] = c == '\0' ? ' ' : c;

			// print ascii string once reaching 8 bytes boundary
			if(addr + 1 == ALIGN(addr + 8, 8)){
				ascii[j] = 0;
				j = 0;
				ui->win_print(win_id, "    %s\n", strescape(ascii, &ascii, &len));
				line_map[line++] = mem;

				if(i + 1 < mem->length)
					ui->win_print(win_id, " %#0*p    ", sizeof(void*) * 2 + 2, 10 + 1);
			}
		}

		/* print trailing bytes, that are not part of content, to fill line */
		if(ALIGN(addr, 8) != addr){
			for(displ=ALIGN(addr + 8, 8); addr<displ; addr++){
				ui->win_print(win_id, " ??");
				ascii[j++] = ' ';
			}
		}

		ascii[j] = 0;
		ui->win_print(win_id, "    %s\n", strescape(ascii, &ascii, &len));
		line_map[line++] = mem;

		if(j != 0){
			ui->win_print(win_id, "\n");
			line++;
		}
	}

	ui->atomic(false);

	return 0;
}
<|endoftext|>


<commit_before>//===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the SourceMgr class.  This class is used as a simple
// substrate for diagnostics, #include handling, and other low level things for
// simple parsers.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
  struct LineNoCacheTy {
    int LastQueryBufferID;
    const char *LastQuery;
    unsigned LineNoOfQuery;
  };
}

static LineNoCacheTy *getCache(void *Ptr) {
  return (LineNoCacheTy*)Ptr;
}

SourceMgr::~SourceMgr() {
  // Delete the line # cache if allocated.
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
    delete Cache;

  while (!Buffers.empty()) {
    delete Buffers.back().Buffer;
    Buffers.pop_back();
  }
}

/// AddIncludeFile - Search for a file with the specified name in the current
/// directory or in one of the IncludeDirs.  If no file is found, this returns
/// ~0, otherwise it returns the buffer ID of the stacked file.
unsigned SourceMgr::AddIncludeFile(const std::string &Filename,
                                   SMLoc IncludeLoc) {

  MemoryBuffer *NewBuf = MemoryBuffer::getFile(Filename.c_str());

  // If the file didn't exist directly, see if it's in an include path.
  for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBuf; ++i) {
    std::string IncFile = IncludeDirectories[i] + "/" + Filename;
    NewBuf = MemoryBuffer::getFile(IncFile.c_str());
  }

  if (NewBuf == 0) return ~0U;

  return AddNewSourceBuffer(NewBuf, IncludeLoc);
}

/// FindBufferContainingLoc - Return the ID of the buffer containing the
/// specified location, returning -1 if not found.
int SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
  for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
    if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
        // Use <= here so that a pointer to the null at the end of the buffer
        // is included as part of the buffer.
        Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
      return i;
  return -1;
}

/// FindLineNumber - Find the line number for the specified location in the
/// specified file.  This is not a fast method.
unsigned SourceMgr::FindLineNumber(SMLoc Loc, int BufferID) const {
  if (BufferID == -1) BufferID = FindBufferContainingLoc(Loc);
  assert(BufferID != -1 && "Invalid Location!");

  MemoryBuffer *Buff = getBufferInfo(BufferID).Buffer;

  // Count the number of \n's between the start of the file and the specified
  // location.
  unsigned LineNo = 1;

  const char *Ptr = Buff->getBufferStart();

  // If we have a line number cache, and if the query is to a later point in the
  // same file, start searching from the last query location.  This optimizes
  // for the case when multiple diagnostics come out of one file in order.
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
    if (Cache->LastQueryBufferID == BufferID &&
        Cache->LastQuery <= Loc.getPointer()) {
      Ptr = Cache->LastQuery;
      LineNo = Cache->LineNoOfQuery;
    }

  // Scan for the location being queried, keeping track of the number of lines
  // we see.
  for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr)
    if (*Ptr == '\n') ++LineNo;

  // Allocate the line number cache if it doesn't exist.
  if (LineNoCache == 0)
    LineNoCache = new LineNoCacheTy();

  // Update the line # cache.
  LineNoCacheTy &Cache = *getCache(LineNoCache);
  Cache.LastQueryBufferID = BufferID;
  Cache.LastQuery = Ptr;
  Cache.LineNoOfQuery = LineNo;
  return LineNo;
}

void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
  if (IncludeLoc == SMLoc()) return;  // Top of stack.

  int CurBuf = FindBufferContainingLoc(IncludeLoc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");

  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);

  OS << "Included from "
     << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
     << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
}

/// GetMessage - Return an SMDiagnostic at the specified location with the
/// specified string.
///
/// @param Type - If non-null, the kind of message (e.g., "error") which is
/// prefixed to the message.
SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, const std::string &Msg,
                                   const char *Type, bool ShowLine) const {

  // First thing to do: find the current buffer containing the specified
  // location.
  int CurBuf = FindBufferContainingLoc(Loc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");

  MemoryBuffer *CurMB = getBufferInfo(CurBuf).Buffer;

  // Scan backward to find the start of the line.
  const char *LineStart = Loc.getPointer();
  while (LineStart != CurMB->getBufferStart() &&
         LineStart[-1] != '\n' && LineStart[-1] != '\r')
    --LineStart;

  std::string LineStr;
  if (ShowLine) {
    // Get the end of the line.
    const char *LineEnd = Loc.getPointer();
    while (LineEnd != CurMB->getBufferEnd() &&
           LineEnd[0] != '\n' && LineEnd[0] != '\r')
      ++LineEnd;
    LineStr = std::string(LineStart, LineEnd);
  }

  std::string PrintedMsg;
  if (Type) {
    PrintedMsg = Type;
    PrintedMsg += ": ";
  }
  PrintedMsg += Msg;

  return SMDiagnostic(CurMB->getBufferIdentifier(), FindLineNumber(Loc, CurBuf),
                      Loc.getPointer()-LineStart, PrintedMsg,
                      LineStr, ShowLine);
}

void SourceMgr::PrintMessage(SMLoc Loc, const std::string &Msg,
                             const char *Type, bool ShowLine) const {
  raw_ostream &OS = errs();

  int CurBuf = FindBufferContainingLoc(Loc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");
  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);

  GetMessage(Loc, Msg, Type, ShowLine).Print(0, OS);
}

//===----------------------------------------------------------------------===//
// SMDiagnostic Implementation
//===----------------------------------------------------------------------===//

void SMDiagnostic::Print(const char *ProgName, raw_ostream &S) {
  if (ProgName && ProgName[0])
    S << ProgName << ": ";

  if (Filename == "-")
    S << "<stdin>";
  else
    S << Filename;

  if (LineNo != -1) {
    S << ':' << LineNo;
    if (ColumnNo != -1)
      S << ':' << (ColumnNo+1);
  }

  S << ": " << Message << '\n';

  if (LineNo != -1 && ColumnNo != -1 && ShowLine) {
    S << LineContents << '\n';

    // Print out spaces/tabs before the caret.
    for (unsigned i = 0; i != unsigned(ColumnNo); ++i)
      S << (LineContents[i] == '\t' ? '\t' : ' ');
    S << "^\n";
  }
}

<commit_msg>Avoid printing a spurious semicolon when there is no filename.<commit_after>//===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the SourceMgr class.  This class is used as a simple
// substrate for diagnostics, #include handling, and other low level things for
// simple parsers.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
  struct LineNoCacheTy {
    int LastQueryBufferID;
    const char *LastQuery;
    unsigned LineNoOfQuery;
  };
}

static LineNoCacheTy *getCache(void *Ptr) {
  return (LineNoCacheTy*)Ptr;
}

SourceMgr::~SourceMgr() {
  // Delete the line # cache if allocated.
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
    delete Cache;

  while (!Buffers.empty()) {
    delete Buffers.back().Buffer;
    Buffers.pop_back();
  }
}

/// AddIncludeFile - Search for a file with the specified name in the current
/// directory or in one of the IncludeDirs.  If no file is found, this returns
/// ~0, otherwise it returns the buffer ID of the stacked file.
unsigned SourceMgr::AddIncludeFile(const std::string &Filename,
                                   SMLoc IncludeLoc) {

  MemoryBuffer *NewBuf = MemoryBuffer::getFile(Filename.c_str());

  // If the file didn't exist directly, see if it's in an include path.
  for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBuf; ++i) {
    std::string IncFile = IncludeDirectories[i] + "/" + Filename;
    NewBuf = MemoryBuffer::getFile(IncFile.c_str());
  }

  if (NewBuf == 0) return ~0U;

  return AddNewSourceBuffer(NewBuf, IncludeLoc);
}

/// FindBufferContainingLoc - Return the ID of the buffer containing the
/// specified location, returning -1 if not found.
int SourceMgr::FindBufferContainingLoc(SMLoc Loc) const {
  for (unsigned i = 0, e = Buffers.size(); i != e; ++i)
    if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() &&
        // Use <= here so that a pointer to the null at the end of the buffer
        // is included as part of the buffer.
        Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd())
      return i;
  return -1;
}

/// FindLineNumber - Find the line number for the specified location in the
/// specified file.  This is not a fast method.
unsigned SourceMgr::FindLineNumber(SMLoc Loc, int BufferID) const {
  if (BufferID == -1) BufferID = FindBufferContainingLoc(Loc);
  assert(BufferID != -1 && "Invalid Location!");

  MemoryBuffer *Buff = getBufferInfo(BufferID).Buffer;

  // Count the number of \n's between the start of the file and the specified
  // location.
  unsigned LineNo = 1;

  const char *Ptr = Buff->getBufferStart();

  // If we have a line number cache, and if the query is to a later point in the
  // same file, start searching from the last query location.  This optimizes
  // for the case when multiple diagnostics come out of one file in order.
  if (LineNoCacheTy *Cache = getCache(LineNoCache))
    if (Cache->LastQueryBufferID == BufferID &&
        Cache->LastQuery <= Loc.getPointer()) {
      Ptr = Cache->LastQuery;
      LineNo = Cache->LineNoOfQuery;
    }

  // Scan for the location being queried, keeping track of the number of lines
  // we see.
  for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr)
    if (*Ptr == '\n') ++LineNo;

  // Allocate the line number cache if it doesn't exist.
  if (LineNoCache == 0)
    LineNoCache = new LineNoCacheTy();

  // Update the line # cache.
  LineNoCacheTy &Cache = *getCache(LineNoCache);
  Cache.LastQueryBufferID = BufferID;
  Cache.LastQuery = Ptr;
  Cache.LineNoOfQuery = LineNo;
  return LineNo;
}

void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const {
  if (IncludeLoc == SMLoc()) return;  // Top of stack.

  int CurBuf = FindBufferContainingLoc(IncludeLoc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");

  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);

  OS << "Included from "
     << getBufferInfo(CurBuf).Buffer->getBufferIdentifier()
     << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n";
}

/// GetMessage - Return an SMDiagnostic at the specified location with the
/// specified string.
///
/// @param Type - If non-null, the kind of message (e.g., "error") which is
/// prefixed to the message.
SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, const std::string &Msg,
                                   const char *Type, bool ShowLine) const {

  // First thing to do: find the current buffer containing the specified
  // location.
  int CurBuf = FindBufferContainingLoc(Loc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");

  MemoryBuffer *CurMB = getBufferInfo(CurBuf).Buffer;

  // Scan backward to find the start of the line.
  const char *LineStart = Loc.getPointer();
  while (LineStart != CurMB->getBufferStart() &&
         LineStart[-1] != '\n' && LineStart[-1] != '\r')
    --LineStart;

  std::string LineStr;
  if (ShowLine) {
    // Get the end of the line.
    const char *LineEnd = Loc.getPointer();
    while (LineEnd != CurMB->getBufferEnd() &&
           LineEnd[0] != '\n' && LineEnd[0] != '\r')
      ++LineEnd;
    LineStr = std::string(LineStart, LineEnd);
  }

  std::string PrintedMsg;
  if (Type) {
    PrintedMsg = Type;
    PrintedMsg += ": ";
  }
  PrintedMsg += Msg;

  return SMDiagnostic(CurMB->getBufferIdentifier(), FindLineNumber(Loc, CurBuf),
                      Loc.getPointer()-LineStart, PrintedMsg,
                      LineStr, ShowLine);
}

void SourceMgr::PrintMessage(SMLoc Loc, const std::string &Msg,
                             const char *Type, bool ShowLine) const {
  raw_ostream &OS = errs();

  int CurBuf = FindBufferContainingLoc(Loc);
  assert(CurBuf != -1 && "Invalid or unspecified location!");
  PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS);

  GetMessage(Loc, Msg, Type, ShowLine).Print(0, OS);
}

//===----------------------------------------------------------------------===//
// SMDiagnostic Implementation
//===----------------------------------------------------------------------===//

void SMDiagnostic::Print(const char *ProgName, raw_ostream &S) {
  if (ProgName && ProgName[0])
    S << ProgName << ": ";

  if (!Filename.empty()) {
    if (Filename == "-")
      S << "<stdin>";
    else
      S << Filename;

    if (LineNo != -1) {
      S << ':' << LineNo;
      if (ColumnNo != -1)
        S << ':' << (ColumnNo+1);
    }
    S << ": ";
  }

  S << Message << '\n';

  if (LineNo != -1 && ColumnNo != -1 && ShowLine) {
    S << LineContents << '\n';

    // Print out spaces/tabs before the caret.
    for (unsigned i = 0; i != unsigned(ColumnNo); ++i)
      S << (LineContents[i] == '\t' ? '\t' : ' ');
    S << "^\n";
  }
}

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * Modified by ScyllaDB
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "streaming/stream_result_future.hh"
#include "streaming/stream_manager.hh"
#include "streaming/stream_exception.hh"
#include "log.hh"
#include <cfloat>

namespace streaming {

extern logging::logger sslog;

future<stream_state> stream_result_future::init_sending_side(UUID plan_id_, sstring description_,
        std::vector<stream_event_handler*> listeners_, shared_ptr<stream_coordinator> coordinator_) {
    auto sr = make_shared<stream_result_future>(plan_id_, description_, coordinator_);
    get_local_stream_manager().register_sending(sr);

    for (auto& listener : listeners_) {
        sr->add_event_listener(listener);
    }

    sslog.info("[Stream #{}] Executing streaming plan for {} with peers={}, master", plan_id_,  description_, coordinator_->get_peers());

    // Initialize and start all sessions
    for (auto& session : coordinator_->get_all_stream_sessions()) {
        session->init(sr);
    }
    coordinator_->connect_all_stream_sessions();

    return sr->_done.get_future();
}

shared_ptr<stream_result_future> stream_result_future::init_receiving_side(UUID plan_id, sstring description, inet_address from) {
    auto& sm = get_local_stream_manager();
    auto sr = sm.get_receiving_stream(plan_id);
    if (sr) {
        auto err = sprint("[Stream #%s] GOT PREPARE_MESSAGE from %s, description=%s,"
                          "stream_plan exists, duplicated message received?", plan_id, description, from);
        sslog.warn(err.c_str());
        throw std::runtime_error(err);
    }
    sslog.info("[Stream #{}] Executing streaming plan for {} with peers={}, slave", plan_id, description, from);
    bool is_receiving = true;
    sr = make_shared<stream_result_future>(plan_id, description, is_receiving);
    sm.register_receiving(sr);
    return sr;
}

void stream_result_future::handle_session_prepared(shared_ptr<stream_session> session) {
    auto si = session->make_session_info();
    sslog.debug("[Stream #{}] Prepare completed with {}. Receiving {}, sending {}",
               session->plan_id(),
               session->peer,
               si.get_total_files_to_receive(),
               si.get_total_files_to_send());
    auto event = session_prepared_event(plan_id, si);
    session->get_session_info() = si;
    fire_stream_event(std::move(event));
}

void stream_result_future::handle_session_complete(shared_ptr<stream_session> session) {
    sslog.debug("[Stream #{}] Session with {} is complete, state={}", session->plan_id(), session->peer, session->get_state());
    auto event = session_complete_event(session);
    fire_stream_event(std::move(event));
    auto si = session->make_session_info();
    session->get_session_info() = si;
    maybe_complete();
}

template <typename Event>
void stream_result_future::fire_stream_event(Event event) {
    // delegate to listener
    for (auto listener : _event_listeners) {
        listener->handle_stream_event(std::move(event));
    }
}

void stream_result_future::maybe_complete() {
    auto has_active_sessions = _coordinator->has_active_sessions();
    auto plan_id = this->plan_id;
    sslog.debug("[Stream #{}] stream_result_future: has_active_sessions={}", plan_id, has_active_sessions);
    if (!has_active_sessions) {
        auto& sm = get_local_stream_manager();
        if (sslog.is_enabled(logging::log_level::debug)) {
            sm.show_streams();
        }
        auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(lowres_clock::now() - _start_time).count();
        auto stats = make_lw_shared<sstring>("");
        //FIXME: discarded future.
        (void)sm.get_progress_on_all_shards(plan_id).then([plan_id, duration, stats] (auto sbytes) {
            auto tx_bw = sstring("0");
            auto rx_bw = sstring("0");
            if (std::fabs(duration) > FLT_EPSILON) {
                tx_bw = format("{:.2f}", sbytes.bytes_sent / duration / 1024);
                rx_bw = format("{:.2f}", sbytes.bytes_received  / duration / 1024);
            }
            *stats = format("tx={:d} KiB, {} KiB/s, rx={:d} KiB, {} KiB/s", sbytes.bytes_sent / 1024, tx_bw, sbytes.bytes_received / 1024, rx_bw);
        }).handle_exception([plan_id] (auto ep) {
            sslog.warn("[Stream #{}] Fail to get progess on all shards: {}", plan_id, ep);
        }).finally([this, plan_id, stats, &sm] () {
            sm.remove_stream(plan_id);
            auto final_state = get_current_state();
            if (final_state.has_failed_session()) {
                sslog.warn("[Stream #{}] Streaming plan for {} failed, peers={}, {}", plan_id, description, _coordinator->get_peers(), *stats);
                _done.set_exception(stream_exception(final_state, "Stream failed"));
            } else {
                sslog.info("[Stream #{}] Streaming plan for {} succeeded, peers={}, {}", plan_id, description, _coordinator->get_peers(), *stats);
                _done.set_value(final_state);
            }
        });
    }
}

stream_state stream_result_future::get_current_state() {
    return stream_state(plan_id, description, _coordinator->get_all_session_info());
}

void stream_result_future::handle_progress(progress_info progress) {
    fire_stream_event(progress_event(plan_id, std::move(progress)));
}

} // namespace streaming
<commit_msg>streaming: Fix typo in stream_result_future::maybe_complete<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * Modified by ScyllaDB
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "streaming/stream_result_future.hh"
#include "streaming/stream_manager.hh"
#include "streaming/stream_exception.hh"
#include "log.hh"
#include <cfloat>

namespace streaming {

extern logging::logger sslog;

future<stream_state> stream_result_future::init_sending_side(UUID plan_id_, sstring description_,
        std::vector<stream_event_handler*> listeners_, shared_ptr<stream_coordinator> coordinator_) {
    auto sr = make_shared<stream_result_future>(plan_id_, description_, coordinator_);
    get_local_stream_manager().register_sending(sr);

    for (auto& listener : listeners_) {
        sr->add_event_listener(listener);
    }

    sslog.info("[Stream #{}] Executing streaming plan for {} with peers={}, master", plan_id_,  description_, coordinator_->get_peers());

    // Initialize and start all sessions
    for (auto& session : coordinator_->get_all_stream_sessions()) {
        session->init(sr);
    }
    coordinator_->connect_all_stream_sessions();

    return sr->_done.get_future();
}

shared_ptr<stream_result_future> stream_result_future::init_receiving_side(UUID plan_id, sstring description, inet_address from) {
    auto& sm = get_local_stream_manager();
    auto sr = sm.get_receiving_stream(plan_id);
    if (sr) {
        auto err = sprint("[Stream #%s] GOT PREPARE_MESSAGE from %s, description=%s,"
                          "stream_plan exists, duplicated message received?", plan_id, description, from);
        sslog.warn(err.c_str());
        throw std::runtime_error(err);
    }
    sslog.info("[Stream #{}] Executing streaming plan for {} with peers={}, slave", plan_id, description, from);
    bool is_receiving = true;
    sr = make_shared<stream_result_future>(plan_id, description, is_receiving);
    sm.register_receiving(sr);
    return sr;
}

void stream_result_future::handle_session_prepared(shared_ptr<stream_session> session) {
    auto si = session->make_session_info();
    sslog.debug("[Stream #{}] Prepare completed with {}. Receiving {}, sending {}",
               session->plan_id(),
               session->peer,
               si.get_total_files_to_receive(),
               si.get_total_files_to_send());
    auto event = session_prepared_event(plan_id, si);
    session->get_session_info() = si;
    fire_stream_event(std::move(event));
}

void stream_result_future::handle_session_complete(shared_ptr<stream_session> session) {
    sslog.debug("[Stream #{}] Session with {} is complete, state={}", session->plan_id(), session->peer, session->get_state());
    auto event = session_complete_event(session);
    fire_stream_event(std::move(event));
    auto si = session->make_session_info();
    session->get_session_info() = si;
    maybe_complete();
}

template <typename Event>
void stream_result_future::fire_stream_event(Event event) {
    // delegate to listener
    for (auto listener : _event_listeners) {
        listener->handle_stream_event(std::move(event));
    }
}

void stream_result_future::maybe_complete() {
    auto has_active_sessions = _coordinator->has_active_sessions();
    auto plan_id = this->plan_id;
    sslog.debug("[Stream #{}] stream_result_future: has_active_sessions={}", plan_id, has_active_sessions);
    if (!has_active_sessions) {
        auto& sm = get_local_stream_manager();
        if (sslog.is_enabled(logging::log_level::debug)) {
            sm.show_streams();
        }
        auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(lowres_clock::now() - _start_time).count();
        auto stats = make_lw_shared<sstring>("");
        //FIXME: discarded future.
        (void)sm.get_progress_on_all_shards(plan_id).then([plan_id, duration, stats] (auto sbytes) {
            auto tx_bw = sstring("0");
            auto rx_bw = sstring("0");
            if (std::fabs(duration) > FLT_EPSILON) {
                tx_bw = format("{:.2f}", sbytes.bytes_sent / duration / 1024);
                rx_bw = format("{:.2f}", sbytes.bytes_received  / duration / 1024);
            }
            *stats = format("tx={:d} KiB, {} KiB/s, rx={:d} KiB, {} KiB/s", sbytes.bytes_sent / 1024, tx_bw, sbytes.bytes_received / 1024, rx_bw);
        }).handle_exception([plan_id] (auto ep) {
            sslog.warn("[Stream #{}] Fail to get progress on all shards: {}", plan_id, ep);
        }).finally([this, plan_id, stats, &sm] () {
            sm.remove_stream(plan_id);
            auto final_state = get_current_state();
            if (final_state.has_failed_session()) {
                sslog.warn("[Stream #{}] Streaming plan for {} failed, peers={}, {}", plan_id, description, _coordinator->get_peers(), *stats);
                _done.set_exception(stream_exception(final_state, "Stream failed"));
            } else {
                sslog.info("[Stream #{}] Streaming plan for {} succeeded, peers={}, {}", plan_id, description, _coordinator->get_peers(), *stats);
                _done.set_value(final_state);
            }
        });
    }
}

stream_state stream_result_future::get_current_state() {
    return stream_state(plan_id, description, _coordinator->get_all_session_info());
}

void stream_result_future::handle_progress(progress_info progress) {
    fire_stream_event(progress_event(plan_id, std::move(progress)));
}

} // namespace streaming
<|endoftext|>