<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXDE-Qt - a lightweight, Qt based, desktop toolset
 * http://lxqt.org
 *
 * Copyright: 2015 LXQt team
 * Authors:
 *  Balázs Béla <balazsbela[at]gmail.com>
 *  Paulo Lieuthier <paulolieuthier@gmail.com>
 *
 * This program or 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.1 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
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "statusnotifierbutton.h"

#include <QDir>
#include <QFile>
#include <dbusmenu-qt5/dbusmenuimporter.h>
#include "../panel/ilxqtpanelplugin.h"
#include "sniasync.h"

StatusNotifierButton::StatusNotifierButton(QString service, QString objectPath, ILXQtPanelPlugin* plugin, QWidget *parent)
    : QToolButton(parent),
    mMenu(nullptr),
    mStatus(Passive),
    mFallbackIcon(QIcon::fromTheme("application-x-executable")),
    mPlugin(plugin)
{
    interface = new SniAsync(service, objectPath, QDBusConnection::sessionBus(), this);

    connect(interface, &SniAsync::NewIcon, this, &StatusNotifierButton::newIcon);
    connect(interface, &SniAsync::NewOverlayIcon, this, &StatusNotifierButton::newOverlayIcon);
    connect(interface, &SniAsync::NewAttentionIcon, this, &StatusNotifierButton::newAttentionIcon);
    connect(interface, &SniAsync::NewToolTip, this, &StatusNotifierButton::newToolTip);
    connect(interface, &SniAsync::NewStatus, this, &StatusNotifierButton::newStatus);

    interface->propertyGetAsync(QLatin1String("Menu"), [this] (QDBusObjectPath path) {
        if (!path.path().isEmpty())
        {
            mMenu = (new DBusMenuImporter(interface->service(), path.path(), this))->menu();
            dynamic_cast<QObject &>(*mMenu).setParent(this);
            mMenu->setObjectName(QLatin1String("StatusNotifierMenu"));
        }
    });

    interface->propertyGetAsync(QLatin1String("Status"), [this] (QString status) {
        newStatus(status);
    });

    interface->propertyGetAsync(QLatin1String("IconThemePath"), [this] (QString value) {
        mThemePath = value;
        //do the logic of icons after we've got the theme path
        refetchIcon(Active);
        refetchIcon(Passive);
        refetchIcon(NeedsAttention);
    });

    newToolTip();
}

StatusNotifierButton::~StatusNotifierButton()
{
    delete interface;
}

void StatusNotifierButton::newIcon()
{
    refetchIcon(Passive);
}

void StatusNotifierButton::newOverlayIcon()
{
    refetchIcon(Active);
}

void StatusNotifierButton::newAttentionIcon()
{
    refetchIcon(NeedsAttention);
}

void StatusNotifierButton::refetchIcon(Status status)
{
    QString nameProperty, pixmapProperty;
    if (status == Active)
    {
        nameProperty = QLatin1String("OverlayIconName");
        pixmapProperty = QLatin1String("OverlayIconPixmap");
    }
    else if (status == NeedsAttention)
    {
        nameProperty = QLatin1String("AttentionIconName");
        pixmapProperty = QLatin1String("AttentionIconPixmap");
    }
    else // status == Passive
    {
        nameProperty = QLatin1String("IconName");
        pixmapProperty = QLatin1String("IconPixmap");
    }

    interface->propertyGetAsync(nameProperty, [this, status, pixmapProperty] (QString iconName) {
        QIcon nextIcon;
        if (!iconName.isEmpty())
        {
            if (QIcon::hasThemeIcon(iconName))
                nextIcon = QIcon::fromTheme(iconName);
            else
            {
                QDir themeDir(mThemePath);
                if (themeDir.exists())
                {
                    if (themeDir.exists(iconName + ".png"))
                        nextIcon.addFile(themeDir.filePath(iconName + ".png"));

                    if (themeDir.cd("hicolor") || (themeDir.cd("icons") && themeDir.cd("hicolor")))
                    {
                        QStringList sizes = themeDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                        foreach (QString dir, sizes)
                        {
                            QStringList dirs = QDir(themeDir.filePath(dir)).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                            foreach (QString innerDir, dirs)
                            {
                                QString file = themeDir.absolutePath() + "/" + dir + "/" + innerDir + "/" + iconName + ".png";
                                if (QFile::exists(file))
                                    nextIcon.addFile(file);
                            }
                        }
                    }
                }
            }

            switch (status)
            {
                case Active:
                    mOverlayIcon = nextIcon;
                    break;
                case NeedsAttention:
                    mAttentionIcon = nextIcon;
                    break;
                case Passive:
                    mIcon = nextIcon;
                    break;
            }

            resetIcon();
        }
        else
        {
            interface->propertyGetAsync(pixmapProperty, [this, status, pixmapProperty] (IconPixmapList iconPixmaps) {
                if (iconPixmaps.empty())
                    return;

                QIcon nextIcon;

                for (IconPixmap iconPixmap: iconPixmaps)
                {
                    if (!iconPixmap.bytes.isNull())
                    {
                        QImage image((uchar*) iconPixmap.bytes.data(), iconPixmap.width,
                                     iconPixmap.height, QImage::Format_ARGB32);

                        const uchar *end = image.constBits() + image.byteCount();
                        uchar *dest = reinterpret_cast<uchar*>(iconPixmap.bytes.data());
                        for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
                            qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest);

                        nextIcon.addPixmap(QPixmap::fromImage(image));
                    }
                }

                switch (status)
                {
                    case Active:
                        mOverlayIcon = nextIcon;
                        break;
                    case NeedsAttention:
                        mAttentionIcon = nextIcon;
                        break;
                    case Passive:
                        mIcon = nextIcon;
                        break;
                }

                resetIcon();
            });
        }
    });
}

void StatusNotifierButton::newToolTip()
{
    interface->propertyGetAsync(QLatin1String("ToolTip"), [this] (ToolTip tooltip) {
        QString toolTipTitle = tooltip.title;
        if (!toolTipTitle.isEmpty())
            setToolTip(toolTipTitle);
        else
            interface->propertyGetAsync(QLatin1String("Title"), [this] (QString title) {
                // we should get here only in case the ToolTip.title was empty
                if (!title.isEmpty())
                    setToolTip(title);
            });
    });
}

void StatusNotifierButton::newStatus(QString status)
{
    Status newStatus;
    if (status == QLatin1String("Passive"))
        newStatus = Passive;
    else if (status == QLatin1String("Active"))
        newStatus = Active;
    else
        newStatus = NeedsAttention;

    if (mStatus == newStatus)
        return;

    mStatus = newStatus;
    resetIcon();
}

void StatusNotifierButton::contextMenuEvent(QContextMenuEvent* event)
{
    //XXX: avoid showing of parent's context menu, we are (optionaly) providing context menu on mouseReleaseEvent
    //QWidget::contextMenuEvent(event);
}

void StatusNotifierButton::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        interface->Activate(QCursor::pos().x(), QCursor::pos().y());
    else if (event->button() == Qt::MidButton)
        interface->SecondaryActivate(QCursor::pos().x(), QCursor::pos().y());
    else if (Qt::RightButton == event->button())
    {
        if (mMenu)
        {
            mPlugin->willShowWindow(mMenu);
            mMenu->popup(mPlugin->calculatePopupWindowPos(mMenu->sizeHint()).topLeft());
        } else
            interface->ContextMenu(QCursor::pos().x(), QCursor::pos().y());
    }

    QToolButton::mouseReleaseEvent(event);
}

void StatusNotifierButton::wheelEvent(QWheelEvent *event)
{
    interface->Scroll(event->delta(), "vertical");
}

void StatusNotifierButton::resetIcon()
{
    if (mStatus == Active && !mOverlayIcon.isNull())
        setIcon(mOverlayIcon);
    else if (mStatus == NeedsAttention && !mAttentionIcon.isNull())
        setIcon(mAttentionIcon);
    else if (!mIcon.isNull()) // mStatus == Passive
        setIcon(mIcon);
    else if (!mOverlayIcon.isNull())
        setIcon(mOverlayIcon);
    else if (!mAttentionIcon.isNull())
        setIcon(mAttentionIcon);
    else
        setIcon(mFallbackIcon);
}
<commit_msg>statusnotifier: fix position of context menus<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXDE-Qt - a lightweight, Qt based, desktop toolset
 * http://lxqt.org
 *
 * Copyright: 2015 LXQt team
 * Authors:
 *  Balázs Béla <balazsbela[at]gmail.com>
 *  Paulo Lieuthier <paulolieuthier@gmail.com>
 *
 * This program or 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.1 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
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "statusnotifierbutton.h"

#include <QDir>
#include <QFile>
#include <dbusmenu-qt5/dbusmenuimporter.h>
#include "../panel/ilxqtpanelplugin.h"
#include "sniasync.h"

StatusNotifierButton::StatusNotifierButton(QString service, QString objectPath, ILXQtPanelPlugin* plugin, QWidget *parent)
    : QToolButton(parent),
    mMenu(nullptr),
    mStatus(Passive),
    mFallbackIcon(QIcon::fromTheme("application-x-executable")),
    mPlugin(plugin)
{
    interface = new SniAsync(service, objectPath, QDBusConnection::sessionBus(), this);

    connect(interface, &SniAsync::NewIcon, this, &StatusNotifierButton::newIcon);
    connect(interface, &SniAsync::NewOverlayIcon, this, &StatusNotifierButton::newOverlayIcon);
    connect(interface, &SniAsync::NewAttentionIcon, this, &StatusNotifierButton::newAttentionIcon);
    connect(interface, &SniAsync::NewToolTip, this, &StatusNotifierButton::newToolTip);
    connect(interface, &SniAsync::NewStatus, this, &StatusNotifierButton::newStatus);

    interface->propertyGetAsync(QLatin1String("Menu"), [this] (QDBusObjectPath path) {
        if (!path.path().isEmpty())
        {
            mMenu = (new DBusMenuImporter(interface->service(), path.path(), this))->menu();
            dynamic_cast<QObject &>(*mMenu).setParent(this);
            mMenu->setObjectName(QLatin1String("StatusNotifierMenu"));
        }
    });

    interface->propertyGetAsync(QLatin1String("Status"), [this] (QString status) {
        newStatus(status);
    });

    interface->propertyGetAsync(QLatin1String("IconThemePath"), [this] (QString value) {
        mThemePath = value;
        //do the logic of icons after we've got the theme path
        refetchIcon(Active);
        refetchIcon(Passive);
        refetchIcon(NeedsAttention);
    });

    newToolTip();
}

StatusNotifierButton::~StatusNotifierButton()
{
    delete interface;
}

void StatusNotifierButton::newIcon()
{
    refetchIcon(Passive);
}

void StatusNotifierButton::newOverlayIcon()
{
    refetchIcon(Active);
}

void StatusNotifierButton::newAttentionIcon()
{
    refetchIcon(NeedsAttention);
}

void StatusNotifierButton::refetchIcon(Status status)
{
    QString nameProperty, pixmapProperty;
    if (status == Active)
    {
        nameProperty = QLatin1String("OverlayIconName");
        pixmapProperty = QLatin1String("OverlayIconPixmap");
    }
    else if (status == NeedsAttention)
    {
        nameProperty = QLatin1String("AttentionIconName");
        pixmapProperty = QLatin1String("AttentionIconPixmap");
    }
    else // status == Passive
    {
        nameProperty = QLatin1String("IconName");
        pixmapProperty = QLatin1String("IconPixmap");
    }

    interface->propertyGetAsync(nameProperty, [this, status, pixmapProperty] (QString iconName) {
        QIcon nextIcon;
        if (!iconName.isEmpty())
        {
            if (QIcon::hasThemeIcon(iconName))
                nextIcon = QIcon::fromTheme(iconName);
            else
            {
                QDir themeDir(mThemePath);
                if (themeDir.exists())
                {
                    if (themeDir.exists(iconName + ".png"))
                        nextIcon.addFile(themeDir.filePath(iconName + ".png"));

                    if (themeDir.cd("hicolor") || (themeDir.cd("icons") && themeDir.cd("hicolor")))
                    {
                        QStringList sizes = themeDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                        foreach (QString dir, sizes)
                        {
                            QStringList dirs = QDir(themeDir.filePath(dir)).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                            foreach (QString innerDir, dirs)
                            {
                                QString file = themeDir.absolutePath() + "/" + dir + "/" + innerDir + "/" + iconName + ".png";
                                if (QFile::exists(file))
                                    nextIcon.addFile(file);
                            }
                        }
                    }
                }
            }

            switch (status)
            {
                case Active:
                    mOverlayIcon = nextIcon;
                    break;
                case NeedsAttention:
                    mAttentionIcon = nextIcon;
                    break;
                case Passive:
                    mIcon = nextIcon;
                    break;
            }

            resetIcon();
        }
        else
        {
            interface->propertyGetAsync(pixmapProperty, [this, status, pixmapProperty] (IconPixmapList iconPixmaps) {
                if (iconPixmaps.empty())
                    return;

                QIcon nextIcon;

                for (IconPixmap iconPixmap: iconPixmaps)
                {
                    if (!iconPixmap.bytes.isNull())
                    {
                        QImage image((uchar*) iconPixmap.bytes.data(), iconPixmap.width,
                                     iconPixmap.height, QImage::Format_ARGB32);

                        const uchar *end = image.constBits() + image.byteCount();
                        uchar *dest = reinterpret_cast<uchar*>(iconPixmap.bytes.data());
                        for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
                            qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest);

                        nextIcon.addPixmap(QPixmap::fromImage(image));
                    }
                }

                switch (status)
                {
                    case Active:
                        mOverlayIcon = nextIcon;
                        break;
                    case NeedsAttention:
                        mAttentionIcon = nextIcon;
                        break;
                    case Passive:
                        mIcon = nextIcon;
                        break;
                }

                resetIcon();
            });
        }
    });
}

void StatusNotifierButton::newToolTip()
{
    interface->propertyGetAsync(QLatin1String("ToolTip"), [this] (ToolTip tooltip) {
        QString toolTipTitle = tooltip.title;
        if (!toolTipTitle.isEmpty())
            setToolTip(toolTipTitle);
        else
            interface->propertyGetAsync(QLatin1String("Title"), [this] (QString title) {
                // we should get here only in case the ToolTip.title was empty
                if (!title.isEmpty())
                    setToolTip(title);
            });
    });
}

void StatusNotifierButton::newStatus(QString status)
{
    Status newStatus;
    if (status == QLatin1String("Passive"))
        newStatus = Passive;
    else if (status == QLatin1String("Active"))
        newStatus = Active;
    else
        newStatus = NeedsAttention;

    if (mStatus == newStatus)
        return;

    mStatus = newStatus;
    resetIcon();
}

void StatusNotifierButton::contextMenuEvent(QContextMenuEvent* event)
{
    //XXX: avoid showing of parent's context menu, we are (optionaly) providing context menu on mouseReleaseEvent
    //QWidget::contextMenuEvent(event);
}

void StatusNotifierButton::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        interface->Activate(QCursor::pos().x(), QCursor::pos().y());
    else if (event->button() == Qt::MidButton)
        interface->SecondaryActivate(QCursor::pos().x(), QCursor::pos().y());
    else if (Qt::RightButton == event->button())
    {
        if (mMenu)
        {
            mPlugin->willShowWindow(mMenu);
            mMenu->popup(QCursor::pos());
        } else
            interface->ContextMenu(QCursor::pos().x(), QCursor::pos().y());
    }

    QToolButton::mouseReleaseEvent(event);
}

void StatusNotifierButton::wheelEvent(QWheelEvent *event)
{
    interface->Scroll(event->delta(), "vertical");
}

void StatusNotifierButton::resetIcon()
{
    if (mStatus == Active && !mOverlayIcon.isNull())
        setIcon(mOverlayIcon);
    else if (mStatus == NeedsAttention && !mAttentionIcon.isNull())
        setIcon(mAttentionIcon);
    else if (!mIcon.isNull()) // mStatus == Passive
        setIcon(mIcon);
    else if (!mOverlayIcon.isNull())
        setIcon(mOverlayIcon);
    else if (!mAttentionIcon.isNull())
        setIcon(mAttentionIcon);
    else
        setIcon(mFallbackIcon);
}
<|endoftext|>


<commit_before>// Stolen from libLAS' endian.hpp
/******************************************************************************
 * $Id$
 *
 * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
 * Purpose:  Endian macros
 * Author:   Mateusz Loskot, mateusz@loskot.net
 *
 * This file has been stolen from <boost/endian.hpp> and
 * modified for libLAS purposes.
 *
 * (C) Copyright Mateusz Loskot 2007, mateusz@loskot.net
 * (C) Copyright Caleb Epstein 2005
 * (C) Copyright John Maddock 2006
 * Distributed under the Boost  Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * Revision History
 * 06 Feb 2006 - Initial Revision
 * 09 Nov 2006 - fixed variant and version bits for v4 guids
 * 13 Nov 2006 - added serialization
 * 17 Nov 2006 - added name-based guid creation
 * 20 Nov 2006 - add fixes for gcc (from Tim Blechmann)
 * 07 Mar 2007 - converted to header only
 * 20 Jan 2008 - removed dependency of Boost and modified for libLAS (by Mateusz Loskot)
 ******************************************************************************
 *
 * Copyright (c) 1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 *
 *
 * Copyright notice reproduced from <boost/detail/limits.hpp>, from
 * which this code was originally taken.
 *
 * Modified by Caleb Epstein to use <endian.h> with GNU libc and to
 * defined the BOOST_ENDIAN macro.
 ****************************************************************************/
#ifndef INCLUDED_DRIVER_OCI_ENDIAN_HPP
#define INCLUDED_DRIVER_OCI_ENDIAN_HPP

# define SWAP_BE_TO_LE(p) \
    do { \
        char* first = static_cast<char*>(static_cast<void*>(&p)); \
        char* last = first + sizeof(p) - 1; \
        for(; first < last; ++first, --last) { \
            char const x = *last; \
            *last = *first; \
            *first = x; \
        }} while(false)

# define SWAP_BE_TO_LE_N(p, n) \
    do { \
        char* first = static_cast<char*>(static_cast<void*>(&p)); \
        char* last = first + n - 1; \
        for(; first < last; ++first, --last) { \
            char const x = *last; \
            *last = *first; \
            *first = x; \
        }} while(false)

#endif<commit_msg>newline at end of file<commit_after>// Stolen from libLAS' endian.hpp
/******************************************************************************
 * $Id$
 *
 * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
 * Purpose:  Endian macros
 * Author:   Mateusz Loskot, mateusz@loskot.net
 *
 * This file has been stolen from <boost/endian.hpp> and
 * modified for libLAS purposes.
 *
 * (C) Copyright Mateusz Loskot 2007, mateusz@loskot.net
 * (C) Copyright Caleb Epstein 2005
 * (C) Copyright John Maddock 2006
 * Distributed under the Boost  Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * Revision History
 * 06 Feb 2006 - Initial Revision
 * 09 Nov 2006 - fixed variant and version bits for v4 guids
 * 13 Nov 2006 - added serialization
 * 17 Nov 2006 - added name-based guid creation
 * 20 Nov 2006 - add fixes for gcc (from Tim Blechmann)
 * 07 Mar 2007 - converted to header only
 * 20 Jan 2008 - removed dependency of Boost and modified for libLAS (by Mateusz Loskot)
 ******************************************************************************
 *
 * Copyright (c) 1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 *
 *
 * Copyright notice reproduced from <boost/detail/limits.hpp>, from
 * which this code was originally taken.
 *
 * Modified by Caleb Epstein to use <endian.h> with GNU libc and to
 * defined the BOOST_ENDIAN macro.
 ****************************************************************************/
#ifndef INCLUDED_DRIVER_OCI_ENDIAN_HPP
#define INCLUDED_DRIVER_OCI_ENDIAN_HPP

# define SWAP_BE_TO_LE(p) \
    do { \
        char* first = static_cast<char*>(static_cast<void*>(&p)); \
        char* last = first + sizeof(p) - 1; \
        for(; first < last; ++first, --last) { \
            char const x = *last; \
            *last = *first; \
            *first = x; \
        }} while(false)

# define SWAP_BE_TO_LE_N(p, n) \
    do { \
        char* first = static_cast<char*>(static_cast<void*>(&p)); \
        char* last = first + n - 1; \
        for(; first < last; ++first, --last) { \
            char const x = *last; \
            *last = *first; \
            *first = x; \
        }} while(false)

#endif
<|endoftext|>


<commit_before>/*
  Copyright (C) 2007 by Marten Svanfeldt
                2004 by Anders Stenberg, Daniel Duhprey

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include "csgfx/imagemanipulate.h"
#include "csgfx/rgbpixel.h"
#include "csutil/csendian.h"
#include "csutil/objreg.h"
#include "imesh/terrain2.h"
#include "iutil/vfs.h"
#include "imap/loader.h"

#include "feederhelper.h"

CS_PLUGIN_NAMESPACE_BEGIN(Terrain2)
{

  template<typename Endianness>
  struct GetterFloat
  {
    static inline void Get (char*& buf, float&f)
    {
      f = csIEEEfloat::ToNative (Endianness::Convert (
	    csGetFromAddress::UInt32 (buf)));
      buf += sizeof(uint32);
    }
    static inline size_t ItemSize()
    { return sizeof(uint32); }
  };

  struct GetterUint8
  {
    static inline void Get (char*& buf, float&f)
    {
      uint8 v = (uint8) (*buf);
      buf += sizeof (uint8);
      f = float(v) / 255.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint8); }
  };

  template<typename Endianness>
  struct GetterUint16
  {
    static inline void Get (char*& buf, float&f)
    {
      uint16 v = Endianness::Convert (csGetFromAddress::UInt16 (buf));
      buf += sizeof (uint16);
      f = float(v) / 65535.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint16); }
  };

  template<typename Endianness>
  struct GetterUint32
  {
    static inline void Get (char*& buf, float&f)
    {
      uint32 v = Endianness::Convert (csGetFromAddress::UInt32 (buf));
      buf += sizeof (uint32);
      f = float(v) / 4294967295.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint32); }
  };

  template<typename Tgetter>
  class RawHeightmapReader
  {
  public:
    bool ReadData (float* outputBuffer, size_t outputWidth,
      size_t outputHeight, size_t outputPitch, float heightScale, float offset,
      char* inputBuffer)
    {
      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;
        for (size_t x = 0; x < outputWidth; ++x)
        {
          Tgetter::Get (inputBuffer, *row);
          *row = *row * heightScale + offset;
          row++;
        }
        outputBuffer += outputPitch;
      }

      return true;
    }
  };

  FeederHeightSourceType ParseFormatString (const csString& format)
  {
    if (format.IsEmpty ())
    {
      return HEIGHT_SOURCE_IMAGE;
    }

    static const char* formatStrings[] =
    {
      "image",
      "raw8",
      "raw16le",
      "raw16be",
      "raw32le",
      "raw32be",
      "rawfloatle",
      "rawfloatbe"
    };

    for (size_t i = 0; i < sizeof(formatStrings) / sizeof(char*); ++i)
    {
      if (format == formatStrings[i])
      {
        return (FeederHeightSourceType)i;
      }
    }

    return HEIGHT_SOURCE_IMAGE;
  }

  HeightFeederParser::HeightFeederParser (const csString& mapSource,
    const csString& format, iLoader* imageLoader, iObjectRegistry* objReg)
    : sourceLocation (mapSource), sourceFormat (ParseFormatString (format)),
    imageLoader (imageLoader), objReg (objReg)
  {
    // Depending on format we might need some extra pointers, store those
    if (sourceFormat != HEIGHT_SOURCE_IMAGE)
    {
      // Need vfs, get it
      vfs = csQueryRegistry<iVFS> (objReg);
    }
  }

  bool HeightFeederParser::Load (float* outputBuffer, size_t outputWidth,
    size_t outputHeight, size_t outputPitch, float heightScale, float offset)
  {
    if (sourceFormat == HEIGHT_SOURCE_IMAGE)
    {
      return LoadFromImage (outputBuffer, outputWidth, outputHeight,
        outputPitch, heightScale, offset);
    }

    // Handle loading from all other (raw) formats
    if (!vfs)
      return false;

    csRef<iDataBuffer> buf = vfs->ReadFile (sourceLocation.GetDataSafe (),
	false);
    if (!buf ||
      outputHeight * outputWidth != buf->GetSize ())
      return false;

    switch (sourceFormat)
    {
      case HEIGHT_SOURCE_RAW8:
        {
          RawHeightmapReader<GetterUint8> reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW16LE:
        {
          RawHeightmapReader<GetterUint16<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW16BE:
        break;
        {
          RawHeightmapReader<GetterUint16<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
      case HEIGHT_SOURCE_RAW32LE:
        {
          RawHeightmapReader<GetterUint32<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW32BE:
        {
          RawHeightmapReader<GetterUint32<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAWFLOATLE:
        {
          RawHeightmapReader<GetterFloat<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAWFLOATBE:
        {
          RawHeightmapReader<GetterFloat<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_IMAGE:
	// @@@FIXME: Handle this case?
	break;
    }

    return false;
  }

  bool HeightFeederParser::LoadFromImage (float* outputBuffer,
      size_t outputWidth, size_t outputHeight, size_t outputPitch,
      float heightScale, float offset)
  {
    csRef<iImage> image = imageLoader->LoadImage (sourceLocation.GetDataSafe (),
      CS_IMGFMT_ANY);

    if (!image)
      return false;

    if ((size_t)image->GetWidth () != outputWidth ||
      (size_t)image->GetHeight () != outputHeight)
    {
      image = csImageManipulate::Rescale (image, (int)outputWidth,
	  (int)outputHeight);
    }

    if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_TRUECOLOR)
    {
      const csRGBpixel *data = (csRGBpixel*)image->GetImageData ();

      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x)
        {
          const csRGBpixel& p = *data++;

          unsigned int h;
          h = p.red; h <<= 8;
          h |= p.green; h <<= 8;
          h |= p.blue;

          *row++ = (h / ((float) ((1<<24)-1))) * heightScale + offset;
        }

        outputBuffer += outputPitch;
      }
      return true;
    }
    else if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_PALETTED8)
    {
      unsigned char *data = (unsigned char*)image->GetImageData ();
      const csRGBpixel *palette = image->GetPalette ();

      const float heightConstant = heightScale / 255.0f;

      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x)
        {
          const unsigned char p = *data++;
          const int h = palette[p].Intensity ();

          *row++ = h * heightConstant + offset;
        }

        outputBuffer += outputPitch;
      }
      return true;
    }

    return false;
  }

  void SmoothHeightmap (float* heightBuffer, size_t width, size_t height,
    size_t pitch)
  {
    // We need a temporary buffer for smoothing into
    float* tempBuffer = (float*)cs_malloc(width * height * sizeof(float));

    for (size_t passes = 0; passes < 2; ++passes)
    {
      // Smooth into tempBuffer

      // Copy first and last row
      for (size_t col = 0; col < width; ++col)
      {
        tempBuffer[0*width + col] = heightBuffer[0*pitch + col];
        tempBuffer[(height - 1)*width + col] = heightBuffer[(height - 1)*pitch + col];
      }

      // Handle interior
      for (size_t row = 1; row < height - 1; ++row)
      {
        // First column
        tempBuffer[row*width + 0] = heightBuffer[row*pitch + 0];

        // Rest
        for (size_t col = 1; col < width - 1; ++col)
        {
          float result = 0;

          result = 0.25f * heightBuffer[row*pitch + col - 1] +
                   0.50f * heightBuffer[row*pitch + col + 0] +
                   0.25f * heightBuffer[row*pitch + col + 1];

          tempBuffer[row*width + col] = result;
        }

        // Last column
        tempBuffer[row*width + width - 1] = heightBuffer[row*pitch + width - 1];
      }

      // Smooth back into the height buffer
      for (size_t row = 1; row < height - 1; ++row)
      {
        // Interior
        for (size_t col = 1; col < width - 1; ++col)
        {
          float result = 0;

          result = 0.25f * tempBuffer[(row - 1)*width + col] +
                   0.50f * tempBuffer[(row + 0)*width + col] +
                   0.25f * tempBuffer[(row + 1)*width + col];

          heightBuffer[row*pitch + col] = result;
        }
      }
    }

    cs_free (tempBuffer);
  }

  NormalFeederParser::NormalFeederParser (const csString& mapSource, iLoader* imageLoader, iObjectRegistry* objReg)
    : sourceLocation (mapSource), imageLoader (imageLoader), objReg (objReg)
  {

  }

  bool NormalFeederParser::Load (csVector3* outputBuffer, size_t outputWidth, size_t outputHeight, size_t outputPitch)
  {
    csRef<iImage> image = imageLoader->LoadImage (sourceLocation.GetDataSafe (),
      CS_IMGFMT_ANY);

    if (!image)
      return false;

    if ((size_t)image->GetWidth () != outputWidth ||
      (size_t)image->GetHeight () != outputHeight)
    {
      image = csImageManipulate::Rescale (image, (int)outputWidth,
        (int)outputHeight);
    }

    if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_TRUECOLOR)
    {
      const uint16 *data = (uint16*)image->GetImageData ();

      for (size_t y = 0; y < outputHeight; ++y)
      {
        csVector3* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x, ++row)
        {
          // Convert to 'single' precision float.
          row->x = csIEEEfloat::ToNative (csBigEndian::Convert(*data++));
          row->y = csIEEEfloat::ToNative (csBigEndian::Convert(*data++));

          // Calculate z.
          row->z = 1.0f - (row->x + row->y);
        }

        outputBuffer += outputPitch;
      }
      return true;
    }

    return false;
  }

}
CS_PLUGIN_NAMESPACE_END(Terrain2)
<commit_msg>Fix a wrong data size validation check. Spotted by Lirrec<commit_after>/*
  Copyright (C) 2007 by Marten Svanfeldt
                2004 by Anders Stenberg, Daniel Duhprey

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include "csgfx/imagemanipulate.h"
#include "csgfx/rgbpixel.h"
#include "csutil/csendian.h"
#include "csutil/objreg.h"
#include "imesh/terrain2.h"
#include "iutil/vfs.h"
#include "imap/loader.h"

#include "feederhelper.h"

CS_PLUGIN_NAMESPACE_BEGIN(Terrain2)
{

  template<typename Endianness>
  struct GetterFloat
  {
    static inline void Get (char*& buf, float&f)
    {
      f = csIEEEfloat::ToNative (Endianness::Convert (
	    csGetFromAddress::UInt32 (buf)));
      buf += sizeof(uint32);
    }
    static inline size_t ItemSize()
    { return sizeof(uint32); }
  };

  struct GetterUint8
  {
    static inline void Get (char*& buf, float&f)
    {
      uint8 v = (uint8) (*buf);
      buf += sizeof (uint8);
      f = float(v) / 255.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint8); }
  };

  template<typename Endianness>
  struct GetterUint16
  {
    static inline void Get (char*& buf, float&f)
    {
      uint16 v = Endianness::Convert (csGetFromAddress::UInt16 (buf));
      buf += sizeof (uint16);
      f = float(v) / 65535.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint16); }
  };

  template<typename Endianness>
  struct GetterUint32
  {
    static inline void Get (char*& buf, float&f)
    {
      uint32 v = Endianness::Convert (csGetFromAddress::UInt32 (buf));
      buf += sizeof (uint32);
      f = float(v) / 4294967295.0f;
    }
    static inline size_t ItemSize()
    { return sizeof(uint32); }
  };

  template<typename Tgetter>
  class RawHeightmapReader
  {
  public:
    bool ReadData (float* outputBuffer, size_t outputWidth,
      size_t outputHeight, size_t outputPitch, float heightScale, float offset,
      char* inputBuffer)
    {
      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;
        for (size_t x = 0; x < outputWidth; ++x)
        {
          Tgetter::Get (inputBuffer, *row);
          *row = *row * heightScale + offset;
          row++;
        }
        outputBuffer += outputPitch;
      }

      return true;
    }
  };

  FeederHeightSourceType ParseFormatString (const csString& format)
  {
    if (format.IsEmpty ())
    {
      return HEIGHT_SOURCE_IMAGE;
    }

    static const char* formatStrings[] =
    {
      "image",
      "raw8",
      "raw16le",
      "raw16be",
      "raw32le",
      "raw32be",
      "rawfloatle",
      "rawfloatbe"
    };

    for (size_t i = 0; i < sizeof(formatStrings) / sizeof(char*); ++i)
    {
      if (format == formatStrings[i])
      {
        return (FeederHeightSourceType)i;
      }
    }

    return HEIGHT_SOURCE_IMAGE;
  }

  HeightFeederParser::HeightFeederParser (const csString& mapSource,
    const csString& format, iLoader* imageLoader, iObjectRegistry* objReg)
    : sourceLocation (mapSource), sourceFormat (ParseFormatString (format)),
    imageLoader (imageLoader), objReg (objReg)
  {
    // Depending on format we might need some extra pointers, store those
    if (sourceFormat != HEIGHT_SOURCE_IMAGE)
    {
      // Need vfs, get it
      vfs = csQueryRegistry<iVFS> (objReg);
    }
  }

  bool HeightFeederParser::Load (float* outputBuffer, size_t outputWidth,
    size_t outputHeight, size_t outputPitch, float heightScale, float offset)
  {
    if (sourceFormat == HEIGHT_SOURCE_IMAGE)
    {
      return LoadFromImage (outputBuffer, outputWidth, outputHeight,
        outputPitch, heightScale, offset);
    }

    // Handle loading from all other (raw) formats
    if (!vfs)
      return false;

    size_t valueSize = 0;
    switch (sourceFormat)
    {
    case HEIGHT_SOURCE_RAW8:
      valueSize = sizeof (uint8);
      break;
    case HEIGHT_SOURCE_RAW16LE:
    case HEIGHT_SOURCE_RAW16BE:
      valueSize = sizeof (uint16);
      break;
    case HEIGHT_SOURCE_RAW32LE:
    case HEIGHT_SOURCE_RAW32BE:
    case HEIGHT_SOURCE_RAWFLOATLE:
    case HEIGHT_SOURCE_RAWFLOATBE:
      valueSize = sizeof (uint32);
      break;
    case HEIGHT_SOURCE_IMAGE:
      // Can not happen.
      CS_ASSERT (false);
    }

    csRef<iDataBuffer> buf = vfs->ReadFile (sourceLocation.GetDataSafe (),
	false);
    if (!buf ||
	((outputHeight * outputWidth * valueSize) != buf->GetSize ()))
      return false;

    switch (sourceFormat)
    {
      case HEIGHT_SOURCE_RAW8:
        {
          RawHeightmapReader<GetterUint8> reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW16LE:
        {
          RawHeightmapReader<GetterUint16<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW16BE:
        break;
        {
          RawHeightmapReader<GetterUint16<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
      case HEIGHT_SOURCE_RAW32LE:
        {
          RawHeightmapReader<GetterUint32<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAW32BE:
        {
          RawHeightmapReader<GetterUint32<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAWFLOATLE:
        {
          RawHeightmapReader<GetterFloat<csLittleEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_RAWFLOATBE:
        {
          RawHeightmapReader<GetterFloat<csBigEndian> > reader;
          return reader.ReadData (outputBuffer, outputWidth, outputHeight,
            outputPitch, heightScale, offset, buf->GetData ());
        }
        break;
      case HEIGHT_SOURCE_IMAGE:
	// @@@FIXME: Handle this case?
	break;
    }

    return false;
  }

  bool HeightFeederParser::LoadFromImage (float* outputBuffer,
      size_t outputWidth, size_t outputHeight, size_t outputPitch,
      float heightScale, float offset)
  {
    csRef<iImage> image = imageLoader->LoadImage (sourceLocation.GetDataSafe (),
      CS_IMGFMT_ANY);

    if (!image)
      return false;

    if ((size_t)image->GetWidth () != outputWidth ||
      (size_t)image->GetHeight () != outputHeight)
    {
      image = csImageManipulate::Rescale (image, (int)outputWidth,
	  (int)outputHeight);
    }

    if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_TRUECOLOR)
    {
      const csRGBpixel *data = (csRGBpixel*)image->GetImageData ();

      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x)
        {
          const csRGBpixel& p = *data++;

          unsigned int h;
          h = p.red; h <<= 8;
          h |= p.green; h <<= 8;
          h |= p.blue;

          *row++ = (h / ((float) ((1<<24)-1))) * heightScale + offset;
        }

        outputBuffer += outputPitch;
      }
      return true;
    }
    else if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_PALETTED8)
    {
      unsigned char *data = (unsigned char*)image->GetImageData ();
      const csRGBpixel *palette = image->GetPalette ();

      const float heightConstant = heightScale / 255.0f;

      for (size_t y = 0; y < outputHeight; ++y)
      {
        float* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x)
        {
          const unsigned char p = *data++;
          const int h = palette[p].Intensity ();

          *row++ = h * heightConstant + offset;
        }

        outputBuffer += outputPitch;
      }
      return true;
    }

    return false;
  }

  void SmoothHeightmap (float* heightBuffer, size_t width, size_t height,
    size_t pitch)
  {
    // We need a temporary buffer for smoothing into
    float* tempBuffer = (float*)cs_malloc(width * height * sizeof(float));

    for (size_t passes = 0; passes < 2; ++passes)
    {
      // Smooth into tempBuffer

      // Copy first and last row
      for (size_t col = 0; col < width; ++col)
      {
        tempBuffer[0*width + col] = heightBuffer[0*pitch + col];
        tempBuffer[(height - 1)*width + col] = heightBuffer[(height - 1)*pitch + col];
      }

      // Handle interior
      for (size_t row = 1; row < height - 1; ++row)
      {
        // First column
        tempBuffer[row*width + 0] = heightBuffer[row*pitch + 0];

        // Rest
        for (size_t col = 1; col < width - 1; ++col)
        {
          float result = 0;

          result = 0.25f * heightBuffer[row*pitch + col - 1] +
                   0.50f * heightBuffer[row*pitch + col + 0] +
                   0.25f * heightBuffer[row*pitch + col + 1];

          tempBuffer[row*width + col] = result;
        }

        // Last column
        tempBuffer[row*width + width - 1] = heightBuffer[row*pitch + width - 1];
      }

      // Smooth back into the height buffer
      for (size_t row = 1; row < height - 1; ++row)
      {
        // Interior
        for (size_t col = 1; col < width - 1; ++col)
        {
          float result = 0;

          result = 0.25f * tempBuffer[(row - 1)*width + col] +
                   0.50f * tempBuffer[(row + 0)*width + col] +
                   0.25f * tempBuffer[(row + 1)*width + col];

          heightBuffer[row*pitch + col] = result;
        }
      }
    }

    cs_free (tempBuffer);
  }

  NormalFeederParser::NormalFeederParser (const csString& mapSource, iLoader* imageLoader, iObjectRegistry* objReg)
    : sourceLocation (mapSource), imageLoader (imageLoader), objReg (objReg)
  {

  }

  bool NormalFeederParser::Load (csVector3* outputBuffer, size_t outputWidth, size_t outputHeight, size_t outputPitch)
  {
    csRef<iImage> image = imageLoader->LoadImage (sourceLocation.GetDataSafe (),
      CS_IMGFMT_ANY);

    if (!image)
      return false;

    if ((size_t)image->GetWidth () != outputWidth ||
      (size_t)image->GetHeight () != outputHeight)
    {
      image = csImageManipulate::Rescale (image, (int)outputWidth,
        (int)outputHeight);
    }

    if ((image->GetFormat () & CS_IMGFMT_MASK) == CS_IMGFMT_TRUECOLOR)
    {
      const uint16 *data = (uint16*)image->GetImageData ();

      for (size_t y = 0; y < outputHeight; ++y)
      {
        csVector3* row = outputBuffer;

        for (size_t x = 0; x < outputWidth; ++x, ++row)
        {
          // Convert to 'single' precision float.
          row->x = csIEEEfloat::ToNative (csBigEndian::Convert(*data++));
          row->y = csIEEEfloat::ToNative (csBigEndian::Convert(*data++));

          // Calculate z.
          row->z = 1.0f - (row->x + row->y);
        }

        outputBuffer += outputPitch;
      }
      return true;
    }

    return false;
  }

}
CS_PLUGIN_NAMESPACE_END(Terrain2)
<|endoftext|>


<commit_before><commit_msg>refactor conditional output.<commit_after><|endoftext|>


<commit_before><commit_msg>project_generate: Improve handling of bulk enable/disable options.<commit_after><|endoftext|>


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

#include <cassert>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>

#include "ZConsole.h"

static FILE*  input_fp = nullptr;
static FILE*  print_fp = nullptr;
static FILE*  snoop_fp = nullptr;

ZConsole::ZConsole(PLT::Device* device_)
   : Curses(device_)
{
   raw();
   noecho();

   input_fp = fopen("test.in", "r");
   if (input_fp)
   {
       // Fixed width
       cols = 80;
   }
   else
   {
      input_fp = fopen("fast.in", "r");
   }

   print_fp = NULL;
   snoop_fp = NULL;
}

ZConsole::~ZConsole()
{
   if (print_fp) fclose(print_fp);
   if (snoop_fp) fclose(snoop_fp);
}

void ZConsole::print(uint16_t zscii)
{
   // Filter repeated new-line
   static unsigned newline_count = 1;
   if (zscii == 0xD)
   {
      zscii = '\n';
   }
   if (zscii == '\n')
   {
      if (++newline_count >= 3) return;
   }
   else
   {
      newline_count = 0;
   }

   if (print_fp == NULL) print_fp = fopen("print.log", "w");
   assert(print_fp);
   fputc(zscii, print_fp);
}

void ZConsole::snoop(uint16_t zscii)
{
   if (snoop_fp == NULL) snoop_fp = fopen("snoop.in", "w");
   assert(snoop_fp);
   fputc(zscii, snoop_fp);
}

bool ZConsole::read(uint16_t& zscii, unsigned timeout)
{
   // TODO timeout
   if ((input_fp == NULL) || feof(input_fp))
   {
      input_fp = NULL;
      int ch = getch();
      if (ch < 0)
      {
         exit(0);
      }
      if (ch == 0x7F) ch = '\b';
      zscii = ch;
   }
   else
   {
      zscii = fgetc(input_fp);
   }
   return true;
}

void tprintf(const char* format, ...)
{
   static FILE* fp = NULL;
   va_list  ap;

   if (fp == NULL)
   {
      fp = fopen("zif.log", "w");
   }

   va_start(ap, format);
   vfprintf(fp, format, ap);
   va_end(ap);

   fflush(fp);
}

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

#include <cassert>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>

#include "ZConsole.h"

static FILE*  input_fp = nullptr;
static FILE*  print_fp = nullptr;
static FILE*  snoop_fp = nullptr;

ZConsole::ZConsole(PLT::Device* device_)
   : Curses(device_)
{
   raw();
   noecho();

   input_fp = fopen("test.in", "r");
   if (input_fp)
   {
       // Fixed width
       cols = 80;
   }
   else
   {
      input_fp = fopen("fast.in", "r");
   }

   print_fp = nullptr;
   snoop_fp = nullptr;
}

ZConsole::~ZConsole()
{
   if (print_fp) fclose(print_fp);
   if (snoop_fp) fclose(snoop_fp);
}

void ZConsole::print(uint16_t zscii)
{
   // Filter repeated new-line
   static unsigned newline_count = 1;
   if (zscii == 0xD)
   {
      zscii = '\n';
   }
   if (zscii == '\n')
   {
      if (++newline_count >= 3) return;
   }
   else
   {
      newline_count = 0;
   }

   if (print_fp == nullptr) print_fp = fopen("print.log", "w");
   assert(print_fp);
   fputc(zscii, print_fp);
}

void ZConsole::snoop(uint16_t zscii)
{
   if (snoop_fp == nullptr) snoop_fp = fopen("snoop.in", "w");
   assert(snoop_fp);
   fputc(zscii, snoop_fp);
}

bool ZConsole::read(uint16_t& zscii, unsigned timeout)
{
   // TODO timeout
   if ((input_fp == nullptr) || feof(input_fp))
   {
      input_fp = nullptr;

      int ch;

      while(true)
      {
         int ch = getch();
         if (ch < 0)
         {
            exit(0);
         }
         else if (ch == 0x7F)
         {
            ch = '\b';
         }
         else if (ch < 0x7F)
         {
            break;
         }
      }
   }
   else
   {
      ch = fgetc(input_fp);
   }
   zscii = ch;
   return true;
}

void tprintf(const char* format, ...)
{
   static FILE* fp = nullptr;
   va_list  ap;

   if (fp == nullptr)
   {
      fp = fopen("zif.log", "w");
   }

   va_start(ap, format);
   vfprintf(fp, format, ap);
   va_end(ap);

   fflush(fp);
}

<|endoftext|>


<commit_before>// Copyright (c) 2016-2020 The ZCash developers
// Copyright (c) 2020 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "test/test_pivx.h"

#include "sapling/address.hpp"
#include "sapling/key_io_sapling.h"
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(sapling_key_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(ps_address_test)
{
    for (size_t i = 0; i < 1000; i++) {
        auto sk = libzcash::SaplingSpendingKey::random();
        {
            std::string sk_string = KeyIO::EncodeSpendingKey(sk);
            BOOST_CHECK(sk_string.compare(0, 26, Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY)) == 0);

            auto spendingkey2 = KeyIO::DecodeSpendingKey(sk_string);
            BOOST_CHECK(IsValidSpendingKey(spendingkey2));

            BOOST_ASSERT(boost::get<libzcash::SaplingSpendingKey>(&spendingkey2) != nullptr);
            auto sk2 = boost::get<libzcash::SaplingSpendingKey>(spendingkey2);
            BOOST_CHECK(sk == sk2);
        }
        {
            libzcash::SaplingPaymentAddress addr = sk.default_address();

            std::string addr_string = KeyIO::EncodePaymentAddress(addr);
            BOOST_CHECK(addr_string.compare(0, 2, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);

            auto paymentaddr2 = KeyIO::DecodePaymentAddress(addr_string);
            BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));

            BOOST_ASSERT(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
            auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
            BOOST_CHECK(addr == addr2);
        }
    }
}

BOOST_AUTO_TEST_CASE(EncodeAndDecodeSapling)
{
    SelectParams(CBaseChainParams::MAIN);

    for (size_t i = 0; i < 1000; i++) {
        auto sk = libzcash::SaplingSpendingKey::random();
        {
            std::string sk_string = KeyIO::EncodeSpendingKey(sk);
            BOOST_CHECK(
                    sk_string.substr(0, 26) ==
                    Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY));

            auto spendingkey2 = KeyIO::DecodeSpendingKey(sk_string);
            BOOST_CHECK(IsValidSpendingKey(spendingkey2));

            BOOST_CHECK(boost::get<libzcash::SaplingSpendingKey>(&spendingkey2) != nullptr);
            auto sk2 = boost::get<libzcash::SaplingSpendingKey>(spendingkey2);
            BOOST_CHECK(sk == sk2);
        }
        {
            auto addr = sk.default_address();

            std::string addr_string = KeyIO::EncodePaymentAddress(addr);
            BOOST_CHECK(
                    addr_string.substr(0, 2) ==
                    Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));

            auto paymentaddr2 = KeyIO::DecodePaymentAddress(addr_string);
            BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));

            BOOST_CHECK(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
            auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
            BOOST_CHECK(addr == addr2);
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>[Test] Use regtest in sapling/key_tests/ps_address_test<commit_after>// Copyright (c) 2016-2020 The ZCash developers
// Copyright (c) 2020 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "test/test_pivx.h"

#include "sapling/address.hpp"
#include "sapling/key_io_sapling.h"
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(sapling_key_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(ps_address_test)
{
    SelectParams(CBaseChainParams::REGTEST);

    for (size_t i = 0; i < 1000; i++) {
        auto sk = libzcash::SaplingSpendingKey::random();
        {
            std::string sk_string = KeyIO::EncodeSpendingKey(sk);
            BOOST_CHECK(sk_string.compare(0, 26, Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY)) == 0);

            auto spendingkey2 = KeyIO::DecodeSpendingKey(sk_string);
            BOOST_CHECK(IsValidSpendingKey(spendingkey2));

            BOOST_ASSERT(boost::get<libzcash::SaplingSpendingKey>(&spendingkey2) != nullptr);
            auto sk2 = boost::get<libzcash::SaplingSpendingKey>(spendingkey2);
            BOOST_CHECK(sk == sk2);
        }
        {
            libzcash::SaplingPaymentAddress addr = sk.default_address();

            std::string addr_string = KeyIO::EncodePaymentAddress(addr);
            BOOST_CHECK(addr_string.compare(0, 12, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);

            auto paymentaddr2 = KeyIO::DecodePaymentAddress(addr_string);
            BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));

            BOOST_ASSERT(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
            auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
            BOOST_CHECK(addr == addr2);
        }
    }
}

BOOST_AUTO_TEST_CASE(EncodeAndDecodeSapling)
{
    SelectParams(CBaseChainParams::MAIN);

    for (size_t i = 0; i < 1000; i++) {
        auto sk = libzcash::SaplingSpendingKey::random();
        {
            std::string sk_string = KeyIO::EncodeSpendingKey(sk);
            BOOST_CHECK(
                    sk_string.substr(0, 26) ==
                    Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY));

            auto spendingkey2 = KeyIO::DecodeSpendingKey(sk_string);
            BOOST_CHECK(IsValidSpendingKey(spendingkey2));

            BOOST_CHECK(boost::get<libzcash::SaplingSpendingKey>(&spendingkey2) != nullptr);
            auto sk2 = boost::get<libzcash::SaplingSpendingKey>(spendingkey2);
            BOOST_CHECK(sk == sk2);
        }
        {
            auto addr = sk.default_address();

            std::string addr_string = KeyIO::EncodePaymentAddress(addr);
            BOOST_CHECK(
                    addr_string.substr(0, 2) ==
                    Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));

            auto paymentaddr2 = KeyIO::DecodePaymentAddress(addr_string);
            BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));

            BOOST_CHECK(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
            auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
            BOOST_CHECK(addr == addr2);
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>// BGPreview.cpp : implements preview of graphics files from memory - rendered in a background thread (part of CHexEditDoc)

#include "stdafx.h"
#include "HexEdit.h"

#include "HexEditDoc.h"
#include "HexEditView.h"
#include <FreeImage.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// TODO xxx
//  test with a 2GByte file

void CHexEditDoc::AddPreviewView(CHexEditView *pview)
{
	TRACE("+++ preview +++ %d\n", preview_count_);
	if (++preview_count_ == 1)
	{
		// Create the background thread and start it scanning
		CreatePreviewThread();
		TRACE("+++ Pulsing preview event\n");
		start_preview_event_.SetEvent();
	}
	ASSERT(pthread6_ != NULL);
}

void CHexEditDoc::RemovePreviewView()
{
	if (--preview_count_ == 0)
	{
		if (pthread6_ != NULL)
			KillPreviewThread();
		FIBITMAP *dib = preview_dib_;
		preview_dib_ = NULL;
		TRACE("+++  preview: FreeImage_Unload(%d)\n", dib);
		if (dib != NULL)
			FreeImage_Unload(dib);
	}
	TRACE("+++  Preview --- %d\n", preview_count_);
}

// Doc or colours have changed - signal to redisplay
void CHexEditDoc::PreviewChange(CHexEditView *pview /*= NULL*/)
{
	if (preview_count_ == 0) return;
	ASSERT(pthread6_ != NULL);
	if (pthread6_ == NULL) return;

	// Wait for thread to stop if necessary
	bool waiting;
	docdata_.Lock();
	preview_command_ = STOP;
	docdata_.Unlock();
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_NORMAL);
	for (int ii = 0; ii < 100; ++ii)
	{
		// Wait just a little bit in case the thread was just about to go into wait state
		docdata_.Lock();
		waiting = preview_state_ == WAITING;
		docdata_.Unlock();
		if (waiting)
			break;
		TRACE("+++ PreviewChange - thread not waiting (yet)\n");
		Sleep(1);
	}
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_LOWEST);
	ASSERT(waiting);

	// Restart the scan
	docdata_.Lock();
	preview_command_ = NONE;  // make sure we don't stop the scan before it starts
	preview_fin_ = false;
	docdata_.Unlock();

	TRACE("+++ Pulsing preview event (restart)\n");
	start_preview_event_.SetEvent();
}

int CHexEditDoc::PreviewProgress()
{
	return -1;
}

// Sends a message for the thread to kill itself then tides up shared members
void CHexEditDoc::KillPreviewThread()
{
	ASSERT(pthread6_ != NULL);
	if (pthread6_ == NULL) return;

	HANDLE hh = pthread6_->m_hThread;    // Save handle since it will be lost when thread is killed and object is destroyed
	TRACE1("+++ Killing preview thread for %p\n", this);

	bool waiting, dying;
	docdata_.Lock();
	preview_command_ = DIE;
	docdata_.Unlock();
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_NORMAL); // Make it a quick and painless death
	for (int ii = 0; ii < 100; ++ii)
	{
		// Wait just a little bit in case the thread was just about to go into wait state
		docdata_.Lock();
		waiting = preview_state_ == WAITING;
		dying   = preview_state_ == DYING;
		docdata_.Unlock();
		if (waiting || dying)
			break;
		Sleep(1);
	}
	ASSERT(waiting || dying);

	timer tt(true);
	if (waiting)
		start_preview_event_.SetEvent();

	pthread6_ = NULL;
	DWORD wait_status = ::WaitForSingleObject(hh, INFINITE);
	ASSERT(wait_status == WAIT_OBJECT_0 || wait_status == WAIT_FAILED);
	tt.stop();
	TRACE1("+++ Thread took %g secs to kill\n", double(tt.elapsed()));

	// Free resources that are only needed during scan
	if (pfile6_ != NULL)
	{
		pfile6_->Close();
		delete pfile6_;
		pfile6_ = NULL;
	}
	for (int ii = 0; ii < doc_loc::max_data_files; ++ii)
	{
		if (data_file6_[ii] != NULL)
		{
			data_file6_[ii]->Close();
			delete data_file6_[ii];
			data_file6_[ii] = NULL;
		}
	}
}

// bg_func is the entry point for the thread.
// It just calls the RunPreviewThread member for the doc passed to it.
static UINT bg_func(LPVOID pParam)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)pParam;

	TRACE1("+++ Preview thread started for doc %p\n", pDoc);

	return pDoc->RunPreviewThread();
}

// Sets up shared members and creates the thread using bg_func (above)
void CHexEditDoc::CreatePreviewThread()
{
	ASSERT(pthread6_ == NULL);
	ASSERT(pfile6_ == NULL);

	// Open copy of file to be used by background thread
	if (pfile1_ != NULL)
	{
		if (IsDevice())
			pfile6_ = new CFileNC();
		else
			pfile6_ = new CFile64();
		if (!pfile6_->Open(pfile1_->GetFilePath(),
					CFile::modeRead|CFile::shareDenyNone|CFile::typeBinary) )
		{
			TRACE1("+++ Preview file open failed for %p\n", this);
			return;
		}
	}

	// Open copy of any data files in use too
	for (int ii = 0; ii < doc_loc::max_data_files; ++ii)
	{
		ASSERT(data_file6_[ii] == NULL);
		if (data_file_[ii] != NULL)
			data_file6_[ii] = new CFile64(data_file_[ii]->GetFilePath(),
										  CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary);
	}

	// Create new thread
	preview_command_ = NONE;
	preview_state_ = STARTING;    // pre start and very beginning
	preview_fin_ = false;
	TRACE1("+++ Creating preview thread for %p\n", this);
	pthread6_ = AfxBeginThread(&bg_func, this, THREAD_PRIORITY_LOWEST);
	ASSERT(pthread6_ != NULL);
}

//---------------------------------------------------------------------------------------
// Funcs to modify the bitmap in memory (after loaded)

void CHexEditDoc::RotateClockWise()
{
	preview_rotate_ = (preview_rotate_ + 1) % 4;

	rotate_to(preview_rotate_);
}

void CHexEditDoc::FlipVert()
{
	CSingleLock sl(&docdata_, TRUE);

	if (preview_state_ != WAITING || preview_dib_ == NULL)
		return;

	preview_flipvert_ = !preview_flipvert_;
}

void CHexEditDoc::FlipHoriz()
{
	CSingleLock sl(&docdata_, TRUE);

	if (preview_state_ != WAITING || preview_dib_ == NULL)
		return;

	preview_fliphoriz_ = !preview_fliphoriz_;
}

void CHexEditDoc::rotate_to(int)
{
	CSingleLock sl(&docdata_, TRUE);

	if (preview_state_ != WAITING || preview_dib_ == NULL)
		return;
}

//---------------------------------------------------------------------------------------
// These 4 funcs (fi_read(), etc) are used with FreeImage for in-memory processing of bitmap data.
// Pointers to these funcs are added to a FreeImageIO struct (see fi_funcs below) and passed
// to FreeImage functions like FreeImage_LoadFromHandle() or FreeImage_GetFileTypeFromHandle().

// fi_read() should behave like fread (returns count if all items were read OK, returns 0 on eof/error)
unsigned __stdcall CHexEditDoc::fi_read(void *buffer, unsigned size, unsigned count, fi_handle handle)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;

	if (pDoc->PreviewProcessStop())
		return 0;       // indicate that processing should stop

	size_t got = pDoc->GetData((unsigned char *)buffer, size*count, pDoc->preview_address_);
	pDoc->preview_address_ += got;
	return got/size;
}

unsigned __stdcall CHexEditDoc::fi_write(void *buffer, unsigned size, unsigned count, fi_handle handle)
{
	ASSERT(0);   // writing should not happen - we are only reading to display the bitmap
	return 0;
}

int __stdcall CHexEditDoc::fi_seek(fi_handle handle, long offset, int origin)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;
	FILE_ADDRESS addr = 0;
	switch (origin)
	{
	case SEEK_SET:
		addr = offset;
		break;
	case SEEK_CUR:
		addr = pDoc->preview_address_ + offset;
		break;
	case SEEK_END:
		addr = pDoc->length_ + offset;
		break;
	}
	if (addr > LONG_MAX)
		return -1L;        // seek failed

	pDoc->preview_address_ = (long)addr;
	return 0L;             // seek succeeded
}

long __stdcall CHexEditDoc::fi_tell(fi_handle handle)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;

	return pDoc->preview_address_;
}

FreeImageIO fi_funcs = { &CHexEditDoc::fi_read, &CHexEditDoc::fi_write, &CHexEditDoc::fi_seek, &CHexEditDoc::fi_tell };

// This is what does the work in the background thread
UINT CHexEditDoc::RunPreviewThread()
{
	// Keep looping until we are told to die
	for (;;)
	{
		// Signal that we are waiting then wait for start_preview_event_ to be pulsed
		{
			CSingleLock sl(&docdata_, TRUE);
			preview_state_ = WAITING;
		}
		TRACE1("+++ BGPreview: waiting for %p\n", this);
		DWORD wait_status = ::WaitForSingleObject(HANDLE(start_preview_event_), INFINITE);
		docdata_.Lock();
		preview_state_ = SCANNING;
		docdata_.Unlock();
		start_preview_event_.ResetEvent();
		ASSERT(wait_status == WAIT_OBJECT_0);
		TRACE1("+++ BGPreview: got event for %p\n", this);

		if (PreviewProcessStop())
			continue;

		// Reset for new scan
		docdata_.Lock();
		preview_fin_ = false;
		preview_address_ = 0;
		if (preview_dib_ != NULL)
		{
			FreeImage_Unload(preview_dib_);
			preview_dib_ = NULL;
		}
		docdata_.Unlock();

		FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromHandle(&fi_funcs, this);
		TRACE("Image format is %d\n", fif);
		FIBITMAP * dib = FreeImage_LoadFromHandle(fif, &fi_funcs, this);

		// use FreeImage_Rotate, FreeImage_FlipHorizontal, FreeImage_FlipVertical here? (use preview_rotate_, preview_flip_horiz_, preview_flip_vert_)
		TRACE1("+++ BGPreview: finished load for %p\n", this);
		docdata_.Lock();
		preview_fin_ = true;
		preview_address_ = 0;
		preview_dib_ = dib;
		docdata_.Unlock();
	}
	return 0;   // never reached
}

// Check for a stop scanning (or kill) of the background thread
bool CHexEditDoc::PreviewProcessStop()
{
	bool retval = false;

	CSingleLock sl(&docdata_, TRUE);
	switch(preview_command_)
	{
	case STOP:                      // stop scan and wait
		TRACE1("+++ BGPreview: stop for %p\n", this);
		retval = true;
		break;
	case DIE:                       // terminate this thread
		TRACE1("+++ BGPreview: killed thread for %p\n", this);
		preview_state_ = DYING;
		sl.Unlock();                // we need this here as AfxEndThread() never returns so d'tor is not called
		AfxEndThread(1);            // kills thread (no return)
		break;                      // Avoid warning
	case NONE:                      // nothing needed here - just continue scanning
		break;
	default:                        // should not happen
		ASSERT(0);
	}

	// Prevent reprocessing of the same command
	preview_command_ = NONE;
	return retval;
}

<commit_msg>Remove code that is not to be used - beginning of preview rotate/flip options in doc (new strategy is to do it during render process per view)<commit_after>// BGPreview.cpp : implements preview of graphics files from memory - rendered in a background thread (part of CHexEditDoc)

#include "stdafx.h"
#include "HexEdit.h"

#include "HexEditDoc.h"
#include "HexEditView.h"
#include <FreeImage.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// TODO xxx
//  test with a 2GByte file

void CHexEditDoc::AddPreviewView(CHexEditView *pview)
{
	TRACE("+++ preview +++ %d\n", preview_count_);
	if (++preview_count_ == 1)
	{
		// Create the background thread and start it scanning
		CreatePreviewThread();
		TRACE("+++ Pulsing preview event\n");
		start_preview_event_.SetEvent();
	}
	ASSERT(pthread6_ != NULL);
}

void CHexEditDoc::RemovePreviewView()
{
	if (--preview_count_ == 0)
	{
		if (pthread6_ != NULL)
			KillPreviewThread();
		FIBITMAP *dib = preview_dib_;
		preview_dib_ = NULL;
		TRACE("+++  preview: FreeImage_Unload(%d)\n", dib);
		if (dib != NULL)
			FreeImage_Unload(dib);
	}
	TRACE("+++  Preview --- %d\n", preview_count_);
}

// Doc or colours have changed - signal to redisplay
void CHexEditDoc::PreviewChange(CHexEditView *pview /*= NULL*/)
{
	if (preview_count_ == 0) return;
	ASSERT(pthread6_ != NULL);
	if (pthread6_ == NULL) return;

	// Wait for thread to stop if necessary
	bool waiting;
	docdata_.Lock();
	preview_command_ = STOP;
	docdata_.Unlock();
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_NORMAL);
	for (int ii = 0; ii < 100; ++ii)
	{
		// Wait just a little bit in case the thread was just about to go into wait state
		docdata_.Lock();
		waiting = preview_state_ == WAITING;
		docdata_.Unlock();
		if (waiting)
			break;
		TRACE("+++ PreviewChange - thread not waiting (yet)\n");
		Sleep(1);
	}
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_LOWEST);
	ASSERT(waiting);

	// Restart the scan
	docdata_.Lock();
	preview_command_ = NONE;  // make sure we don't stop the scan before it starts
	preview_fin_ = false;
	docdata_.Unlock();

	TRACE("+++ Pulsing preview event (restart)\n");
	start_preview_event_.SetEvent();
}

int CHexEditDoc::PreviewProgress()
{
	return -1;
}

// Sends a message for the thread to kill itself then tides up shared members
void CHexEditDoc::KillPreviewThread()
{
	ASSERT(pthread6_ != NULL);
	if (pthread6_ == NULL) return;

	HANDLE hh = pthread6_->m_hThread;    // Save handle since it will be lost when thread is killed and object is destroyed
	TRACE1("+++ Killing preview thread for %p\n", this);

	bool waiting, dying;
	docdata_.Lock();
	preview_command_ = DIE;
	docdata_.Unlock();
	SetThreadPriority(pthread6_->m_hThread, THREAD_PRIORITY_NORMAL); // Make it a quick and painless death
	for (int ii = 0; ii < 100; ++ii)
	{
		// Wait just a little bit in case the thread was just about to go into wait state
		docdata_.Lock();
		waiting = preview_state_ == WAITING;
		dying   = preview_state_ == DYING;
		docdata_.Unlock();
		if (waiting || dying)
			break;
		Sleep(1);
	}
	ASSERT(waiting || dying);

	timer tt(true);
	if (waiting)
		start_preview_event_.SetEvent();

	pthread6_ = NULL;
	DWORD wait_status = ::WaitForSingleObject(hh, INFINITE);
	ASSERT(wait_status == WAIT_OBJECT_0 || wait_status == WAIT_FAILED);
	tt.stop();
	TRACE1("+++ Thread took %g secs to kill\n", double(tt.elapsed()));

	// Free resources that are only needed during scan
	if (pfile6_ != NULL)
	{
		pfile6_->Close();
		delete pfile6_;
		pfile6_ = NULL;
	}
	for (int ii = 0; ii < doc_loc::max_data_files; ++ii)
	{
		if (data_file6_[ii] != NULL)
		{
			data_file6_[ii]->Close();
			delete data_file6_[ii];
			data_file6_[ii] = NULL;
		}
	}
}

// bg_func is the entry point for the thread.
// It just calls the RunPreviewThread member for the doc passed to it.
static UINT bg_func(LPVOID pParam)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)pParam;

	TRACE1("+++ Preview thread started for doc %p\n", pDoc);

	return pDoc->RunPreviewThread();
}

// Sets up shared members and creates the thread using bg_func (above)
void CHexEditDoc::CreatePreviewThread()
{
	ASSERT(pthread6_ == NULL);
	ASSERT(pfile6_ == NULL);

	// Open copy of file to be used by background thread
	if (pfile1_ != NULL)
	{
		if (IsDevice())
			pfile6_ = new CFileNC();
		else
			pfile6_ = new CFile64();
		if (!pfile6_->Open(pfile1_->GetFilePath(),
					CFile::modeRead|CFile::shareDenyNone|CFile::typeBinary) )
		{
			TRACE1("+++ Preview file open failed for %p\n", this);
			return;
		}
	}

	// Open copy of any data files in use too
	for (int ii = 0; ii < doc_loc::max_data_files; ++ii)
	{
		ASSERT(data_file6_[ii] == NULL);
		if (data_file_[ii] != NULL)
			data_file6_[ii] = new CFile64(data_file_[ii]->GetFilePath(),
										  CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary);
	}

	// Create new thread
	preview_command_ = NONE;
	preview_state_ = STARTING;    // pre start and very beginning
	preview_fin_ = false;
	TRACE1("+++ Creating preview thread for %p\n", this);
	pthread6_ = AfxBeginThread(&bg_func, this, THREAD_PRIORITY_LOWEST);
	ASSERT(pthread6_ != NULL);
}

//---------------------------------------------------------------------------------------
// These 4 funcs (fi_read(), etc) are used with FreeImage for in-memory processing of bitmap data.
// Pointers to these funcs are added to a FreeImageIO struct (see fi_funcs below) and passed
// to FreeImage functions like FreeImage_LoadFromHandle() or FreeImage_GetFileTypeFromHandle().

// fi_read() should behave like fread (returns count if all items were read OK, returns 0 on eof/error)
unsigned __stdcall CHexEditDoc::fi_read(void *buffer, unsigned size, unsigned count, fi_handle handle)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;

	if (pDoc->PreviewProcessStop())
		return 0;       // indicate that processing should stop

	size_t got = pDoc->GetData((unsigned char *)buffer, size*count, pDoc->preview_address_);
	pDoc->preview_address_ += got;
	return got/size;
}

unsigned __stdcall CHexEditDoc::fi_write(void *buffer, unsigned size, unsigned count, fi_handle handle)
{
	ASSERT(0);   // writing should not happen - we are only reading to display the bitmap
	return 0;
}

int __stdcall CHexEditDoc::fi_seek(fi_handle handle, long offset, int origin)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;
	FILE_ADDRESS addr = 0;
	switch (origin)
	{
	case SEEK_SET:
		addr = offset;
		break;
	case SEEK_CUR:
		addr = pDoc->preview_address_ + offset;
		break;
	case SEEK_END:
		addr = pDoc->length_ + offset;
		break;
	}
	if (addr > LONG_MAX)
		return -1L;        // seek failed

	pDoc->preview_address_ = (long)addr;
	return 0L;             // seek succeeded
}

long __stdcall CHexEditDoc::fi_tell(fi_handle handle)
{
	CHexEditDoc *pDoc = (CHexEditDoc *)handle;

	return pDoc->preview_address_;
}

FreeImageIO fi_funcs = { &CHexEditDoc::fi_read, &CHexEditDoc::fi_write, &CHexEditDoc::fi_seek, &CHexEditDoc::fi_tell };

// This is what does the work in the background thread
UINT CHexEditDoc::RunPreviewThread()
{
	// Keep looping until we are told to die
	for (;;)
	{
		// Signal that we are waiting then wait for start_preview_event_ to be pulsed
		{
			CSingleLock sl(&docdata_, TRUE);
			preview_state_ = WAITING;
		}
		TRACE1("+++ BGPreview: waiting for %p\n", this);
		DWORD wait_status = ::WaitForSingleObject(HANDLE(start_preview_event_), INFINITE);
		docdata_.Lock();
		preview_state_ = SCANNING;
		docdata_.Unlock();
		start_preview_event_.ResetEvent();
		ASSERT(wait_status == WAIT_OBJECT_0);
		TRACE1("+++ BGPreview: got event for %p\n", this);

		if (PreviewProcessStop())
			continue;

		// Reset for new scan
		docdata_.Lock();
		preview_fin_ = false;
		preview_address_ = 0;
		if (preview_dib_ != NULL)
		{
			FreeImage_Unload(preview_dib_);
			preview_dib_ = NULL;
		}
		docdata_.Unlock();

		FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromHandle(&fi_funcs, this);
		TRACE("Image format is %d\n", fif);
		FIBITMAP * dib = FreeImage_LoadFromHandle(fif, &fi_funcs, this);

		TRACE1("+++ BGPreview: finished load for %p\n", this);
		docdata_.Lock();
		preview_fin_ = true;
		preview_address_ = 0;
		preview_dib_ = dib;
		docdata_.Unlock();
	}
	return 0;   // never reached
}

// Check for a stop scanning (or kill) of the background thread
bool CHexEditDoc::PreviewProcessStop()
{
	bool retval = false;

	CSingleLock sl(&docdata_, TRUE);
	switch(preview_command_)
	{
	case STOP:                      // stop scan and wait
		TRACE1("+++ BGPreview: stop for %p\n", this);
		retval = true;
		break;
	case DIE:                       // terminate this thread
		TRACE1("+++ BGPreview: killed thread for %p\n", this);
		preview_state_ = DYING;
		sl.Unlock();                // we need this here as AfxEndThread() never returns so d'tor is not called
		AfxEndThread(1);            // kills thread (no return)
		break;                      // Avoid warning
	case NONE:                      // nothing needed here - just continue scanning
		break;
	default:                        // should not happen
		ASSERT(0);
	}

	// Prevent reprocessing of the same command
	preview_command_ = NONE;
	return retval;
}

<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <omp.h>
#include <random>
#include <stdio.h>
#include <string>
#include <time.h>

#include "Neural_Networks.h"

using namespace std;

void Read_MNIST(string training_set_images, string training_set_labels, string test_set_images, string test_set_labels, int number_training, int number_test, float **input, float **output) {
	ifstream file(training_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_images + " not found" << endl;
	}

	file.open(training_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_labels + " not found" << endl;
	}

	file.open(test_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_images + " not found" << endl;
	}

	file.open(test_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_labels + " not found" << endl;
	}
}

int main() {
	int batch_size = 128;
	int epochs = 30;
	int number_threads = 6;
	int number_training = 60000;
	int number_test = 10000;
	int number_nodes[] = { 784, 10 };
	int time_step = 28;

	float **x_data = new float*[number_training + number_test];
	float **y_data = new float*[number_training + number_test];
	float **x_train = x_data;
	float **y_train = y_data;
	float **x_test = &x_data[number_training];
	float **y_test = &y_data[number_training];

	double decay = 0.000001;
	double learning_rate = 0.1;
	double momentum = 0.9;

	string path;

	Neural_Networks NN = Neural_Networks();

	cout << "path where MNIST handwritten digits dataset is : ";
	getline(cin, path);

	for (int h = 0; h < number_training + number_test; h++) {
		x_data[h] = new float[number_nodes[0]];
		y_data[h] = new float[number_nodes[1]];
	}
	Read_MNIST(path + "train-images.idx3-ubyte", path + "train-labels.idx1-ubyte", path + "t10k-images.idx3-ubyte", path + "t10k-labels.idx1-ubyte", number_training, number_test, x_data, y_data);
	omp_set_num_threads(number_threads);

	srand(0);

	NN.Add(Layer(time_step, number_nodes[0] / time_step));
	NN.Add(LSTM(time_step, 128))->Initializer(Initializer(1), LSTM::forget)->Direction(+1);
	NN.Add(LSTM(time_step, 128))->Initializer(Initializer(1), LSTM::forget)->Direction(-1);
	NN.Add(Layer(2, 128));
	NN.Add(Layer(1, 256));
	NN.Add(number_nodes[1])->Activation(Activation::softmax);

	NN.Connect(1, 0, "W");
	NN.Connect(1, 1, "W,recurrent");
	NN.Connect(2, 0, "W");
	NN.Connect(2, 2, "W,recurrent");
	{
		unordered_multimap<int, int> time_connection;

		time_connection.insert(pair<int, int>(0, time_step - 1));
		NN.Connect(3, 1, "copy", &time_connection);

		time_connection.clear();
		time_connection.insert(pair<int, int>(1, 0));
		NN.Connect(3, 2, "copy", &time_connection);
	}
	NN.Connect(4, 3, "copy");
	NN.Connect(5, 4, "W");

	NN.Compile(Loss::cross_entropy, Optimizer(Nesterov(learning_rate, momentum, decay)));

	for (int e = 0, time = clock(); e < epochs; e++) {
		int score[2] = { 0, };

		float **_input = new float*[batch_size];
		float **output = new float*[batch_size];

		double loss[2] = { NN.Fit(NN.Shuffle(x_train, number_training), NN.Shuffle(y_train, number_training), number_training, batch_size), NN.Evaluate(x_test, y_test, number_test, batch_size) };

		for (int h = 0; h < batch_size; h++) {
			output[h] = new float[number_nodes[1]];
		}
		for (int h = 0, i = 0; i < number_training + number_test; i++) {
			_input[h] = x_data[i];

			if (++h == batch_size || i == number_training + number_test - 1) {
				NN.Predict(_input, output, h);

				for (int argmax, index = i - h + 1; --h >= 0;) {
					double max = 0;

					for (int j = 0; j < number_nodes[1]; j++) {
						if (j == 0 || max < output[h][j]) {
							max = output[h][argmax = j];
						}
					}
					score[(index + h < number_training) ? (0) : (1)] += (int)y_data[index + h][argmax];
				}
				h = 0;
			}
		}
		printf("loss: %.4f / %.4f	accuracy: %.4f / %.4f	step %d  %.2f sec\n", loss[0], loss[1], 1.0 * score[0] / number_training, 1.0 * score[1] / number_test, e + 1, (double)(clock() - time) / CLOCKS_PER_SEC);

		for (int h = 0; h < batch_size; h++) {
			delete[] output[h];
		}
		delete[] _input;
		delete[] output;
	}

	for (int i = 0; i < number_training + number_test; i++) {
		delete[] x_data[i];
		delete[] y_data[i];
	}
	delete[] x_data;
	delete[] y_data;
}
<commit_msg>Update main.cpp<commit_after>#include <fstream>
#include <iostream>
#include <omp.h>
#include <stdio.h>
#include <string>
#include <time.h>

#include "Neural_Networks.h"

using namespace std;

void Read_MNIST(string training_set_images, string training_set_labels, string test_set_images, string test_set_labels, int number_training, int number_test, float **input, float **output) {
	ifstream file(training_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_images + " not found" << endl;
	}

	file.open(training_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_labels + " not found" << endl;
	}

	file.open(test_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_images + " not found" << endl;
	}

	file.open(test_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_labels + " not found" << endl;
	}
}

int main() {
	int batch_size = 128;
	int epochs = 30;
	int number_threads = 6;
	int number_training = 60000;
	int number_test = 10000;
	int number_nodes[] = { 784, 10 };
	int time_step = 28;

	float **x_data = new float*[number_training + number_test];
	float **y_data = new float*[number_training + number_test];
	float **x_train = x_data;
	float **y_train = y_data;
	float **x_test = &x_data[number_training];
	float **y_test = &y_data[number_training];

	double decay = 0.000001;
	double learning_rate = 0.1;
	double momentum = 0.9;

	string path;

	Neural_Networks NN = Neural_Networks();

	cout << "path where MNIST handwritten digits dataset is : ";
	getline(cin, path);

	for (int h = 0; h < number_training + number_test; h++) {
		x_data[h] = new float[number_nodes[0]];
		y_data[h] = new float[number_nodes[1]];
	}
	Read_MNIST(path + "train-images.idx3-ubyte", path + "train-labels.idx1-ubyte", path + "t10k-images.idx3-ubyte", path + "t10k-labels.idx1-ubyte", number_training, number_test, x_data, y_data);
	omp_set_num_threads(number_threads);

	srand(0);

	NN.Add(Layer(time_step, number_nodes[0] / time_step));
	NN.Add(LSTM(time_step, 128))->Initializer(Initializer(1), LSTM::forget)->Direction(+1);
	NN.Add(LSTM(time_step, 128))->Initializer(Initializer(1), LSTM::forget)->Direction(-1);
	NN.Add(Layer(2, 128));
	NN.Add(Layer(1, 256));
	NN.Add(number_nodes[1])->Activation(Activation::softmax);

	NN.Connect(1, 0, "W");
	NN.Connect(1, 1, "W,recurrent");
	NN.Connect(2, 0, "W");
	NN.Connect(2, 2, "W,recurrent");
	{
		unordered_multimap<int, int> time_connection;

		time_connection.insert(pair<int, int>(0, time_step - 1));
		NN.Connect(3, 1, "copy", &time_connection);

		time_connection.clear();
		time_connection.insert(pair<int, int>(1, 0));
		NN.Connect(3, 2, "copy", &time_connection);
	}
	NN.Connect(4, 3, "copy");
	NN.Connect(5, 4, "W");

	NN.Compile(Loss::cross_entropy, Optimizer(Nesterov(learning_rate, momentum, decay)));

	for (int e = 0, time = clock(); e < epochs; e++) {
		int score[2] = { 0, };

		float **_input = new float*[batch_size];
		float **output = new float*[batch_size];

		double loss[2] = { NN.Fit(NN.Shuffle(x_train, number_training), NN.Shuffle(y_train, number_training), number_training, batch_size), NN.Evaluate(x_test, y_test, number_test, batch_size) };

		for (int h = 0; h < batch_size; h++) {
			output[h] = new float[number_nodes[1]];
		}
		for (int h = 0, i = 0; i < number_training + number_test; i++) {
			_input[h] = x_data[i];

			if (++h == batch_size || i == number_training + number_test - 1) {
				NN.Predict(_input, output, h);

				for (int argmax, index = i - h + 1; --h >= 0;) {
					double max = 0;

					for (int j = 0; j < number_nodes[1]; j++) {
						if (j == 0 || max < output[h][j]) {
							max = output[h][argmax = j];
						}
					}
					score[(index + h < number_training) ? (0) : (1)] += (int)y_data[index + h][argmax];
				}
				h = 0;
			}
		}
		printf("loss: %.4f / %.4f	accuracy: %.4f / %.4f	step %d  %.2f sec\n", loss[0], loss[1], 1.0 * score[0] / number_training, 1.0 * score[1] / number_test, e + 1, (double)(clock() - time) / CLOCKS_PER_SEC);

		for (int h = 0; h < batch_size; h++) {
			delete[] output[h];
		}
		delete[] _input;
		delete[] output;
	}

	for (int i = 0; i < number_training + number_test; i++) {
		delete[] x_data[i];
		delete[] y_data[i];
	}
	delete[] x_data;
	delete[] y_data;
}
<|endoftext|>