<commit_before>/*===================================================================
APM_PLANNER Open Source Ground Control Station

(c) 2016 APM_PLANNER PROJECT <http://www.ardupilot.com>

This file is part of the APM_PLANNER project

    APM_PLANNER is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    APM_PLANNER 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 APM_PLANNER. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/
/**
 * @file TlogParser.cpp.cpp
 * @author Arne Wischmann <wischmann-a@gmx.de>
 * @author Michael Carpenter <malcom2073@gmail.com>
 * @date 22 Okt 2016
 * @brief File providing implementation for the tlog log parser
 */

#include "TlogParser.h"
#include "logging.h"

bool TlogParser::tlogDescriptor::isValid() const
{
    return (m_ID != 0xFF) && (m_length > 0) && (m_name.size() > 0) &&
           (m_format.size() > 0) && (m_format.size() == m_labels.size());
}

//*****************************************

TlogParser::TlogParser(LogdataStorage::Ptr storagePtr, IParserCallback *object) :
    LogParserBase (storagePtr, object),
    m_mavDecoderPtr(new MAVLinkDecoder()),
    m_lastModeVal(255)
{
    QLOG_DEBUG() << "TlogParser::TlogParser - CTOR";
}

TlogParser::~TlogParser()
{
    QLOG_DEBUG() << "TlogParser::TlogParser - DTOR";
}

AP2DataPlotStatus TlogParser::parse(QFile &logfile)
{
    QLOG_DEBUG() << "TlogParser::parse:" << logfile.fileName();

    if(!m_dataStoragePtr || !m_callbackObject)
    {
        QLOG_ERROR() << "TlogParser::parse - No valid datamodel or callback object - parsing stopped";
        return m_logLoadingState;
    }

    // tlogs always have this timestamp
    m_activeTimestamp = timeStampType("time_boot_ms", 1000.0);

    // tlogs do not provide special messages like MODE or MSG. As we can reconstruct the data
    // from other messages we add those descriptors artificially to the DB
    addMissingDescriptors();

    int emptyMessages = 0;

    while(!logfile.atEnd() && !m_stop)
    {
        mavlink_message_t mavlinkMessage;
        mavlink_status_t mavlinkStatus;

        m_callbackObject->onProgress(logfile.pos(),logfile.size());
        m_dataBlock = logfile.read(8192);

        for (int i = 0; i < m_dataBlock.size(); ++i)
        {
            unsigned int decodeState = mavlink_parse_char(14, static_cast<uint8_t>(m_dataBlock[i]), &mavlinkMessage, &mavlinkStatus);
            if (decodeState == MAVLINK_FRAMING_OK)
            {
                tlogDescriptor descriptor;
                descriptor.m_name = m_mavDecoderPtr->getMessageName(mavlinkMessage.msgid);
                if ((descriptor.m_name != "EMPTY") && (mavlinkMessage.sysid != s_GCSType))
                {
                    descriptor.m_ID = mavlinkMessage.msgid;
                    if(!m_nameToDescriptorMap.contains(descriptor.m_name))
                    {
                        if(parseDescriptor(descriptor))
                        {
                            descriptor.finalize(m_activeTimestamp);
                            if(!storeDescriptor(descriptor))
                            {
                                return m_logLoadingState;
                            }
                        }
                    }

                    // Read packet data - if there is something
                    QList<NameValuePair> NameValuePairList;
                    if(decodeData(mavlinkMessage, NameValuePairList))
                    {
                        if(!storeNameValuePairList(NameValuePairList, m_nameToDescriptorMap.value(descriptor.m_name)))
                        {
                            return m_logLoadingState;
                        }

                        // Special message handling - Heartbeat
                        if(mavlinkMessage.msgid == MAVLINK_MSG_ID_HEARTBEAT)
                        {
                            // extract mode message from tlog data
                            if(!extractModeMessage(NameValuePairList))
                            {
                                return m_logLoadingState;
                            }
                            // detect mav type
                            if(m_loadedLogType == MAV_TYPE_GENERIC)
                            {
                                detectMavType(NameValuePairList);
                            }

                        }
                        // Special message handling - Statustext
                        else if(mavlinkMessage.msgid == MAVLINK_MSG_ID_STATUSTEXT)
                        {
                            // Create a MsgMessage from STATUSTEXT
                            if(!extractMsgMessage(NameValuePairList))
                            {
                                return m_logLoadingState;
                            }
                        }
                    }
                }
                else
                {
                   emptyMessages++;
                }
            }
            else if(decodeState == MAVLINK_FRAMING_BAD_CRC)
            {
                m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter), "Bad CRC");
            }
        }
    }

    if(emptyMessages != 0) // Did we have messages named "EMPTY" ?
    {
        m_logLoadingState.corruptDataRead(0, "Found " + QString::number(emptyMessages) +" 'EMPTY' messages wich could not be processed");
    }

    m_dataStoragePtr->setTimeStamp(m_activeTimestamp.m_name, m_activeTimestamp.m_divisor);
    return m_logLoadingState;
}

void TlogParser::addMissingDescriptors()
{
    // Tlog does not contain MODE messages the mode information ins transmitted in
    // a heartbeat message. So we create the datatype for MODE here and put it into data model
    tlogDescriptor descriptor;
    descriptor.m_ID = 0;
    descriptor.m_name = ModeMessage::TypeName;
    descriptor.m_format = QString("QMBZ");
    descriptor.m_length = 8 + 1 + 1 + 64;     // Q:8byte M:1byte B:1byte Z:64byte
    descriptor.m_labels.push_back(m_activeTimestamp.m_name);
    descriptor.m_labels.push_back(QString("Mode"));
    descriptor.m_labels.push_back(QString("ModeNum"));
    descriptor.m_labels.push_back(QString("Info"));
    descriptor.finalize(m_activeTimestamp);

    storeDescriptor(descriptor);

    // Tlog does not contain MSG messages. The information is gathered from STATUSTEXT tlog
    // messages. So we create the datatype for MSG here and put it into data model.
    descriptor = tlogDescriptor();
    descriptor.m_ID = 0;
    descriptor.m_name = MsgMessage::TypeName;
    descriptor.m_format = QString("QZZ");
    descriptor.m_length = 8 + 64 + 64;                   // Q:8byte Z:64byte Z:64byte
    descriptor.m_labels.push_back(m_activeTimestamp.m_name);
    descriptor.m_labels.push_back(QString("Message"));
    descriptor.m_labels.push_back(QString("Info"));
    descriptor.finalize(m_activeTimestamp);

    storeDescriptor(descriptor);
}

bool TlogParser::parseDescriptor(tlogDescriptor &desc)
{
    QList<QString> fieldnames = m_mavDecoderPtr->getFieldList(desc.m_name);
    for (int i = 0; i < fieldnames.size(); ++i)
    {
        mavlink_field_info_t fieldinfo = m_mavDecoderPtr->getFieldInfo(desc.m_name, fieldnames.at(i));
        desc.m_labels.push_back(QString(fieldinfo.name));
        switch (fieldinfo.type)
        {
            case MAVLINK_TYPE_CHAR:
            {
                if (fieldinfo.array_length == 0)
                {
                    desc.m_format += "b";   // it is a single byte
                    desc.m_length += 1;
                }
                else
                {
                    desc.m_format += "Z";   // everything else is a string
                    desc.m_length += 64;
                }
            }
            break;
            case MAVLINK_TYPE_UINT8_T:
            {
                desc.m_format += "B";
                desc.m_length += 1;
            }
            break;
            case MAVLINK_TYPE_INT8_T:
            {
                desc.m_format += "b";
                desc.m_length += 1;
            }
            break;
            case MAVLINK_TYPE_UINT16_T:
            {
                desc.m_format += "H";
                desc.m_length += 2;
            }
            break;
            case MAVLINK_TYPE_INT16_T:
            {
                desc.m_format += "h";
                desc.m_length += 2;
            }
            break;
            case MAVLINK_TYPE_UINT32_T:
            {
                desc.m_format += "I";
                desc.m_length += 4;
            }
                break;
            case MAVLINK_TYPE_INT32_T:
            {
                desc.m_format += "i";
                desc.m_length += 4;
            }
            break;
            case MAVLINK_TYPE_FLOAT:
            {
                desc.m_format += "f";
                desc.m_length += 4;
            }
            break;
            case MAVLINK_TYPE_DOUBLE:
            {
                desc.m_format += "d";
                desc.m_length += 8;
            }
            break;
            case MAVLINK_TYPE_UINT64_T:
            {
                desc.m_format += "Q";
                desc.m_length += 8;
            }
            break;
            case MAVLINK_TYPE_INT64_T:
            {
                desc.m_format += "q";
                desc.m_length += 8;
            }
            break;
            default:
            {
                QLOG_ERROR() << "TlogParser::parseDescriptor:Unknown data type:" << QString::number(fieldinfo.type);
                m_logLoadingState.corruptFMTRead(static_cast<int>(m_MessageCounter),
                                                 desc.m_name + " data: Unknown data type:" + QString::number(fieldinfo.type));
                return false;
            }
        }
    }
    return true;
}

bool TlogParser::storeDescriptor(tlogDescriptor desc)
{
    if(desc.isValid())
    {
        m_nameToDescriptorMap.insert(desc.m_name, desc);
        if(desc.hasNoTimestamp())
        {
            desc.addTimeStampField(m_activeTimestamp);
        }

        m_dataStoragePtr->addDataType(desc.m_name, desc.m_ID, desc.m_length, desc.m_format, desc.m_labels, desc.m_timeStampIndex);
        m_MessageCounter++;
    }
    else
    {
        QLOG_WARN() << "TlogParser::storeDescriptor(): Invalid type descriptor found for type" << desc.m_ID << ":" << desc.m_name;
        m_logLoadingState.corruptFMTRead(static_cast<int>(m_MessageCounter), desc.m_name +
                                         " format data: Corrupt or missing. Message type is:0x" + QString::number(desc.m_ID, 16));
    }
    return true;
}

bool TlogParser::decodeData(const mavlink_message_t &mavlinkMessage, QList<NameValuePair> &NameValuePairList)
{
    QList<NameValuePair> decodedMessage = m_mavDecoderPtr->receiveMessage(0, mavlinkMessage);
    for (int i = 0; i < decodedMessage.size(); ++i)
    {
        QStringList list = decodedMessage.at(i).first.split(".");
        if (list.size() >= 2)
        {
            NameValuePairList.append(NameValuePair(list[1], decodedMessage.at(i).second));
        }
        else
        {
            QLOG_WARN() << "Missing data type information. Message:" << decodedMessage.at(i).first <<  ":"
                        << decodedMessage.at(i).second.toString();
            m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter),
                                              "Missing data type information. Message:" + decodedMessage.at(i).first +
                                              ":" + decodedMessage.at(i).second.toString());
            return false;
        }
    }
    return NameValuePairList.size() > 0 ? true : false;
}

bool TlogParser::extractModeMessage(const QList<NameValuePair> &NameValuePairList)
{
    // Tlog does not contain MODE messages the mode information ins transmitted in
    // a heartbeat message. So here we extract MODE data from heartbeat

    // Only if mode val has canged
    if (m_lastModeVal != static_cast<quint8>(NameValuePairList[1].second.toInt()))
    {
        QList<NameValuePair> modeValuePairlist;
        tlogDescriptor modeDesc = m_nameToDescriptorMap.value(ModeMessage::TypeName);
        // Extract MODE messages from heratbeat messages
        m_lastModeVal = static_cast<quint8>(NameValuePairList[1].second.toInt());

        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[0], m_lastValidTimeStamp));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[1], m_lastModeVal));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[2], m_lastModeVal));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[3], "Generated from heartbeat"));

        if(!storeNameValuePairList(modeValuePairlist, modeDesc))
        {
            return false;
        }
    }

    return true;
}

bool TlogParser::extractMsgMessage(const QList<NameValuePair> &NameValuePairList)
{
    // Tlog does not contain MSG messages the MSG information ins transmitted in
    // a statustext message. So here we extract MSG data from statustext
    QList<NameValuePair> msgValuePairlist;
    tlogDescriptor msgDesc = m_nameToDescriptorMap.value(MsgMessage::TypeName);

    if((msgDesc.m_labels.size() >= 2) && (NameValuePairList.size() >= 2))
    {
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[0], m_lastValidTimeStamp));
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[1], NameValuePairList[2].second));
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[2], "Generated from statustext"));
        if (!storeNameValuePairList(msgValuePairlist, msgDesc))
        {
            return false;
        }
    }
    else
    {
        m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter), "MSG message extraction from statustext failed. Not enough elements.");
    }
    return true;
}
<commit_msg>Graph View: Extended type checking for tlog parser<commit_after>/*===================================================================
APM_PLANNER Open Source Ground Control Station

(c) 2016 APM_PLANNER PROJECT <http://www.ardupilot.com>

This file is part of the APM_PLANNER project

    APM_PLANNER is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    APM_PLANNER 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 APM_PLANNER. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/
/**
 * @file TlogParser.cpp.cpp
 * @author Arne Wischmann <wischmann-a@gmx.de>
 * @author Michael Carpenter <malcom2073@gmail.com>
 * @date 22 Okt 2016
 * @brief File providing implementation for the tlog log parser
 */

#include "TlogParser.h"
#include "logging.h"

bool TlogParser::tlogDescriptor::isValid() const
{
    return (m_ID != 0xFF) && (m_length > 0) && (m_name.size() > 0) &&
           (m_format.size() > 0) && (m_format.size() == m_labels.size());
}

//*****************************************

TlogParser::TlogParser(LogdataStorage::Ptr storagePtr, IParserCallback *object) :
    LogParserBase (storagePtr, object),
    m_mavDecoderPtr(new MAVLinkDecoder()),
    m_lastModeVal(255)
{
    QLOG_DEBUG() << "TlogParser::TlogParser - CTOR";
}

TlogParser::~TlogParser()
{
    QLOG_DEBUG() << "TlogParser::TlogParser - DTOR";
}

AP2DataPlotStatus TlogParser::parse(QFile &logfile)
{
    QLOG_DEBUG() << "TlogParser::parse:" << logfile.fileName();

    if(!m_dataStoragePtr || !m_callbackObject)
    {
        QLOG_ERROR() << "TlogParser::parse - No valid datamodel or callback object - parsing stopped";
        return m_logLoadingState;
    }

    // tlogs always have this timestamp
    m_activeTimestamp = timeStampType("time_boot_ms", 1000.0);

    // tlogs do not provide special messages like MODE or MSG. As we can reconstruct the data
    // from other messages we add those descriptors artificially to the DB
    addMissingDescriptors();

    int emptyMessages = 0;

    while(!logfile.atEnd() && !m_stop)
    {
        mavlink_message_t mavlinkMessage;
        mavlink_status_t mavlinkStatus;

        m_callbackObject->onProgress(logfile.pos(),logfile.size());
        m_dataBlock = logfile.read(8192);

        for (int i = 0; i < m_dataBlock.size(); ++i)
        {
            unsigned int decodeState = mavlink_parse_char(14, static_cast<uint8_t>(m_dataBlock[i]), &mavlinkMessage, &mavlinkStatus);
            if (decodeState == MAVLINK_FRAMING_OK)
            {
                tlogDescriptor descriptor;
                descriptor.m_name = m_mavDecoderPtr->getMessageName(mavlinkMessage.msgid);
                if ((descriptor.m_name != "EMPTY") && (mavlinkMessage.sysid != s_GCSType))
                {
                    descriptor.m_ID = mavlinkMessage.msgid;
                    if(!m_nameToDescriptorMap.contains(descriptor.m_name))
                    {
                        if(parseDescriptor(descriptor))
                        {
                            descriptor.finalize(m_activeTimestamp);
                            if(!storeDescriptor(descriptor))
                            {
                                return m_logLoadingState;
                            }
                        }
                    }

                    // Read packet data - if there is something
                    QList<NameValuePair> NameValuePairList;
                    if(decodeData(mavlinkMessage, NameValuePairList))
                    {
                        if(!storeNameValuePairList(NameValuePairList, m_nameToDescriptorMap.value(descriptor.m_name)))
                        {
                            return m_logLoadingState;
                        }

                        // Special message handling - Heartbeat
                        if(mavlinkMessage.msgid == MAVLINK_MSG_ID_HEARTBEAT)
                        {
                            // extract mode message from tlog data
                            if(!extractModeMessage(NameValuePairList))
                            {
                                return m_logLoadingState;
                            }
                            // detect mav type
                            if(m_loadedLogType == MAV_TYPE_GENERIC)
                            {
                                detectMavType(NameValuePairList);
                            }

                        }
                        // Special message handling - Statustext
                        else if(mavlinkMessage.msgid == MAVLINK_MSG_ID_STATUSTEXT)
                        {
                            // Create a MsgMessage from STATUSTEXT
                            if(!extractMsgMessage(NameValuePairList))
                            {
                                return m_logLoadingState;
                            }
                        }
                    }
                }
                else
                {
                   emptyMessages++;
                }
            }
            else if(decodeState == MAVLINK_FRAMING_BAD_CRC)
            {
                m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter), "Bad CRC");
            }
        }
    }

    if(emptyMessages != 0) // Did we have messages named "EMPTY" ?
    {
        m_logLoadingState.corruptDataRead(0, "Found " + QString::number(emptyMessages) +" 'EMPTY' messages wich could not be processed");
    }

    m_dataStoragePtr->setTimeStamp(m_activeTimestamp.m_name, m_activeTimestamp.m_divisor);
    return m_logLoadingState;
}

void TlogParser::addMissingDescriptors()
{
    // Tlog does not contain MODE messages the mode information ins transmitted in
    // a heartbeat message. So we create the datatype for MODE here and put it into data model
    tlogDescriptor descriptor;
    descriptor.m_ID = 0;
    descriptor.m_name = ModeMessage::TypeName;
    descriptor.m_format = QString("QMBZ");
    descriptor.m_length = 8 + 1 + 1 + 64;     // Q:8byte M:1byte B:1byte Z:64byte
    descriptor.m_labels.push_back(m_activeTimestamp.m_name);
    descriptor.m_labels.push_back(QString("Mode"));
    descriptor.m_labels.push_back(QString("ModeNum"));
    descriptor.m_labels.push_back(QString("Info"));
    descriptor.finalize(m_activeTimestamp);

    storeDescriptor(descriptor);

    // Tlog does not contain MSG messages. The information is gathered from STATUSTEXT tlog
    // messages. So we create the datatype for MSG here and put it into data model.
    descriptor = tlogDescriptor();
    descriptor.m_ID = 0;
    descriptor.m_name = MsgMessage::TypeName;
    descriptor.m_format = QString("QZZ");
    descriptor.m_length = 8 + 64 + 64;                   // Q:8byte Z:64byte Z:64byte
    descriptor.m_labels.push_back(m_activeTimestamp.m_name);
    descriptor.m_labels.push_back(QString("Message"));
    descriptor.m_labels.push_back(QString("Info"));
    descriptor.finalize(m_activeTimestamp);

    storeDescriptor(descriptor);
}

bool TlogParser::parseDescriptor(tlogDescriptor &desc)
{
    QList<QString> fieldnames = m_mavDecoderPtr->getFieldList(desc.m_name);
    for (int i = 0; i < fieldnames.size(); ++i)
    {
        mavlink_field_info_t fieldinfo = m_mavDecoderPtr->getFieldInfo(desc.m_name, fieldnames.at(i));
        desc.m_labels.push_back(QString(fieldinfo.name));
        switch (fieldinfo.type)
        {
            case MAVLINK_TYPE_CHAR:
            {
                if (fieldinfo.array_length == 0)
                {
                    desc.m_format += "b";   // it is a single byte
                    desc.m_length += 1;
                }
                else
                {
                    desc.m_format += "Z";   // everything else is a string
                    desc.m_length += 64;
                }
            }
            break;
            case MAVLINK_TYPE_UINT8_T:
            {
                desc.m_format += "B";
                desc.m_length += 1;
            }
            break;
            case MAVLINK_TYPE_INT8_T:
            {
                desc.m_format += "b";
                desc.m_length += 1;
            }
            break;
            case MAVLINK_TYPE_UINT16_T:
            {
                desc.m_format += "H";
                desc.m_length += 2;
            }
            break;
            case MAVLINK_TYPE_INT16_T:
            {
                desc.m_format += "h";
                desc.m_length += 2;
            }
            break;
            case MAVLINK_TYPE_UINT32_T:
            {
                desc.m_format += "I";
                desc.m_length += 4;
            }
                break;
            case MAVLINK_TYPE_INT32_T:
            {
                desc.m_format += "i";
                desc.m_length += 4;
            }
            break;
            case MAVLINK_TYPE_FLOAT:
            {
                desc.m_format += "f";
                desc.m_length += 4;
            }
            break;
            case MAVLINK_TYPE_DOUBLE:
            {
                desc.m_format += "d";
                desc.m_length += 8;
            }
            break;
            case MAVLINK_TYPE_UINT64_T:
            {
                desc.m_format += "Q";
                desc.m_length += 8;
            }
            break;
            case MAVLINK_TYPE_INT64_T:
            {
                desc.m_format += "q";
                desc.m_length += 8;
            }
            break;
            default:
            {
                QLOG_ERROR() << "TlogParser::parseDescriptor:Unknown data type:" << QString::number(fieldinfo.type);
                m_logLoadingState.corruptFMTRead(static_cast<int>(m_MessageCounter),
                                                 desc.m_name + " data: Unknown data type:" + QString::number(fieldinfo.type));
                return false;
            }
        }
    }
    return true;
}

bool TlogParser::storeDescriptor(tlogDescriptor desc)
{
    if(desc.isValid())
    {
        m_nameToDescriptorMap.insert(desc.m_name, desc);
        if(desc.hasNoTimestamp())
        {
            desc.addTimeStampField(m_activeTimestamp);
        }

        m_dataStoragePtr->addDataType(desc.m_name, desc.m_ID, desc.m_length, desc.m_format, desc.m_labels, desc.m_timeStampIndex);
        m_MessageCounter++;
    }
    else
    {
        QLOG_WARN() << "TlogParser::storeDescriptor(): Invalid type descriptor found for type" << desc.m_ID << ":" << desc.m_name;
        m_logLoadingState.corruptFMTRead(static_cast<int>(m_MessageCounter), desc.m_name +
                                         " format data: Corrupt or missing. Message type is:0x" + QString::number(desc.m_ID, 16));
    }
    return true;
}

bool TlogParser::decodeData(const mavlink_message_t &mavlinkMessage, QList<NameValuePair> &NameValuePairList)
{
    QList<NameValuePair> decodedMessage = m_mavDecoderPtr->receiveMessage(0, mavlinkMessage);
    if(!decodedMessage.empty())
    {
        // Copy data
        for (int i = 0; i < decodedMessage.size(); ++i)
        {
            QStringList list = decodedMessage.at(i).first.split(".");
            if (list.size() >= 2)   // There must be at least 2 elements
            {
                NameValuePairList.append(NameValuePair(list[1], decodedMessage.at(i).second));
            }
            else
            {
                QLOG_WARN() << "Missing data type information. Message:" << decodedMessage.at(i).first <<  ":"
                            << decodedMessage.at(i).second.toString();
                m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter),
                                                  "Missing data type information. Message:" + decodedMessage.at(i).first +
                                                  ":" + decodedMessage.at(i).second.toString());
                return false;
            }
        }

        // Verify data matches descriptor - simple size check
        QString typeName = m_mavDecoderPtr->getMessageName(mavlinkMessage.msgid);
        tlogDescriptor descriptor = m_nameToDescriptorMap.value(typeName);
        if(NameValuePairList.size() != descriptor.m_labels.size())
        {
            QLOG_WARN() << "Number of received values does not match number defined in type. Type:"
                        << typeName << " Expected:" << descriptor.m_labels.size() << " got:"
                        << NameValuePairList.size() << ". Dropping message.";
            m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter),
                                              "Number of received values does not match number defined in type. Type:"
                                              + typeName + " Expected:" + QString::number(descriptor.m_labels.size()) + " got:"
                                              + QString::number(NameValuePairList.size()) + ". Dropping message.");
            return false;
        }
        return true;    // everything is ok
    }
    return false;   // No data
}

bool TlogParser::extractModeMessage(const QList<NameValuePair> &NameValuePairList)
{
    // Tlog does not contain MODE messages the mode information ins transmitted in
    // a heartbeat message. So here we extract MODE data from heartbeat

    // Only if mode val has canged
    if (m_lastModeVal != static_cast<quint8>(NameValuePairList[1].second.toInt()))
    {
        QList<NameValuePair> modeValuePairlist;
        tlogDescriptor modeDesc = m_nameToDescriptorMap.value(ModeMessage::TypeName);
        // Extract MODE messages from heratbeat messages
        m_lastModeVal = static_cast<quint8>(NameValuePairList[1].second.toInt());

        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[0], m_lastValidTimeStamp));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[1], m_lastModeVal));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[2], m_lastModeVal));
        modeValuePairlist.append(QPair<QString, QVariant>(modeDesc.m_labels[3], "Generated from heartbeat"));

        if(!storeNameValuePairList(modeValuePairlist, modeDesc))
        {
            return false;
        }
    }

    return true;
}

bool TlogParser::extractMsgMessage(const QList<NameValuePair> &NameValuePairList)
{
    // Tlog does not contain MSG messages the MSG information ins transmitted in
    // a statustext message. So here we extract MSG data from statustext
    QList<NameValuePair> msgValuePairlist;
    tlogDescriptor msgDesc = m_nameToDescriptorMap.value(MsgMessage::TypeName);

    if((msgDesc.m_labels.size() >= 2) && (NameValuePairList.size() >= 2))
    {
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[0], m_lastValidTimeStamp));
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[1], NameValuePairList[2].second));
        msgValuePairlist.append(QPair<QString, QVariant>(msgDesc.m_labels[2], "Generated from statustext"));
        if (!storeNameValuePairList(msgValuePairlist, msgDesc))
        {
            return false;
        }
    }
    else
    {
        m_logLoadingState.corruptDataRead(static_cast<int>(m_MessageCounter), "MSG message extraction from statustext failed. Not enough elements.");
    }
    return true;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: versionhelper.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: lla $ $Date: 2003-01-20 11:10:27 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdlib.h>
#include "versionhelper.hxx"

#include <rtl/ustring.hxx>

// -----------------------------------------------------------------------------
VersionHelper::VersionHelper(rtl::OUString const& _sDLLName, GetOpt & _aOptions)
        :DynamicLibraryHelper(_sDLLName, _aOptions),
         m_pInfo(NULL)
{
    // try to get the entry pointer
    FktGetVersionInfoPtr pFunc = (FktGetVersionInfoPtr) m_pModule->getSymbol( rtl::OUString::createFromAscii( "GetVersionInfo" ) );

    if (pFunc)
    {
        const VersionInfo *pVersion = (pFunc)();
        m_pInfo = pVersion;
    }
}

void VersionHelper::print(std::ostream &stream)
{
    stream << "  Time:" << m_pInfo->aTime   << std::endl;
    stream << "  Date:" << m_pInfo->aDate   << std::endl;
    stream << "   Upd:" << m_pInfo->aUpd    << std::endl;
    stream << " Minor:" << m_pInfo->aMinor  << std::endl;
    stream << " Build:" << m_pInfo->aBuild  << std::endl;
    stream << "Inpath:" << m_pInfo->aInpath << std::endl;
}

std::ostream &
operator <<( std::ostream &stream,
             VersionHelper &_aVersion )
{
    _aVersion.print (stream);
    return stream;
}

// versioner gpf zu haeufig.
// perl script um alles herum
<commit_msg>INTEGRATION: CWS qadev3 (1.3.16); FILE MERGED 2003/03/31 09:12:00 lla 1.3.16.1: #108453# better version number handling<commit_after>/*************************************************************************
 *
 *  $RCSfile: versionhelper.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: vg $ $Date: 2003-04-01 13:17:18 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdlib.h>
#include "versionhelper.hxx"

#include <rtl/ustring.hxx>
#include <rtl/string.hxx>

// -----------------------------------------------------------------------------
VersionHelper::VersionHelper(rtl::OUString const& _sDLLName, GetOpt & _aOptions)
        :DynamicLibraryHelper(_sDLLName, _aOptions),
         m_pInfo(NULL)
{
    // try to get the entry pointer
    FktGetVersionInfoPtr pFunc = (FktGetVersionInfoPtr) m_pModule->getSymbol( rtl::OUString::createFromAscii( "GetVersionInfo" ) );

    if (pFunc)
    {
        const VersionInfo *pVersion = (pFunc)();
        m_pInfo = pVersion;
    }
}

//# void VersionHelper::print(std::ostream &stream)
//# {
//#     stream << "  Time:" << getTime()   << std::endl;
//#     stream << "  Date:" << getDate()   << std::endl;
//#     stream << "   Upd:" << getUpd()    << std::endl;
//#     stream << " Minor:" << getMinor()  << std::endl;
//#     stream << " Build:" << getBuild()  << std::endl;
//#     stream << "Inpath:" << getInpath() << std::endl;
//# }
//#
//# std::ostream & operator <<( std::ostream &stream, VersionHelper &_aVersion )
//# {
//#     _aVersion.print (stream);
//#     return stream;
//# }
//#
// -----------------------------------------------------------------------------

bool VersionHelper::isOk() const
{
    if (m_pInfo != NULL) return true;
    return false;
}

rtl::OString VersionHelper::getTime() const
{
#if SUPD < 669
    return m_pInfo->pTime;
#else
    return m_pInfo->aTime;
#endif
}
rtl::OString VersionHelper::getDate() const
{
#if SUPD < 669
    return m_pInfo->pDate;
#else
    return m_pInfo->aDate;
#endif
}
rtl::OString VersionHelper::getUpd() const
{
#if SUPD < 669
    return m_pInfo->pUpd;
#else
    return m_pInfo->aUpd;
#endif
}
rtl::OString VersionHelper::getMinor() const
{
#if SUPD < 669
    return m_pInfo->pMinor;
#else
    return m_pInfo->aMinor;
#endif
}
rtl::OString VersionHelper::getBuild() const
{
#if SUPD < 669
    return m_pInfo->pBuild;
#else
    return m_pInfo->aBuild;
#endif
}
rtl::OString VersionHelper::getInpath() const
{
#if SUPD < 669
    return m_pInfo->pInpath;
#else
    return m_pInfo->aInpath;
#endif
}

void VersionHelper::printall(FILE * out)
{
    if (isOk())
    {
        fprintf(out, "  Time:%s\n", getTime().getStr()  );
        fprintf(out, "  Date:%s\n", getDate().getStr()  );
        fprintf(out, "   Upd:%s\n", getUpd().getStr()   );
        fprintf(out, " Minor:%s\n", getMinor().getStr() );
        fprintf(out, " Build:%s\n", getBuild().getStr() );
        fprintf(out, "Inpath:%s\n", getInpath().getStr());

        fflush(out);
    }
    else
    {
        fprintf(stderr, "error: No version info found.\n");
    }
}

<|endoftext|>


<commit_before>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2021 Ryo Suzuki
//	Copyright (c) 2016-2021 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <Siv3D/Monitor.hpp>
# include <Siv3D/Unicode.hpp>
# include <Siv3D/Window/IWindow.hpp>
# include <Siv3D/Common/Siv3DEngine.hpp>
# include <Siv3D/Windows/Windows.hpp>
# include <Siv3D/EngineLog.hpp>
# include <Siv3D/DLL.hpp>
# include <ShellScalingApi.h> // for GetDpiForMonitor()

namespace s3d
{
	Optional<decltype(GetDpiForMonitor)*> g_pGetDpiForMonitor;

	namespace detail
	{
		// チェック用デバイス名とモニタハンドル
		using MonitorCheck = std::pair<const String, HMONITOR>;

		static BOOL CALLBACK MonitorCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM userData)
		{
			if (not g_pGetDpiForMonitor.has_value())
			{
				g_pGetDpiForMonitor = nullptr;

				if (LibraryHandle shcore = DLL::LoadSystemLibraryNoThrow(L"Shcore.dll"))
				{
					decltype(GetDpiForMonitor)* pGetDpiForMonitor = DLL::GetFunctionNoThrow(shcore, "GetDpiForMonitor");
					*g_pGetDpiForMonitor = pGetDpiForMonitor;
				}
			}

			MONITORINFOEX monitorInfo{};
			monitorInfo.cbSize = sizeof(monitorInfo);
			::GetMonitorInfoW(hMonitor, &monitorInfo);

			MonitorInfo* monitor = reinterpret_cast<MonitorInfo*>(userData);

			if (monitor->displayDeviceName.toWstr() == monitorInfo.szDevice)
			{
				monitor->displayRect.x = monitorInfo.rcMonitor.left;
				monitor->displayRect.y = monitorInfo.rcMonitor.top;
				monitor->displayRect.w = (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
				monitor->displayRect.h = (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);

				monitor->workArea.x = monitorInfo.rcWork.left;
				monitor->workArea.y = monitorInfo.rcWork.top;
				monitor->workArea.w = (monitorInfo.rcWork.right - monitorInfo.rcWork.left);
				monitor->workArea.h = (monitorInfo.rcWork.bottom - monitorInfo.rcWork.top);

				monitor->fullscreenResolution = monitor->displayRect.size;

				if (*g_pGetDpiForMonitor)
				{
					uint32 dpiX = 0, dpiY = 0;
					if (SUCCEEDED((*g_pGetDpiForMonitor)(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
					{
						monitor->scaling = (dpiX / static_cast<double>(USER_DEFAULT_SCREEN_DPI));
					}
				}
				else
				{
					monitor->scaling = 1.0;
				}

				return false;
			}

			return true;
		}

		static MonitorInfo MakeMonitorInfo(const DISPLAY_DEVICEW& displayDevice, const DISPLAY_DEVICEW& monitor)
		{
			MonitorInfo monitorInfo;
			monitorInfo.name				= Unicode::FromWstring(monitor.DeviceString);
			monitorInfo.id					= Unicode::FromWstring(monitor.DeviceID);
			monitorInfo.displayDeviceName	= Unicode::FromWstring(displayDevice.DeviceName);
			monitorInfo.isPrimary			= !!(displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);

			{
				const HDC hdc = ::CreateDCW(L"DISPLAY", displayDevice.DeviceName, nullptr, nullptr);
				Size sizeMillimeter{ ::GetDeviceCaps(hdc, HORZSIZE),  ::GetDeviceCaps(hdc, VERTSIZE) };
				::DeleteDC(hdc);
				monitorInfo.sizeMillimeter = sizeMillimeter;
			}

			if (DEVMODE devMode{ .dmSize = sizeof(DEVMODE) };
				::EnumDisplaySettingsW(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode))
			{
				monitorInfo.refreshRate = devMode.dmDisplayFrequency;
			}

			// モニターの配置とサイズを取得
			::EnumDisplayMonitors(nullptr, nullptr, MonitorCallback, (LPARAM)&monitorInfo);

			return monitorInfo;
		}

		static BOOL CALLBACK MonitorCheckProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM userData)
		{
			MONITORINFOEX monitorInfo{};
			monitorInfo.cbSize = sizeof(monitorInfo);
			::GetMonitorInfoW(hMonitor, &monitorInfo);

			MonitorCheck* monitor = (MonitorCheck*)userData;

			if (monitor->first.toWstr() == monitorInfo.szDevice)
			{
				monitor->second = hMonitor;
				return false;
			}

			return true;
		}

		static bool GetFriendlyName(Array<MonitorInfo>& monitors)
		{
			UINT32 nPaths, nModes;

			if (::GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &nPaths, &nModes) != ERROR_SUCCESS)
			{
				return false;
			}

			Array<DISPLAYCONFIG_PATH_INFO> displayPaths(nPaths);
			Array<DISPLAYCONFIG_MODE_INFO> displayModes(nModes);

			if (::QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &nPaths, displayPaths.data(), &nModes, displayModes.data(), nullptr) != ERROR_SUCCESS)
			{
				return false;
			}

			for (const auto& mode : displayModes)
			{
				if (mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
				{
					continue;
				}

				const DISPLAYCONFIG_DEVICE_INFO_HEADER deviceHeader
				{
					.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
					.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME),
					.adapterId = mode.adapterId,
					.id = mode.id,
				};

				DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName
				{
					.header = deviceHeader
				};

				if (::DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS)
				{
					continue;
				}

				{
					String friendlyName = Unicode::FromWstring(deviceName.monitorFriendlyDeviceName);

					if ((deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL)
						|| (deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED)
						|| (deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED))
					{
						friendlyName += U"(Internal Display)";
					}

					const String id = Unicode::FromWstring(deviceName.monitorDevicePath);

					for (auto& monitor : monitors)
					{
						if (monitor.id == id)
						{
							if (friendlyName)
							{
								monitor.name = std::move(friendlyName);
							}

							break;
						}
					}
				}
			}

			return true;
		}
	}

	namespace System
	{
		Array<MonitorInfo> EnumerateMonitors()
		{
			Array<MonitorInfo> monitors;
			DISPLAY_DEVICE displayDevice{ .cb = sizeof(DISPLAY_DEVICE) };

			// デスクトップとして割り当てられている仮想ディスプレイを検索
			for (int32 deviceIndex = 0; ::EnumDisplayDevicesW(0, deviceIndex, &displayDevice, 0); ++deviceIndex)
			{
				if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
				{
					DISPLAY_DEVICE monitor{ .cb = sizeof(DISPLAY_DEVICE) };

					// デスクトップとして使われているモニターの一覧を取得
					for (int32 monitorIndex = 0; ::EnumDisplayDevicesW(displayDevice.DeviceName, monitorIndex, &monitor, EDD_GET_DEVICE_INTERFACE_NAME); ++monitorIndex)
					{
						if ((monitor.StateFlags & DISPLAY_DEVICE_ACTIVE) &&
							not(monitor.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
						{

							monitors.push_back(detail::MakeMonitorInfo(displayDevice, monitor));
						}

						ZeroMemory(&monitor, sizeof(monitor));
						monitor.cb = sizeof(monitor);
					}
				}

				ZeroMemory(&displayDevice, sizeof(displayDevice));
				displayDevice.cb = sizeof(displayDevice);
			}

			detail::GetFriendlyName(monitors);

			return monitors;
		}

		size_t GetCurrentMonitorIndex()
		{
			const HMONITOR currentMonitor = ::MonitorFromWindow(static_cast<HWND>(SIV3D_ENGINE(Window)->getHandle()), MONITOR_DEFAULTTOPRIMARY);
			size_t index = 0;

			DISPLAY_DEVICE displayDevice =
			{
				.cb = sizeof(DISPLAY_DEVICE),
			};

			// デスクトップとして割り当てられている仮想デスクトップを検索
			for (int32 deviceIndex = 0; ::EnumDisplayDevicesW(0, deviceIndex, &displayDevice, 0); ++deviceIndex)
			{
				if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
				{
					DISPLAY_DEVICE monitor;
					ZeroMemory(&monitor, sizeof(monitor));
					monitor.cb = sizeof(monitor);

					// デスクトップとして使われているモニターの一覧を取得
					for (int32 monitorIndex = 0; ::EnumDisplayDevicesW(displayDevice.DeviceName, monitorIndex, &monitor, 0); ++monitorIndex)
					{
						if ((monitor.StateFlags & DISPLAY_DEVICE_ACTIVE) &&
							!(monitor.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
						{
							detail::MonitorCheck desc = { Unicode::FromWstring(displayDevice.DeviceName), nullptr };

							// モニターのハンドルを取得
							::EnumDisplayMonitors(nullptr, nullptr, detail::MonitorCheckProc, (LPARAM)&desc);

							if (desc.second == currentMonitor)
							{
								return index;
							}

							++index;
						}

						ZeroMemory(&monitor, sizeof(monitor));
						monitor.cb = sizeof(monitor);
					}
				}

				ZeroMemory(&displayDevice, sizeof(displayDevice));
				displayDevice.cb = sizeof(displayDevice);
			}

			return 0;
		}
	}
}
<commit_msg>[Windows] fix #719<commit_after>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2021 Ryo Suzuki
//	Copyright (c) 2016-2021 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <Siv3D/Monitor.hpp>
# include <Siv3D/Unicode.hpp>
# include <Siv3D/Window/IWindow.hpp>
# include <Siv3D/Common/Siv3DEngine.hpp>
# include <Siv3D/Windows/Windows.hpp>
# include <Siv3D/EngineLog.hpp>
# include <Siv3D/DLL.hpp>
# include <ShellScalingApi.h> // for GetDpiForMonitor()

namespace s3d
{
	Optional<decltype(GetDpiForMonitor)*> g_pGetDpiForMonitor;

	namespace detail
	{
		// チェック用デバイス名とモニタハンドル
		using MonitorCheck = std::pair<const String, HMONITOR>;

		static BOOL CALLBACK MonitorCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM userData)
		{
			if (not g_pGetDpiForMonitor.has_value())
			{
				g_pGetDpiForMonitor = nullptr;

				if (LibraryHandle shcore = DLL::LoadSystemLibraryNoThrow(L"Shcore.dll"))
				{
					decltype(GetDpiForMonitor)* pGetDpiForMonitor = DLL::GetFunctionNoThrow(shcore, "GetDpiForMonitor");
					*g_pGetDpiForMonitor = pGetDpiForMonitor;
				}
			}

			MONITORINFOEX monitorInfo{};
			monitorInfo.cbSize = sizeof(monitorInfo);
			::GetMonitorInfoW(hMonitor, &monitorInfo);

			MonitorInfo* monitor = reinterpret_cast<MonitorInfo*>(userData);

			if (monitor->displayDeviceName.toWstr() == monitorInfo.szDevice)
			{
				monitor->displayRect.x = monitorInfo.rcMonitor.left;
				monitor->displayRect.y = monitorInfo.rcMonitor.top;
				monitor->displayRect.w = (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
				monitor->displayRect.h = (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);

				monitor->workArea.x = monitorInfo.rcWork.left;
				monitor->workArea.y = monitorInfo.rcWork.top;
				monitor->workArea.w = (monitorInfo.rcWork.right - monitorInfo.rcWork.left);
				monitor->workArea.h = (monitorInfo.rcWork.bottom - monitorInfo.rcWork.top);

				monitor->fullscreenResolution = monitor->displayRect.size;

				if (*g_pGetDpiForMonitor)
				{
					uint32 dpiX = 0, dpiY = 0;
					if (SUCCEEDED((*g_pGetDpiForMonitor)(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
					{
						monitor->scaling = (dpiX / static_cast<double>(USER_DEFAULT_SCREEN_DPI));
					}
				}
				else
				{
					monitor->scaling = 1.0;
				}

				return false;
			}

			return true;
		}

		static MonitorInfo MakeMonitorInfo(const DISPLAY_DEVICEW& displayDevice, const DISPLAY_DEVICEW* monitor)
		{
			MonitorInfo monitorInfo;

			if (monitor)
			{
				monitorInfo.name = Unicode::FromWstring(monitor->DeviceString);
				monitorInfo.id = Unicode::FromWstring(monitor->DeviceID);
			}
			else
			{
				monitorInfo.name = Unicode::FromWstring(displayDevice.DeviceString);
				monitorInfo.id = Unicode::FromWstring(displayDevice.DeviceID);
			}

			monitorInfo.displayDeviceName	= Unicode::FromWstring(displayDevice.DeviceName);
			monitorInfo.isPrimary			= !!(displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);

			{
				const HDC hdc = ::CreateDCW(L"DISPLAY", displayDevice.DeviceName, nullptr, nullptr);
				Size sizeMillimeter{ ::GetDeviceCaps(hdc, HORZSIZE),  ::GetDeviceCaps(hdc, VERTSIZE) };
				::DeleteDC(hdc);
				monitorInfo.sizeMillimeter = sizeMillimeter;
			}

			if (DEVMODE devMode{ .dmSize = sizeof(DEVMODE) };
				::EnumDisplaySettingsW(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode))
			{
				monitorInfo.refreshRate = devMode.dmDisplayFrequency;
			}

			// モニターの配置とサイズを取得
			::EnumDisplayMonitors(nullptr, nullptr, MonitorCallback, (LPARAM)&monitorInfo);

			return monitorInfo;
		}

		static BOOL CALLBACK MonitorCheckProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM userData)
		{
			MONITORINFOEX monitorInfo{};
			monitorInfo.cbSize = sizeof(monitorInfo);
			::GetMonitorInfoW(hMonitor, &monitorInfo);

			MonitorCheck* monitor = (MonitorCheck*)userData;

			if (monitor->first.toWstr() == monitorInfo.szDevice)
			{
				monitor->second = hMonitor;
				return false;
			}

			return true;
		}

		static bool GetFriendlyName(Array<MonitorInfo>& monitors)
		{
			UINT32 nPaths, nModes;

			if (::GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &nPaths, &nModes) != ERROR_SUCCESS)
			{
				return false;
			}

			Array<DISPLAYCONFIG_PATH_INFO> displayPaths(nPaths);
			Array<DISPLAYCONFIG_MODE_INFO> displayModes(nModes);

			if (::QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &nPaths, displayPaths.data(), &nModes, displayModes.data(), nullptr) != ERROR_SUCCESS)
			{
				return false;
			}

			for (const auto& mode : displayModes)
			{
				if (mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
				{
					continue;
				}

				const DISPLAYCONFIG_DEVICE_INFO_HEADER deviceHeader
				{
					.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
					.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME),
					.adapterId = mode.adapterId,
					.id = mode.id,
				};

				DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName
				{
					.header = deviceHeader
				};

				if (::DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS)
				{
					continue;
				}

				{
					String friendlyName = Unicode::FromWstring(deviceName.monitorFriendlyDeviceName);

					if ((deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL)
						|| (deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED)
						|| (deviceName.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED))
					{
						friendlyName += U"(Internal Display)";
					}

					const String id = Unicode::FromWstring(deviceName.monitorDevicePath);

					for (auto& monitor : monitors)
					{
						if (monitor.id == id)
						{
							if (friendlyName)
							{
								monitor.name = std::move(friendlyName);
							}

							break;
						}
					}
				}
			}

			return true;
		}
	}

	namespace System
	{
		Array<MonitorInfo> EnumerateMonitors()
		{
			Array<MonitorInfo> monitors;
			DISPLAY_DEVICE displayDevice{ .cb = sizeof(DISPLAY_DEVICE) };

			// デスクトップとして割り当てられている仮想ディスプレイを検索
			for (int32 deviceIndex = 0; ::EnumDisplayDevicesW(0, deviceIndex, &displayDevice, 0); ++deviceIndex)
			{
				if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
				{
					DISPLAY_DEVICE monitor{ .cb = sizeof(DISPLAY_DEVICE) };

					// デスクトップとして使われているモニターの一覧を取得
					for (int32 monitorIndex = 0; ::EnumDisplayDevicesW(displayDevice.DeviceName, monitorIndex, &monitor, EDD_GET_DEVICE_INTERFACE_NAME); ++monitorIndex)
					{
						if ((monitor.StateFlags & DISPLAY_DEVICE_ACTIVE) &&
							not(monitor.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
						{

							monitors.push_back(detail::MakeMonitorInfo(displayDevice, &monitor));
						}

						ZeroMemory(&monitor, sizeof(monitor));
						monitor.cb = sizeof(monitor);
					}
				}

				ZeroMemory(&displayDevice, sizeof(displayDevice));
				displayDevice.cb = sizeof(displayDevice);
			}

			// no monitor is found
			if (monitors.empty())
			{
				for (int32 deviceIndex = 0; ::EnumDisplayDevicesW(0, deviceIndex, &displayDevice, 0); ++deviceIndex)
				{
					if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
					{
						monitors.push_back(detail::MakeMonitorInfo(displayDevice, nullptr));
						break;
					}
				}
			}

			detail::GetFriendlyName(monitors);

			return monitors;
		}

		size_t GetCurrentMonitorIndex()
		{
			const HMONITOR currentMonitor = ::MonitorFromWindow(static_cast<HWND>(SIV3D_ENGINE(Window)->getHandle()), MONITOR_DEFAULTTOPRIMARY);
			size_t index = 0;

			DISPLAY_DEVICE displayDevice =
			{
				.cb = sizeof(DISPLAY_DEVICE),
			};

			// デスクトップとして割り当てられている仮想デスクトップを検索
			for (int32 deviceIndex = 0; ::EnumDisplayDevicesW(0, deviceIndex, &displayDevice, 0); ++deviceIndex)
			{
				if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
				{
					DISPLAY_DEVICE monitor;
					ZeroMemory(&monitor, sizeof(monitor));
					monitor.cb = sizeof(monitor);

					// デスクトップとして使われているモニターの一覧を取得
					for (int32 monitorIndex = 0; ::EnumDisplayDevicesW(displayDevice.DeviceName, monitorIndex, &monitor, 0); ++monitorIndex)
					{
						if ((monitor.StateFlags & DISPLAY_DEVICE_ACTIVE) &&
							!(monitor.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
						{
							detail::MonitorCheck desc = { Unicode::FromWstring(displayDevice.DeviceName), nullptr };

							// モニターのハンドルを取得
							::EnumDisplayMonitors(nullptr, nullptr, detail::MonitorCheckProc, (LPARAM)&desc);

							if (desc.second == currentMonitor)
							{
								return index;
							}

							++index;
						}

						ZeroMemory(&monitor, sizeof(monitor));
						monitor.cb = sizeof(monitor);
					}
				}

				ZeroMemory(&displayDevice, sizeof(displayDevice));
				displayDevice.cb = sizeof(displayDevice);
			}

			return 0;
		}
	}
}
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2017 Axel Waggershauser

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "RawSpeed-API.h"

#include "io/Endianness.h" // for getHostEndianness, BSWAP16, Endianness::l...
#include "md5.h"           // for md5_hash
#include <chrono>          // for milliseconds, steady_clock, duration, dur...
#include <cstdint>         // for uint8_t
#include <cstdio>          // for snprintf, size_t, fclose, fopen, fprintf
#include <cstdlib>         // for system
#include <fstream>         // IWYU pragma: keep
#include <iomanip>         // for operator<<, setw
#include <iostream>        // for cout, cerr, left, internal
#include <map>             // for map
#include <memory>          // for unique_ptr, allocator
#include <sstream>         // IWYU pragma: keep
#include <stdexcept>       // for runtime_error
#include <string>          // for string, char_traits, operator+, operator<<
#include <type_traits>     // for enable_if<>::type
#include <utility>         // for pair
#include <vector>          // for vector
// IWYU pragma: no_include <ext/alloc_traits.h>

#ifdef _OPENMP
#include <omp.h>
#endif

// define this function, it is only declared in rawspeed:
#ifdef _OPENMP
int rawspeed_get_number_of_processor_cores() { return omp_get_num_procs(); }
#else
int __attribute__((const)) rawspeed_get_number_of_processor_cores() {
  return 1;
}
#endif

std::string img_hash(RawSpeed::RawImage& r);

void writePPM(const RawSpeed::RawImage& raw, const std::string& fn);

size_t process(const std::string& filename,
               const RawSpeed::CameraMetaData* metadata, bool create,
               bool dump);

using namespace std;
using namespace RawSpeed;

struct Timer {
  mutable chrono::steady_clock::time_point start = chrono::steady_clock::now();
  size_t operator()() const {
    auto ms = chrono::duration_cast<chrono::milliseconds>(
                  chrono::steady_clock::now() - start)
                  .count();
    start = chrono::steady_clock::now();
    return ms;
  }
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wframe-larger-than="
#pragma GCC diagnostic ignored "-Wstack-usage="

string img_hash(RawImage &r) {
  ostringstream oss;
  char line[1024];

#define APPEND(...)                                                            \
  do {                                                                         \
    snprintf(line, sizeof(line), __VA_ARGS__);                                 \
    oss << line;                                                               \
  } while (false)

  APPEND("make: %s\n", r->metadata.make.c_str());
  APPEND("model: %s\n", r->metadata.model.c_str());
  APPEND("mode: %s\n", r->metadata.mode.c_str());

  APPEND("canonical_make: %s\n", r->metadata.canonical_make.c_str());
  APPEND("canonical_model: %s\n", r->metadata.canonical_model.c_str());
  APPEND("canonical_alias: %s\n", r->metadata.canonical_alias.c_str());
  APPEND("canonical_id: %s\n", r->metadata.canonical_id.c_str());

  APPEND("isoSpeed: %d\n", r->metadata.isoSpeed);
  APPEND("blackLevel: %d\n", r->blackLevel);
  APPEND("whitePoint: %d\n", r->whitePoint);

  APPEND("blackLevelSeparate: %d %d %d %d\n", r->blackLevelSeparate[0],
         r->blackLevelSeparate[1], r->blackLevelSeparate[2],
         r->blackLevelSeparate[3]);

  APPEND("wbCoeffs: %f %f %f %f\n", r->metadata.wbCoeffs[0],
         r->metadata.wbCoeffs[1], r->metadata.wbCoeffs[2],
         r->metadata.wbCoeffs[3]);

  APPEND("isCFA: %d\n", r->isCFA);
  APPEND("cfa: %s\n", r->cfa.asString().c_str());
  APPEND("filters: 0x%x\n", r->cfa.getDcrawFilter());
  APPEND("bpp: %d\n", r->getBpp());
  APPEND("cpp: %d\n", r->getCpp());
  APPEND("dataType: %d\n", r->getDataType());

  const iPoint2D dimUncropped = r->getUncroppedDim();
  APPEND("dimUncropped: %dx%d\n", dimUncropped.x, dimUncropped.y);
  APPEND("dimCropped: %dx%d\n", r->dim.x, r->dim.y);
  const iPoint2D cropTL = r->getCropOffset();
  APPEND("cropOffset: %dx%d\n", cropTL.x, cropTL.y);

  // NOTE: pitch is internal property, a function of dimUncropped.x, bpp and
  // some additional padding overhead, to align each line lenght to be a
  // multiple of (currently) 16 bytes. And maybe with some additional
  // const offset. there is no point in showing it here, it may differ.
  // APPEND("pitch: %d\n", r->pitch);

  APPEND("blackAreas: ");
  for (auto ba : r->blackAreas)
    APPEND("%d:%dx%d, ", ba.isVertical, ba.offset, ba.size);
  APPEND("\n");

  APPEND("fuji_rotation_pos: %d\n", r->metadata.fujiRotationPos);
  APPEND("pixel_aspect_ratio: %f\n", r->metadata.pixelAspectRatio);

  APPEND("badPixelPositions: ");
  for (uint32 p : r->mBadPixelPositions)
    APPEND("%d, ", p);

  APPEND("\n");

  // yes, this is not cool. but i see no way to compute the hash of the
  // full image, without duplicating image, and copying excluding padding
  md5_state hash_of_line_hashes = md5_init;
  {
    vector<md5_state> line_hashes;
    line_hashes.resize(dimUncropped.y, md5_init);
    for (int j = 0; j < dimUncropped.y; j++) {
      auto* d = r->getDataUncropped(0, j);
      md5_hash(d, r->pitch - r->padding, line_hashes[j]);
    }
    md5_hash((const uint8_t*)&line_hashes[0],
             sizeof(line_hashes[0]) * line_hashes.size(), hash_of_line_hashes);
  }

  APPEND("md5sum of per-line md5sums: %s\n",
         hash_to_string(hash_of_line_hashes).c_str());

  for (const string& e : r->errors)
    APPEND("WARNING: [rawspeed] %s\n", e.c_str());

#undef APPEND

  return oss.str();
}

void writePPM(const RawImage& raw, const string& fn) {
  FILE *f = fopen(fn.c_str(), "wb");

  int width = raw->dim.x;
  int height = raw->dim.y;
  string format = raw->getCpp() == 1 ? "P5" : "P6";

  // Write PPM header
  fprintf(f, "%s\n%d %d\n65535\n", format.c_str(), width, height);

  width *= raw->getCpp();

  // Write pixels
  for (int y = 0; y < height; ++y) {
    auto *row = reinterpret_cast<unsigned short *>(raw->getData(0, y));
    // PPM is big-endian
    for (int x = 0; x < width; ++x)
      row[x] = getU16BE(row + x);

    fwrite(row, 2, width, f);
  }
  fclose(f);
}

size_t process(const string& filename, const CameraMetaData* metadata,
               bool create, bool dump) {

  const string hashfile(filename + ".hash");

  // if creating hash and hash exists -> skip current file
  // if not creating and hash is missing -> skip as well
  ifstream hf(hashfile);
  if (!(hf.good() ^ create)) {
#ifdef _OPENMP
#pragma omp critical(io)
#endif
    cout << left << setw(55) << filename << ": hash "
         << (create ? "exists" : "missing") << ", skipping" << endl;
    return 0;
  }

// to narrow down the list of files that could have causes the crash
#ifdef _OPENMP
#pragma omp critical(io)
#endif
  cout << left << setw(55) << filename << ": starting decoding ... " << endl;

  FileReader reader(filename.c_str());

  unique_ptr<Buffer> map = unique_ptr<Buffer>(reader.readFile());
  // Buffer* map = readFile( argv[1] );

  Timer t;

  RawParser parser(map.get());
  unique_ptr<RawDecoder> decoder =
      unique_ptr<RawDecoder>(parser.getDecoder(metadata));
  // RawDecoder* decoder = parseRaw( map );

  decoder->failOnUnknown = false;
  decoder->checkSupport(metadata);

  decoder->decodeRaw();
  decoder->decodeMetaData(metadata);
  RawImage raw = decoder->mRaw;
  // RawImage raw = decoder->decode();

  auto time = t();
#ifdef _OPENMP
#pragma omp critical(io)
#endif
  cout << left << setw(55) << filename << ": " << internal << setw(3)
       << map->getSize() / 1000000 << " MB / " << setw(4) << time << " ms"
       << endl;

  if (create) {
    ofstream f(hashfile);
    f << img_hash(raw);
    if (dump)
      writePPM(raw, filename + ".ppm");
  } else {
    string truth((istreambuf_iterator<char>(hf)), istreambuf_iterator<char>());
    string h = img_hash(raw);
    if (h != truth) {
      ofstream f(filename + ".hash.failed");
      f << h;
      if (dump)
        writePPM(raw, filename + ".failed.ppm");
      throw std::runtime_error("hash/metadata mismatch");
    }
  }

  return time;
}

#pragma GCC diagnostic pop

static int results(const map<string, string>& failedTests) {
  if (failedTests.empty()) {
    cout << "All good, no tests failed!" << endl;
    return 0;
  }

  cerr << "WARNING: the following " << failedTests.size()
       << " tests have failed:\n";

  for (const auto& i : failedTests) {
    cerr << i.second << "\n";
#ifndef WIN32
    const string oldhash(i.first + ".hash");
    const string newhash(oldhash + ".failed");

    ifstream oldfile(oldhash), newfile(newhash);

    // if neither hashes exist, nothing to append...
    if (!(oldfile.good() || newfile.good()))
      continue;

    // DIFF(1): -N, --new-file  treat absent files as empty
    string cmd(R"(diff -N -u0 ")");
    cmd += oldhash;
    cmd += R"(" ")";
    cmd += newhash;
    cmd += R"(" >> rstest.log)";
    if (system(cmd.c_str())) {
    }
  }
#endif

  cerr << "See rstest.log for details.\n";

  return 1;
}

static int usage(const char* progname) {
  cout << "usage: " << progname << R"(
  [-h] print this help
  [-c] for each file: decode, compute hash and store it.
       If hash exists, it does not recompute it!
  [-d] store decoded image as PPM
  <FILE[S]> the file[s] to work on.

  With no options given, each raw with an accompanying hash will be decoded
  and compared to the existing hash. A summary of all errors/failed hash
  comparisons will be reported at the end.

  Suggested workflow for easy regression testing:
    1. remove all .hash files and build 'trusted' version of this program
    2. run with option '-c' -> creates .hash for all supported files
    3. build new version to test for regressions
    4. run with no option   -> checks files with existing .hash
  If the second run shows no errors, you have no regressions,
  otherwise, the diff between hashes is appended to rstest.log
)";
  return 0;
}

int main(int argc, char **argv) {

  auto hasFlag = [&](string flag) {
    bool found = false;
    for (int i = 1; i < argc; ++i) {
      if (!argv[i] || argv[i] != flag)
        continue;
      found = true;
      argv[i] = nullptr;
    }
    return found;
  };

  bool help = hasFlag("-h");
  bool create = hasFlag("-c");
  bool dump = hasFlag("-d");

  if (1 == argc || help)
    return usage(argv[0]);

  const CameraMetaData metadata(CMAKE_SOURCE_DIR "/data/cameras.xml");

  size_t time = 0;
  map<string, string> failedTests;
#ifdef _OPENMP
#pragma omp parallel for default(shared) schedule(static, 1) reduction(+ : time)
#endif
  for (int i = 1; i < argc; ++i) {
    if (!argv[i])
      continue;

    try {
      time += process(argv[i], &metadata, create, dump);
    } catch (std::runtime_error &e) {
#ifdef _OPENMP
#pragma omp critical(io)
#endif
      {
        string msg = string(argv[i]) + " failed: " + e.what();
        cerr << msg << endl;
        failedTests.emplace(argv[i], msg);
      }
    }
  }

  cout << "Total decoding time: " << time / 1000.0 << "s" << endl << endl;

  return results(failedTests);
}
<commit_msg>rstest: hasFlag(): Explicitly capture the required scope variables.<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2017 Axel Waggershauser

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "RawSpeed-API.h"

#include "io/Endianness.h" // for getHostEndianness, BSWAP16, Endianness::l...
#include "md5.h"           // for md5_hash
#include <chrono>          // for milliseconds, steady_clock, duration, dur...
#include <cstdint>         // for uint8_t
#include <cstdio>          // for snprintf, size_t, fclose, fopen, fprintf
#include <cstdlib>         // for system
#include <fstream>         // IWYU pragma: keep
#include <iomanip>         // for operator<<, setw
#include <iostream>        // for cout, cerr, left, internal
#include <map>             // for map
#include <memory>          // for unique_ptr, allocator
#include <sstream>         // IWYU pragma: keep
#include <stdexcept>       // for runtime_error
#include <string>          // for string, char_traits, operator+, operator<<
#include <type_traits>     // for enable_if<>::type
#include <utility>         // for pair
#include <vector>          // for vector
// IWYU pragma: no_include <ext/alloc_traits.h>

#ifdef _OPENMP
#include <omp.h>
#endif

// define this function, it is only declared in rawspeed:
#ifdef _OPENMP
int rawspeed_get_number_of_processor_cores() { return omp_get_num_procs(); }
#else
int __attribute__((const)) rawspeed_get_number_of_processor_cores() {
  return 1;
}
#endif

std::string img_hash(RawSpeed::RawImage& r);

void writePPM(const RawSpeed::RawImage& raw, const std::string& fn);

size_t process(const std::string& filename,
               const RawSpeed::CameraMetaData* metadata, bool create,
               bool dump);

using namespace std;
using namespace RawSpeed;

struct Timer {
  mutable chrono::steady_clock::time_point start = chrono::steady_clock::now();
  size_t operator()() const {
    auto ms = chrono::duration_cast<chrono::milliseconds>(
                  chrono::steady_clock::now() - start)
                  .count();
    start = chrono::steady_clock::now();
    return ms;
  }
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wframe-larger-than="
#pragma GCC diagnostic ignored "-Wstack-usage="

string img_hash(RawImage &r) {
  ostringstream oss;
  char line[1024];

#define APPEND(...)                                                            \
  do {                                                                         \
    snprintf(line, sizeof(line), __VA_ARGS__);                                 \
    oss << line;                                                               \
  } while (false)

  APPEND("make: %s\n", r->metadata.make.c_str());
  APPEND("model: %s\n", r->metadata.model.c_str());
  APPEND("mode: %s\n", r->metadata.mode.c_str());

  APPEND("canonical_make: %s\n", r->metadata.canonical_make.c_str());
  APPEND("canonical_model: %s\n", r->metadata.canonical_model.c_str());
  APPEND("canonical_alias: %s\n", r->metadata.canonical_alias.c_str());
  APPEND("canonical_id: %s\n", r->metadata.canonical_id.c_str());

  APPEND("isoSpeed: %d\n", r->metadata.isoSpeed);
  APPEND("blackLevel: %d\n", r->blackLevel);
  APPEND("whitePoint: %d\n", r->whitePoint);

  APPEND("blackLevelSeparate: %d %d %d %d\n", r->blackLevelSeparate[0],
         r->blackLevelSeparate[1], r->blackLevelSeparate[2],
         r->blackLevelSeparate[3]);

  APPEND("wbCoeffs: %f %f %f %f\n", r->metadata.wbCoeffs[0],
         r->metadata.wbCoeffs[1], r->metadata.wbCoeffs[2],
         r->metadata.wbCoeffs[3]);

  APPEND("isCFA: %d\n", r->isCFA);
  APPEND("cfa: %s\n", r->cfa.asString().c_str());
  APPEND("filters: 0x%x\n", r->cfa.getDcrawFilter());
  APPEND("bpp: %d\n", r->getBpp());
  APPEND("cpp: %d\n", r->getCpp());
  APPEND("dataType: %d\n", r->getDataType());

  const iPoint2D dimUncropped = r->getUncroppedDim();
  APPEND("dimUncropped: %dx%d\n", dimUncropped.x, dimUncropped.y);
  APPEND("dimCropped: %dx%d\n", r->dim.x, r->dim.y);
  const iPoint2D cropTL = r->getCropOffset();
  APPEND("cropOffset: %dx%d\n", cropTL.x, cropTL.y);

  // NOTE: pitch is internal property, a function of dimUncropped.x, bpp and
  // some additional padding overhead, to align each line lenght to be a
  // multiple of (currently) 16 bytes. And maybe with some additional
  // const offset. there is no point in showing it here, it may differ.
  // APPEND("pitch: %d\n", r->pitch);

  APPEND("blackAreas: ");
  for (auto ba : r->blackAreas)
    APPEND("%d:%dx%d, ", ba.isVertical, ba.offset, ba.size);
  APPEND("\n");

  APPEND("fuji_rotation_pos: %d\n", r->metadata.fujiRotationPos);
  APPEND("pixel_aspect_ratio: %f\n", r->metadata.pixelAspectRatio);

  APPEND("badPixelPositions: ");
  for (uint32 p : r->mBadPixelPositions)
    APPEND("%d, ", p);

  APPEND("\n");

  // yes, this is not cool. but i see no way to compute the hash of the
  // full image, without duplicating image, and copying excluding padding
  md5_state hash_of_line_hashes = md5_init;
  {
    vector<md5_state> line_hashes;
    line_hashes.resize(dimUncropped.y, md5_init);
    for (int j = 0; j < dimUncropped.y; j++) {
      auto* d = r->getDataUncropped(0, j);
      md5_hash(d, r->pitch - r->padding, line_hashes[j]);
    }
    md5_hash((const uint8_t*)&line_hashes[0],
             sizeof(line_hashes[0]) * line_hashes.size(), hash_of_line_hashes);
  }

  APPEND("md5sum of per-line md5sums: %s\n",
         hash_to_string(hash_of_line_hashes).c_str());

  for (const string& e : r->errors)
    APPEND("WARNING: [rawspeed] %s\n", e.c_str());

#undef APPEND

  return oss.str();
}

void writePPM(const RawImage& raw, const string& fn) {
  FILE *f = fopen(fn.c_str(), "wb");

  int width = raw->dim.x;
  int height = raw->dim.y;
  string format = raw->getCpp() == 1 ? "P5" : "P6";

  // Write PPM header
  fprintf(f, "%s\n%d %d\n65535\n", format.c_str(), width, height);

  width *= raw->getCpp();

  // Write pixels
  for (int y = 0; y < height; ++y) {
    auto *row = reinterpret_cast<unsigned short *>(raw->getData(0, y));
    // PPM is big-endian
    for (int x = 0; x < width; ++x)
      row[x] = getU16BE(row + x);

    fwrite(row, 2, width, f);
  }
  fclose(f);
}

size_t process(const string& filename, const CameraMetaData* metadata,
               bool create, bool dump) {

  const string hashfile(filename + ".hash");

  // if creating hash and hash exists -> skip current file
  // if not creating and hash is missing -> skip as well
  ifstream hf(hashfile);
  if (!(hf.good() ^ create)) {
#ifdef _OPENMP
#pragma omp critical(io)
#endif
    cout << left << setw(55) << filename << ": hash "
         << (create ? "exists" : "missing") << ", skipping" << endl;
    return 0;
  }

// to narrow down the list of files that could have causes the crash
#ifdef _OPENMP
#pragma omp critical(io)
#endif
  cout << left << setw(55) << filename << ": starting decoding ... " << endl;

  FileReader reader(filename.c_str());

  unique_ptr<Buffer> map = unique_ptr<Buffer>(reader.readFile());
  // Buffer* map = readFile( argv[1] );

  Timer t;

  RawParser parser(map.get());
  unique_ptr<RawDecoder> decoder =
      unique_ptr<RawDecoder>(parser.getDecoder(metadata));
  // RawDecoder* decoder = parseRaw( map );

  decoder->failOnUnknown = false;
  decoder->checkSupport(metadata);

  decoder->decodeRaw();
  decoder->decodeMetaData(metadata);
  RawImage raw = decoder->mRaw;
  // RawImage raw = decoder->decode();

  auto time = t();
#ifdef _OPENMP
#pragma omp critical(io)
#endif
  cout << left << setw(55) << filename << ": " << internal << setw(3)
       << map->getSize() / 1000000 << " MB / " << setw(4) << time << " ms"
       << endl;

  if (create) {
    ofstream f(hashfile);
    f << img_hash(raw);
    if (dump)
      writePPM(raw, filename + ".ppm");
  } else {
    string truth((istreambuf_iterator<char>(hf)), istreambuf_iterator<char>());
    string h = img_hash(raw);
    if (h != truth) {
      ofstream f(filename + ".hash.failed");
      f << h;
      if (dump)
        writePPM(raw, filename + ".failed.ppm");
      throw std::runtime_error("hash/metadata mismatch");
    }
  }

  return time;
}

#pragma GCC diagnostic pop

static int results(const map<string, string>& failedTests) {
  if (failedTests.empty()) {
    cout << "All good, no tests failed!" << endl;
    return 0;
  }

  cerr << "WARNING: the following " << failedTests.size()
       << " tests have failed:\n";

  for (const auto& i : failedTests) {
    cerr << i.second << "\n";
#ifndef WIN32
    const string oldhash(i.first + ".hash");
    const string newhash(oldhash + ".failed");

    ifstream oldfile(oldhash), newfile(newhash);

    // if neither hashes exist, nothing to append...
    if (!(oldfile.good() || newfile.good()))
      continue;

    // DIFF(1): -N, --new-file  treat absent files as empty
    string cmd(R"(diff -N -u0 ")");
    cmd += oldhash;
    cmd += R"(" ")";
    cmd += newhash;
    cmd += R"(" >> rstest.log)";
    if (system(cmd.c_str())) {
    }
  }
#endif

  cerr << "See rstest.log for details.\n";

  return 1;
}

static int usage(const char* progname) {
  cout << "usage: " << progname << R"(
  [-h] print this help
  [-c] for each file: decode, compute hash and store it.
       If hash exists, it does not recompute it!
  [-d] store decoded image as PPM
  <FILE[S]> the file[s] to work on.

  With no options given, each raw with an accompanying hash will be decoded
  and compared to the existing hash. A summary of all errors/failed hash
  comparisons will be reported at the end.

  Suggested workflow for easy regression testing:
    1. remove all .hash files and build 'trusted' version of this program
    2. run with option '-c' -> creates .hash for all supported files
    3. build new version to test for regressions
    4. run with no option   -> checks files with existing .hash
  If the second run shows no errors, you have no regressions,
  otherwise, the diff between hashes is appended to rstest.log
)";
  return 0;
}

int main(int argc, char **argv) {

  auto hasFlag = [argc, argv](string flag) {
    bool found = false;
    for (int i = 1; i < argc; ++i) {
      if (!argv[i] || argv[i] != flag)
        continue;
      found = true;
      argv[i] = nullptr;
    }
    return found;
  };

  bool help = hasFlag("-h");
  bool create = hasFlag("-c");
  bool dump = hasFlag("-d");

  if (1 == argc || help)
    return usage(argv[0]);

  const CameraMetaData metadata(CMAKE_SOURCE_DIR "/data/cameras.xml");

  size_t time = 0;
  map<string, string> failedTests;
#ifdef _OPENMP
#pragma omp parallel for default(shared) schedule(static, 1) reduction(+ : time)
#endif
  for (int i = 1; i < argc; ++i) {
    if (!argv[i])
      continue;

    try {
      time += process(argv[i], &metadata, create, dump);
    } catch (std::runtime_error &e) {
#ifdef _OPENMP
#pragma omp critical(io)
#endif
      {
        string msg = string(argv[i]) + " failed: " + e.what();
        cerr << msg << endl;
        failedTests.emplace(argv[i], msg);
      }
    }
  }

  cout << "Total decoding time: " << time / 1000.0 << "s" << endl << endl;

  return results(failedTests);
}
<|endoftext|>


<commit_before>#include "MeshFilter.h"
#include "Director.h"
#include "ResourceManager.h"

using namespace Rendering::Mesh;
using namespace Resource;

MeshFilter::MeshFilter()
	:	_vertexBuffer(nullptr), _indexBuffer(nullptr), _alloc(false)
{
}

MeshFilter::~MeshFilter()
{
	if(_alloc == false)
		return;

	SAFE_DELETE(_vertexBuffer);
	SAFE_DELETE(_indexBuffer);
}

bool MeshFilter::Initialize(const CreateFuncArguments& args)
{
	ASSERT_COND_MSG(args.indices, "Error, Indices is null!");

	uint vertexCount	= args.vertices.count;
	uint indexCount		= args.indices->size();

	Manager::BufferManager* bufferMgr = ResourceManager::GetInstance()->GetBufferManager();

	std::string vbKey = args.fileName + ":" + args.key;

	// Vertex Buffer Setting
	{
		Buffer::VertexBuffer* vertexBuffer	= nullptr;
		if( bufferMgr->Find(&vertexBuffer, args.fileName, args.key) == false )
		{
			vertexBuffer = new Buffer::VertexBuffer;
			vertexBuffer->Initialize(args.vertices.data, args.vertices.byteWidth, vertexCount, args.useDynamicVB, vbKey, args.semanticInfos);

			bufferMgr->Add(args.fileName, args.key, vertexBuffer);
		}

		_vertexBuffer = vertexBuffer;
	}

	// Index Buffer Setting
	{
		Buffer::IndexBuffer* indexBuffer	= nullptr;
		if( bufferMgr->Find(&indexBuffer, args.fileName, args.key) == false )
		{
			indexBuffer = new Buffer::IndexBuffer;
			if( indexBuffer->Initialize(*args.indices, vbKey, args.useDynamicIB) == false )
				ASSERT_MSG("Error, can not create index buffer");

			bufferMgr->Add(args.fileName, args.key, indexBuffer);
		}

		_indexBuffer = indexBuffer;
	}

	_bufferFlag = args.semanticInfos ? ComputeBufferFlag(*args.semanticInfos) : 0;

	return true;
}

bool MeshFilter::Initialize(Rendering::Buffer::VertexBuffer*& vertexBuffer, Rendering::Buffer::IndexBuffer*& indexBuffer)
{
	_vertexBuffer	= vertexBuffer;
	_indexBuffer	= indexBuffer;

	_bufferFlag		= ComputeBufferFlag(_vertexBuffer->GetSemantics());

	return true;
}

uint MeshFilter::ComputeBufferFlag(
	const std::vector<Rendering::Buffer::VertexBuffer::SemanticInfo>& semantics) const
{
	return 0;
}<commit_msg>ComputeBufferFlag 구현 #88<commit_after>#include "MeshFilter.h"
#include "Director.h"
#include "ResourceManager.h"
#include "MeshImporter.h"

using namespace Rendering::Shader;
using namespace Rendering::Mesh;
using namespace Rendering::Manager;
using namespace Resource;

MeshFilter::MeshFilter()
	:	_vertexBuffer(nullptr), _indexBuffer(nullptr), _alloc(false)
{
}

MeshFilter::~MeshFilter()
{
	if(_alloc == false)
		return;

	SAFE_DELETE(_vertexBuffer);
	SAFE_DELETE(_indexBuffer);
}

bool MeshFilter::Initialize(const CreateFuncArguments& args)
{
	ASSERT_COND_MSG(args.indices, "Error, Indices is null!");

	uint vertexCount	= args.vertices.count;
	uint indexCount		= args.indices->size();

	Manager::BufferManager* bufferMgr = ResourceManager::GetInstance()->GetBufferManager();

	std::string vbKey = args.fileName + ":" + args.key;

	// Vertex Buffer Setting
	{
		Buffer::VertexBuffer* vertexBuffer	= nullptr;
		if( bufferMgr->Find(&vertexBuffer, args.fileName, args.key) == false )
		{
			vertexBuffer = new Buffer::VertexBuffer;
			vertexBuffer->Initialize(args.vertices.data, args.vertices.byteWidth, vertexCount, args.useDynamicVB, vbKey, args.semanticInfos);

			bufferMgr->Add(args.fileName, args.key, vertexBuffer);
		}

		_vertexBuffer = vertexBuffer;
	}

	// Index Buffer Setting
	{
		Buffer::IndexBuffer* indexBuffer	= nullptr;
		if( bufferMgr->Find(&indexBuffer, args.fileName, args.key) == false )
		{
			indexBuffer = new Buffer::IndexBuffer;
			if( indexBuffer->Initialize(*args.indices, vbKey, args.useDynamicIB) == false )
				ASSERT_MSG("Error, can not create index buffer");

			bufferMgr->Add(args.fileName, args.key, indexBuffer);
		}

		_indexBuffer = indexBuffer;
	}

	_bufferFlag = args.semanticInfos ? ComputeBufferFlag(*args.semanticInfos) : 0;

	return true;
}

bool MeshFilter::Initialize(Rendering::Buffer::VertexBuffer*& vertexBuffer, Rendering::Buffer::IndexBuffer*& indexBuffer)
{
	_vertexBuffer	= vertexBuffer;
	_indexBuffer	= indexBuffer;

	_bufferFlag		= ComputeBufferFlag(_vertexBuffer->GetSemantics());

	return true;
}

uint MeshFilter::ComputeBufferFlag(
	const std::vector<VertexShader::SemanticInfo>& semantics) const
{
	uint flag = 0;
	for(auto iter = semantics.begin(); iter != semantics.end(); ++iter)
	{
		const auto& semantic = *iter;

		if(		semantic.name == "POSITION")		continue;
		else if(semantic.name == "TEXCOORD")
		{
			if(semantic.semanticIndex > 1)
				flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::USERS;
			else
				flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::UV0 << semantic.semanticIndex;
		}
		else if(semantic.name == "NORMAL")
			flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::NORMAL;
		else if(semantic.name == "TANGENT")
			flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::TANGENT;
		else if(semantic.name == "COLOR")
			flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::COLOR;
		else if(semantic.name == "BONEWEIGHT")
		{
			if(semantic.semanticIndex+1 >= Importer::MeshImporter::MaximumRecognizeBoneCount)
				flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::BONE << semantic.semanticIndex;
			else
				flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::USERS;
		}
		else
		{
			flag |= (uint)RenderManager::DefaultVertexInputTypeFlag::USERS;
		}
	}

	if(flag & (uint)RenderManager::DefaultVertexInputTypeFlag::USERS)
	{
		DEBUG_LOG("Warning, You use undefined semantic in RenderManager::DefaultVertexInputTypeFlag.");
	}

	return flag;
}<|endoftext|>


<commit_before>/* Brainfuck interpreter using interpreter and composite patterns :) */

#include <exception>
#include <iostream>
#include <list>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <memory>

using namespace std;

struct Data
{
    vector<int> array;
    int ptr;
};

class AbstractExpression
{
    public:
        AbstractExpression() {}
        virtual ~AbstractExpression() {}
        virtual void add(shared_ptr<AbstractExpression>) {}
        virtual bool isComposite() {return false;}
        virtual void interpret(Data &) = 0;
        virtual void parse(const string &) {}
};

class IncrementByte: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.array[data.ptr]++;
        }
};

class DecrementByte: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.array[data.ptr]--;
        }
};

class IncrementPtr: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.ptr++;
            if(data.array.size()==data.ptr) data.array.push_back(0);
        }
};

class DecrementPtr: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.ptr--;
            if(data.ptr<0) throw out_of_range("Negative value of pointer");
        }
};

class Output: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            cout<<char(data.array[data.ptr]);
        }
};

class Input: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            char input;
            cin>>input;
            data.array[data.ptr] = static_cast<char>(input);
        }
};

typedef shared_ptr<AbstractExpression> AbstractExpressionPtr;

class CompositeExpression: public AbstractExpression
{
    private:
        list<AbstractExpressionPtr> expTree;
    public:
        CompositeExpression(): expTree() {}

        virtual ~CompositeExpression() {}

        virtual bool isComposite() {return true;}

        virtual void add(AbstractExpressionPtr exp) {expTree.push_back(exp);}

        virtual void interpret(Data &data) {
            for(list<AbstractExpressionPtr>::iterator it=expTree.begin(); it!=expTree.end(); it++) {
                if((*it)->isComposite()) {
                    while(data.array[data.ptr])
                        (*it)->interpret(data);
                }
                else {
                    (*it)->interpret(data);
                }
            }
        }
};

class Parser
{
    private:
        map<char, AbstractExpressionPtr> expMap;
    public:
        Parser() {
            expMap['+'] = AbstractExpressionPtr(new IncrementByte);
            expMap['-'] = AbstractExpressionPtr(new DecrementByte);
            expMap['>'] = AbstractExpressionPtr(new IncrementPtr);
            expMap['<'] = AbstractExpressionPtr(new DecrementPtr);
            expMap['.'] = AbstractExpressionPtr(new Output);
            expMap[','] = AbstractExpressionPtr(new Input);
        }

        AbstractExpressionPtr buildTree(const string & code) {
            AbstractExpressionPtr syntaxTreePtr(new CompositeExpression);
            int skip(0);
            for(int i=0; i<code.size(); i++) {
                if(skip) {
                    if(code[i] == '[') skip++;
                    if(code[i] == ']') skip--;
                    continue;
                }
                if(expMap.find(code[i]) != expMap.end()) {
                    syntaxTreePtr->add(expMap[code[i]]);
                }
                else if (code[i] == '[') {
                    AbstractExpressionPtr exp = buildTree(code.substr(i+1));
                    syntaxTreePtr->add(exp);
                    skip = 1;
                }
                else if(code[i]==']') break;
            }
            return syntaxTreePtr;
        }
};

int main()
{
    Data data; data.array.assign(1,0); data.ptr = 0;
    string code("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.");
    //string code(",[.[-],]");
    Parser parser;
    AbstractExpressionPtr syntaxTreePtr = parser.buildTree(code);
    syntaxTreePtr->interpret(data);
}
<commit_msg>Implement specific interpret methods for CompositeExpression and Loop<commit_after>/* Brainfuck interpreter using interpreter and composite patterns :) */

#include <exception>
#include <iostream>
#include <list>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <memory>

using namespace std;

struct Data
{
    vector<int> array;
    int ptr;
};

class AbstractExpression
{
    public:
        AbstractExpression() {}
        virtual ~AbstractExpression() {}
        virtual void add(shared_ptr<AbstractExpression>) {}
        virtual void interpret(Data &) = 0;
};

class IncrementByte: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.array[data.ptr]++;
        }
};

class DecrementByte: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.array[data.ptr]--;
        }
};

class IncrementPtr: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.ptr++;
            if(data.array.size()==data.ptr) data.array.push_back(0);
        }
};

class DecrementPtr: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            data.ptr--;
            if(data.ptr<0) throw out_of_range("Negative value of pointer");
        }
};

class Output: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            cout<<char(data.array[data.ptr]);
        }
};

class Input: public AbstractExpression
{
    public:
        virtual void interpret(Data &data) {
            char input;
            cin>>input;
            data.array[data.ptr] = static_cast<char>(input);
        }
};

typedef shared_ptr<AbstractExpression> AbstractExpressionPtr;

class CompositeExpression: public AbstractExpression
{
    protected:
        list<AbstractExpressionPtr> expTree;
    public:
        CompositeExpression(): expTree() {}

        virtual ~CompositeExpression() {}

        virtual void add(AbstractExpressionPtr exp) {expTree.push_back(exp);}

        virtual void interpret(Data &data) {
            for(list<AbstractExpressionPtr>::iterator it=expTree.begin(); it!=expTree.end(); it++)
                (*it)->interpret(data);
        }
};

class Loop: public CompositeExpression
{
    public:
        virtual void interpret(Data &data) {
            while(data.array[data.ptr]) {
                for(list<AbstractExpressionPtr>::iterator it=expTree.begin(); it!=expTree.end(); it++)
                    (*it)->interpret(data);
            }
        }
};

class Parser
{
    private:
        map<char, AbstractExpressionPtr> expMap;
    public:
        Parser() {
            expMap['+'] = AbstractExpressionPtr(new IncrementByte);
            expMap['-'] = AbstractExpressionPtr(new DecrementByte);
            expMap['>'] = AbstractExpressionPtr(new IncrementPtr);
            expMap['<'] = AbstractExpressionPtr(new DecrementPtr);
            expMap['.'] = AbstractExpressionPtr(new Output);
            expMap[','] = AbstractExpressionPtr(new Input);
        }

        AbstractExpressionPtr buildTree(const string & code, bool loop) {
            AbstractExpressionPtr syntaxTreePtr;
            if(loop) {syntaxTreePtr.reset(new Loop);}
            else {syntaxTreePtr.reset(new CompositeExpression);}
            int skip(0);
            for(int i=0; i<code.size(); i++) {
                if(skip) {
                    if(code[i] == '[') skip++;
                    if(code[i] == ']') skip--;
                    continue;
                }
                if(expMap.find(code[i]) != expMap.end()) {
                    syntaxTreePtr->add(expMap[code[i]]);
                }
                else if (code[i] == '[') {
                    AbstractExpressionPtr exp = buildTree(code.substr(i+1),true);
                    syntaxTreePtr->add(exp);
                    skip = 1;
                }
                else if(code[i]==']') break;
            }
            return syntaxTreePtr;
        }
};

int main()
{
    Data data; data.array.assign(1,0); data.ptr = 0;
    string code("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.");
    //string code(",[.[-],]");
    Parser parser;
    AbstractExpressionPtr syntaxTreePtr = parser.buildTree(code,false);
    syntaxTreePtr->interpret(data);
}
<|endoftext|>