<commit_before>/*
 * This File is part of Davix, The IO library for HTTP based protocols
 * Copyright (C) CERN 2019
 * Author: Georgios Bitzes <georgios.bitzes@cern.ch>
 *
 * 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.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
 *
*/

#include "ContentProvider.hpp"
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

namespace Davix {

//------------------------------------------------------------------------------
// Empty constructor
//------------------------------------------------------------------------------
ContentProvider::ContentProvider() {
  _errc = 0;
}

//------------------------------------------------------------------------------
// Is the object ok?
//------------------------------------------------------------------------------
bool ContentProvider::ok() const {
  return (_errc == 0);
}

//------------------------------------------------------------------------------
// Has there been an error?
//------------------------------------------------------------------------------
int ContentProvider::getErrc() const {
  return _errc;
}

//------------------------------------------------------------------------------
// Get error message
//------------------------------------------------------------------------------
std::string ContentProvider::getError() const {
  return _errMsg;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
BufferContentProvider::BufferContentProvider(const char* buf, size_t count)
: _buffer(buf), _count(count), _pos(0) {}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t BufferContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(_pos >= _count) {
    // EOF
    return 0;
  }

  size_t bytesToGive = requestedBytes;
  if(_pos + bytesToGive > _count) {
    // Asked for more bytes than we have, just give all remaining ones
    bytesToGive = _count - _pos;
  }

  ::memcpy(target, _buffer + _pos, bytesToGive);
  _pos += bytesToGive;
  return bytesToGive;
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool BufferContentProvider::rewind() {
  _pos = 0;
  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t BufferContentProvider::getSize() {
  return _count;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
OwnedBufferContentProvider::OwnedBufferContentProvider(const char* buf, size_t count)
: _provider(NULL, 0) {

  _contents.resize(count);
  ::memcpy( (void*) _contents.data(), buf, count);
  _provider = BufferContentProvider(_contents.c_str(), _contents.size());
}

//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
OwnedBufferContentProvider::OwnedBufferContentProvider(const std::string &str)
: OwnedBufferContentProvider(str.c_str(), str.size()) {}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t OwnedBufferContentProvider::pullBytes(char* target, size_t requestedBytes) {
  return _provider.pullBytes(target, requestedBytes);
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool OwnedBufferContentProvider::rewind() {
  return _provider.rewind();
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t OwnedBufferContentProvider::getSize() {
  return _provider.getSize();
}

//------------------------------------------------------------------------------
// FdContentProvider constructor
//------------------------------------------------------------------------------
FdContentProvider::FdContentProvider(int fd) : _fd(fd) {
  _fd_size = ::lseek(_fd, 0, SEEK_END);

  if(_fd_size == -1) {
    _errc = errno;
    _errMsg = strerror(_errc);
  }
  else {
    rewind();
  }
}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t FdContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(!ok()) {
    return - _errc;
  }

  while(true) {
    ssize_t retval = ::read(_fd, target, requestedBytes);

    if(retval >= 0) {
      // No errors
      return retval;
    }
    else if(retval == -1 && errno == EINTR) {
      // Interrupted by a signal... retry...
      continue;
    }
    else {
      // Error
      _errc = errno;
      _errMsg = strerror(_errc);
      return - _errc;
    }
  }
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool FdContentProvider::rewind() {
  if(!ok()) {
    return false;
  }

  off_t retval = ::lseek(_fd, 0, SEEK_SET);
  if(retval == -1) {
    _errc = errno;
    _errMsg = strerror(_errc);
    return false;
  }

  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t FdContentProvider::getSize() {
  return _fd_size;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
CallbackContentProvider::CallbackContentProvider(HttpBodyProvider provider, dav_size_t len,
  void *udata) : _provider(provider), _len(len), _udata(udata) {}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t CallbackContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(!ok()) {
    return - _errc;
  }

  if(requestedBytes == 0) {
    return 0;
  }

  ssize_t retval = _provider(_udata, target, requestedBytes);
  if(retval < 0) {
    _errc = -retval;
    _errMsg = strerror(_errc);
    return -_errc;
  }

  return retval;
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool CallbackContentProvider::rewind() {
  if(!ok()) {
    return false;
  }

  _provider(_udata, 0, 0);
  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t CallbackContentProvider::getSize() {
  return _len;
}

}
<commit_msg>Fix compilation on SLC6, don't use delegating construction<commit_after>/*
 * This File is part of Davix, The IO library for HTTP based protocols
 * Copyright (C) CERN 2019
 * Author: Georgios Bitzes <georgios.bitzes@cern.ch>
 *
 * 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.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
 *
*/

#include "ContentProvider.hpp"
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

namespace Davix {

//------------------------------------------------------------------------------
// Empty constructor
//------------------------------------------------------------------------------
ContentProvider::ContentProvider() {
  _errc = 0;
}

//------------------------------------------------------------------------------
// Is the object ok?
//------------------------------------------------------------------------------
bool ContentProvider::ok() const {
  return (_errc == 0);
}

//------------------------------------------------------------------------------
// Has there been an error?
//------------------------------------------------------------------------------
int ContentProvider::getErrc() const {
  return _errc;
}

//------------------------------------------------------------------------------
// Get error message
//------------------------------------------------------------------------------
std::string ContentProvider::getError() const {
  return _errMsg;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
BufferContentProvider::BufferContentProvider(const char* buf, size_t count)
: _buffer(buf), _count(count), _pos(0) {}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t BufferContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(_pos >= _count) {
    // EOF
    return 0;
  }

  size_t bytesToGive = requestedBytes;
  if(_pos + bytesToGive > _count) {
    // Asked for more bytes than we have, just give all remaining ones
    bytesToGive = _count - _pos;
  }

  ::memcpy(target, _buffer + _pos, bytesToGive);
  _pos += bytesToGive;
  return bytesToGive;
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool BufferContentProvider::rewind() {
  _pos = 0;
  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t BufferContentProvider::getSize() {
  return _count;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
OwnedBufferContentProvider::OwnedBufferContentProvider(const char* buf, size_t count)
: _provider(NULL, 0) {

  _contents.resize(count);
  ::memcpy( (void*) _contents.data(), buf, count);
  _provider = BufferContentProvider(_contents.c_str(), _contents.size());
}

//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
OwnedBufferContentProvider::OwnedBufferContentProvider(const std::string &str)
: _provider(NULL, 0) {

  _contents = str;
  _provider = BufferContentProvider(_contents.c_str(), _contents.size());
}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t OwnedBufferContentProvider::pullBytes(char* target, size_t requestedBytes) {
  return _provider.pullBytes(target, requestedBytes);
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool OwnedBufferContentProvider::rewind() {
  return _provider.rewind();
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t OwnedBufferContentProvider::getSize() {
  return _provider.getSize();
}

//------------------------------------------------------------------------------
// FdContentProvider constructor
//------------------------------------------------------------------------------
FdContentProvider::FdContentProvider(int fd) : _fd(fd) {
  _fd_size = ::lseek(_fd, 0, SEEK_END);

  if(_fd_size == -1) {
    _errc = errno;
    _errMsg = strerror(_errc);
  }
  else {
    rewind();
  }
}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t FdContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(!ok()) {
    return - _errc;
  }

  while(true) {
    ssize_t retval = ::read(_fd, target, requestedBytes);

    if(retval >= 0) {
      // No errors
      return retval;
    }
    else if(retval == -1 && errno == EINTR) {
      // Interrupted by a signal... retry...
      continue;
    }
    else {
      // Error
      _errc = errno;
      _errMsg = strerror(_errc);
      return - _errc;
    }
  }
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool FdContentProvider::rewind() {
  if(!ok()) {
    return false;
  }

  off_t retval = ::lseek(_fd, 0, SEEK_SET);
  if(retval == -1) {
    _errc = errno;
    _errMsg = strerror(_errc);
    return false;
  }

  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t FdContentProvider::getSize() {
  return _fd_size;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
CallbackContentProvider::CallbackContentProvider(HttpBodyProvider provider, dav_size_t len,
  void *udata) : _provider(provider), _len(len), _udata(udata) {}

//------------------------------------------------------------------------------
// pullBytes implementation.
//------------------------------------------------------------------------------
ssize_t CallbackContentProvider::pullBytes(char* target, size_t requestedBytes) {
  if(!ok()) {
    return - _errc;
  }

  if(requestedBytes == 0) {
    return 0;
  }

  ssize_t retval = _provider(_udata, target, requestedBytes);
  if(retval < 0) {
    _errc = -retval;
    _errMsg = strerror(_errc);
    return -_errc;
  }

  return retval;
}

//------------------------------------------------------------------------------
// Rewind implementation.
//------------------------------------------------------------------------------
bool CallbackContentProvider::rewind() {
  if(!ok()) {
    return false;
  }

  _provider(_udata, 0, 0);
  return true;
}

//------------------------------------------------------------------------------
// getSize implementation.
//------------------------------------------------------------------------------
ssize_t CallbackContentProvider::getSize() {
  return _len;
}

}
<|endoftext|>


<commit_before>#include <GLUL/G2D/Line.h>
#include <GLUL/G2D/Quad.h>

#include <glm/geometric.hpp>

namespace GLUL {

    namespace G2D {

        Line::Line(unsigned int thickness) {
            setThickness(thickness);
        }

        Line::Line(const Point& point1, const Point& point2, unsigned int thickness) {
            points[0] = point1;
            points[1] = point2;

            setThickness(thickness);
        }

        Line::Line(const glm::vec2& position1, const glm::vec2& position2, unsigned int thickness)
            : Line(Point { position1 }, Point { position2 }, thickness) { }

        void Line::setColor(const glm::vec4& color) {
            for(auto& point : points)
                point.setColor(color);
        }

        const glm::vec4& Line::getColor() const {
            return points[0].getColor();
        }

        void Line::setThickness(unsigned int thickness) {
            for(auto& point : points)
                point.size = thickness;
        }

        void Line::_pushToBatch(GeometryBatch& geometryBatch) const {
            Quad quad;
            Point point1 = points[0];
            Point point2 = points[1];

            {
                // Make sure point1 is on left of (or exactly under) point2
                const glm::vec2& pos1 = point1.getPosition();
                const glm::vec2& pos2 = point2.getPosition();

                if(pos1.x > pos2.x || (pos1.x == pos2.x && pos1.y > pos2.y))
                    std::swap(point1, point2);
            }

            std::array<Point, 4> qPoints = { point1, point2, point2, point1 };
            glm::vec2 lineVector = glm::normalize(point2.getPosition() - point1.getPosition());
            glm::vec2 perpendicularLineVector = { -lineVector.y, lineVector.x };

            qPoints[0].setPosition(point1.getPosition() - (perpendicularLineVector * static_cast<float>(point1.size / 2)));
            qPoints[1].setPosition(point2.getPosition() - (perpendicularLineVector * static_cast<float>(point2.size / 2)));
            qPoints[2].setPosition(point2.getPosition() + (perpendicularLineVector * static_cast<float>(point2.size - (point2.size / 2))));
            qPoints[3].setPosition(point1.getPosition() + (perpendicularLineVector * static_cast<float>(point1.size - (point1.size / 2))));

            quad.points = qPoints;
            geometryBatch.addPrimitive(quad);
        }

    }

}
<commit_msg>Fixed clang's warning.<commit_after>#include <GLUL/G2D/Line.h>
#include <GLUL/G2D/Quad.h>

#include <glm/geometric.hpp>

namespace GLUL {

    namespace G2D {

        Line::Line(unsigned int thickness) {
            setThickness(thickness);
        }

        Line::Line(const Point& point1, const Point& point2, unsigned int thickness) {
            points[0] = point1;
            points[1] = point2;

            setThickness(thickness);
        }

        Line::Line(const glm::vec2& position1, const glm::vec2& position2, unsigned int thickness)
            : Line(Point { position1 }, Point { position2 }, thickness) { }

        void Line::setColor(const glm::vec4& color) {
            for(auto& point : points)
                point.setColor(color);
        }

        const glm::vec4& Line::getColor() const {
            return points[0].getColor();
        }

        void Line::setThickness(unsigned int thickness) {
            for(auto& point : points)
                point.size = thickness;
        }

        void Line::_pushToBatch(GeometryBatch& geometryBatch) const {
            Quad quad;
            Point point1 = points[0];
            Point point2 = points[1];

            {
                // Make sure point1 is on left of (or exactly under) point2
                const glm::vec2& pos1 = point1.getPosition();
                const glm::vec2& pos2 = point2.getPosition();

                if(pos1.x > pos2.x || (pos1.x == pos2.x && pos1.y > pos2.y))
                    std::swap(point1, point2);
            }

            std::array<Point, 4> qPoints { { point1, point2, point2, point1 } };
            glm::vec2 lineVector = glm::normalize(point2.getPosition() - point1.getPosition());
            glm::vec2 perpendicularLineVector = { -lineVector.y, lineVector.x };

            qPoints[0].setPosition(point1.getPosition() - (perpendicularLineVector * static_cast<float>(point1.size / 2)));
            qPoints[1].setPosition(point2.getPosition() - (perpendicularLineVector * static_cast<float>(point2.size / 2)));
            qPoints[2].setPosition(point2.getPosition() + (perpendicularLineVector * static_cast<float>(point2.size - (point2.size / 2))));
            qPoints[3].setPosition(point1.getPosition() + (perpendicularLineVector * static_cast<float>(point1.size - (point1.size / 2))));

            quad.points = qPoints;
            geometryBatch.addPrimitive(quad);
        }

    }

}
<|endoftext|>


<commit_before>
#pragma once

#include "ChatEnums.hpp"
#include "NodeClient.hpp"
#include "SQLite3.hpp"
#include "easylogging++.h"

class ChatAvatar;
class ChatAvatarService;
class ChatRoom;
class ChatRoomService;
class GatewayNode;
class PersistentMessageService;
class UdpConnection;

struct PersistentHeader;

struct ReqSetAvatarAttributes;
struct ReqGetAnyAvatar;

class GatewayClient : public NodeClient {
public:
    GatewayClient(UdpConnection* connection, GatewayNode* node);
    virtual ~GatewayClient();

    GatewayNode* GetNode() { return node_; }

    void SendFriendLoginUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar);
    void SendFriendLoginUpdates(const ChatAvatar* avatar);
    void SendFriendLogoutUpdates(const ChatAvatar* avatar);
    void SendDestroyRoomUpdate(const ChatAvatar* srcAvatar, uint32_t roomId, std::vector<std::u16string> targets);
    void SendInstantMessageUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const std::u16string& message, const std::u16string& oob);
    void SendRoomMessageUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room, uint32_t messageId, const std::u16string& message, const std::u16string& oob);
    void SendEnterRoomUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room);
    void SendLeaveRoomUpdate(const std::vector<std::u16string>& addresses, uint32_t srcAvatarId, uint32_t roomId);
    void SendPersistentMessageUpdate(const ChatAvatar* destAvatar, const PersistentHeader& header);
    void SendKickAvatarUpdate(const std::vector<std::u16string>& addresses, const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const ChatRoom* room);

private:
    void OnIncoming(std::istringstream& istream) override;

    template<typename HandlerT, typename StreamT>
    void HandleIncomingMessage(StreamT& istream) {
        typedef typename HandlerT::RequestType RequestT;
        typedef typename HandlerT::ResponseType ResponseT;

        RequestT request;
        read(istream, request);
        ResponseT response(request.track);

        try {
            HandlerT(this, request, response);
        } catch (const ChatResultException& e) {
            response.result = e.code;
            LOG(ERROR) << "ChatAPI Error: [" << ToString(e.code) << "] " << e.message;
        } catch (const SQLite3Exception& e) {
            response.result = ChatResultCode::DATABASE;
            LOG(ERROR) << "Database Error: [" << e.code << "] " << e.message;
        }

        Send(response);
    }

    GatewayNode* node_;
    ChatAvatarService* avatarService_;
    ChatRoomService* roomService_;
    PersistentMessageService* messageService_;
};
<commit_msg>Updated this to say just that it is an exceptional situation as not all exceptions thrown are errors, they are just exceptional situations (ie, not the success path).<commit_after>
#pragma once

#include "ChatEnums.hpp"
#include "NodeClient.hpp"
#include "SQLite3.hpp"
#include "easylogging++.h"

class ChatAvatar;
class ChatAvatarService;
class ChatRoom;
class ChatRoomService;
class GatewayNode;
class PersistentMessageService;
class UdpConnection;

struct PersistentHeader;

struct ReqSetAvatarAttributes;
struct ReqGetAnyAvatar;

class GatewayClient : public NodeClient {
public:
    GatewayClient(UdpConnection* connection, GatewayNode* node);
    virtual ~GatewayClient();

    GatewayNode* GetNode() { return node_; }

    void SendFriendLoginUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar);
    void SendFriendLoginUpdates(const ChatAvatar* avatar);
    void SendFriendLogoutUpdates(const ChatAvatar* avatar);
    void SendDestroyRoomUpdate(const ChatAvatar* srcAvatar, uint32_t roomId, std::vector<std::u16string> targets);
    void SendInstantMessageUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const std::u16string& message, const std::u16string& oob);
    void SendRoomMessageUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room, uint32_t messageId, const std::u16string& message, const std::u16string& oob);
    void SendEnterRoomUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room);
    void SendLeaveRoomUpdate(const std::vector<std::u16string>& addresses, uint32_t srcAvatarId, uint32_t roomId);
    void SendPersistentMessageUpdate(const ChatAvatar* destAvatar, const PersistentHeader& header);
    void SendKickAvatarUpdate(const std::vector<std::u16string>& addresses, const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const ChatRoom* room);

private:
    void OnIncoming(std::istringstream& istream) override;

    template<typename HandlerT, typename StreamT>
    void HandleIncomingMessage(StreamT& istream) {
        typedef typename HandlerT::RequestType RequestT;
        typedef typename HandlerT::ResponseType ResponseT;

        RequestT request;
        read(istream, request);
        ResponseT response(request.track);

        try {
            HandlerT(this, request, response);
        } catch (const ChatResultException& e) {
            response.result = e.code;
            LOG(ERROR) << "ChatAPI Result Exception: [" << ToString(e.code) << "] " << e.message;
        } catch (const SQLite3Exception& e) {
            response.result = ChatResultCode::DATABASE;
            LOG(ERROR) << "Database Error: [" << e.code << "] " << e.message;
        }

        Send(response);
    }

    GatewayNode* node_;
    ChatAvatarService* avatarService_;
    ChatRoomService* roomService_;
    PersistentMessageService* messageService_;
};
<|endoftext|>


<commit_before>/*
 *   Copyright (C) 2009 Gregory Haynes <greg@greghaynes.net>
 *
 *   This program 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 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "documentviewinternal.h"
#include "documentview.h"
#include "documentcontroller.h"
#include "document.h"
#include "renderer.h"

#include <QPaintEvent>
#include <QPainter>
#include <QResizeEvent>
#include <QWheelEvent>
#include <QFontMetrics>
#include <QTimer>
#include <QCursor>
#include <QMouseEvent>
#include <QRect>

#include <QDebug>

#include "documentviewinternal.moc"

namespace QSourceView
{

class TextCursor
	: public DocumentPosition
{

	public:
		TextCursor();

		bool is_visible;

};

TextCursor::TextCursor()
	: DocumentPosition(0, 0)
	, is_visible(false)
{
}

DocumentViewInternal::DocumentViewInternal(DocumentView &parentView,
	Renderer &renderer)
	: QWidget(&parentView)
	, m_view(&parentView)
	, m_renderer(&renderer)
	, m_startX(0)
	, m_startY(0)
	, m_caret(new TextCursor)
{
	setAttribute(Qt::WA_OpaquePaintEvent);
	setFocusPolicy(Qt::ClickFocus);
	setCursor(Qt::IBeamCursor);
	QTimer *caretTimer = new QTimer(this);
	caretTimer->setSingleShot(false);
	connect(caretTimer, SIGNAL(timeout()),
		this, SLOT(toggleCaretVisibility()));
	caretTimer->start(500);
}

int DocumentViewInternal::startX() const
{
	return m_startX;
}

int DocumentViewInternal::startY() const
{
	return m_startY;
}

int DocumentViewInternal::endY() const
{
	return m_view->document().lineCount() * fontMetrics().height();
}

const DocumentPosition &DocumentViewInternal::caretPosition() const
{
	return *m_caret;
}

void DocumentViewInternal::setCaretPosition(const DocumentPosition &pos)
{
	m_caret->setLine(pos.line());
	m_caret->setColumn(pos.column());
}

void DocumentViewInternal::setStartX(int x)
{
	if(x < 0)
		m_startX = 0;
	else
		m_startX = x;
	update();
}

void DocumentViewInternal::setStartY(int y)
{
	if(y < 0)
		m_startY = 0;
	else if(y > endY() - height())
		m_startY = endY() - height();
	else
		m_startY = y;
	update();
	emit(startYChanged(m_startY));
}

void DocumentViewInternal::paintEvent(QPaintEvent *event)
{
	QRect rect = event->rect();
	QPainter paint(this);

	unsigned int fontHeight = fontMetrics().height();
	int lineYStart = (startY() % fontHeight);
	int lineNumStart = lineAt(startY());
	int numLines = (height() / fontHeight) + 2;
	int i;
	// Paint the text
	Document *doc = &m_view->document();
	for(i = 0;i < numLines;i++,lineNumStart++)
	{
		QRect bound = QRect(0, (i*fontHeight) - lineYStart, rect.width(), fontHeight);
		paint.fillRect(bound, Qt::white);
		paint.drawText(bound, doc->text(lineNumStart));
	}

	paintCaret(paint);
}

void DocumentViewInternal::resizeEvent(QResizeEvent *event)
{
	emit(sizeChanged(event->size().width(), event->size().height()));
}

void DocumentViewInternal::keyPressEvent(QKeyEvent *event)
{
	m_view->controller().keyPressEvent(event);
}

void DocumentViewInternal::mousePressEvent(QMouseEvent *event)
{
	unsigned int pressLine = lineAt(event->y()+startY());
	unsigned int pressColumn;
	if(pressLine >= m_view->document().lineCount())
	{
		pressLine = m_view->document().lineCount()-1;
	}

	QString line = m_view->document().text(pressLine);
	// Find the column were at
	int i;
	for(i = 0;i < line.size();i++)
	{
		if(event->x() <= fontMetrics().width(line.left(i+1)))
			break;
	}
	pressColumn = i;
	m_caret->setLine(pressLine);
	m_caret->setColumn(pressColumn);
	m_caret->is_visible = true;
	update();
}

void DocumentViewInternal::wheelEvent(QWheelEvent *event)
{
	event->ignore();
	if(event->orientation() == Qt::Vertical)
	{
		setStartY(startY() - (event->delta() / 30));
	}
}

void DocumentViewInternal::paintCaret(QPainter &paint)
{
	unsigned int startLine = lineAt(startY());
	if(startLine > m_caret->line()
	   || lineAt(startY()+height()) <= m_caret->line())
		return;
	// Text printed infront of cursor
	QString prevline = m_view->document().text(m_caret->line()).left(m_caret->column());
	int xStart = fontMetrics().width(prevline);
	QRect bound = QRect(xStart, (fontMetrics().height()*m_caret->line()) - startY(), 1, fontMetrics().height());

	if(m_caret->is_visible)
	{
		if(m_caret->column() >= (unsigned int)m_view->document().text(m_caret->line()).size())
			paint.fillRect(bound, Qt::white);
		else
			paint.drawText(bound, QString(m_view->document().text(m_caret->line())[m_caret->column()]));
	}
	else
	{
		paint.fillRect(bound, Qt::black);
	}
}

void DocumentViewInternal::toggleCaretVisibility()
{
	m_caret->is_visible = !m_caret->is_visible;
	update();
}

unsigned int DocumentViewInternal::lineAt(unsigned int x) const
{
	return x / fontMetrics().height();
}

}
<commit_msg>Fixed vertical wheel event bug on documents less than window size<commit_after>/*
 *   Copyright (C) 2009 Gregory Haynes <greg@greghaynes.net>
 *
 *   This program 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 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "documentviewinternal.h"
#include "documentview.h"
#include "documentcontroller.h"
#include "document.h"
#include "renderer.h"

#include <QPaintEvent>
#include <QPainter>
#include <QResizeEvent>
#include <QWheelEvent>
#include <QFontMetrics>
#include <QTimer>
#include <QCursor>
#include <QMouseEvent>
#include <QRect>

#include <QDebug>

#include "documentviewinternal.moc"

namespace QSourceView
{

class TextCursor
	: public DocumentPosition
{

	public:
		TextCursor();

		bool is_visible;

};

TextCursor::TextCursor()
	: DocumentPosition(0, 0)
	, is_visible(false)
{
}

DocumentViewInternal::DocumentViewInternal(DocumentView &parentView,
	Renderer &renderer)
	: QWidget(&parentView)
	, m_view(&parentView)
	, m_renderer(&renderer)
	, m_startX(0)
	, m_startY(0)
	, m_caret(new TextCursor)
{
	setAttribute(Qt::WA_OpaquePaintEvent);
	setFocusPolicy(Qt::ClickFocus);
	setCursor(Qt::IBeamCursor);
	QTimer *caretTimer = new QTimer(this);
	caretTimer->setSingleShot(false);
	connect(caretTimer, SIGNAL(timeout()),
		this, SLOT(toggleCaretVisibility()));
	caretTimer->start(500);
}

int DocumentViewInternal::startX() const
{
	return m_startX;
}

int DocumentViewInternal::startY() const
{
	return m_startY;
}

int DocumentViewInternal::endY() const
{
	return m_view->document().lineCount() * fontMetrics().height();
}

const DocumentPosition &DocumentViewInternal::caretPosition() const
{
	return *m_caret;
}

void DocumentViewInternal::setCaretPosition(const DocumentPosition &pos)
{
	if(pos.line() < m_view->document().lineCount())
		m_caret->setLine(pos.line());
	if(pos.column() < m_view->document().text(caretPosition().line()).size())
		m_caret->setColumn(pos.column());
}

void DocumentViewInternal::setStartX(int x)
{
	if(x < 0)
		m_startX = 0;
	else
		m_startX = x;
	update();
}

void DocumentViewInternal::setStartY(int y)
{
	qDebug() << y;
	if(y < 0)
		m_startY = 0;
	else if(y > (endY() - height()))
	{
		m_startY = endY() - height();
		if(m_startY < 0)
			m_startY = 0;
	}
	else
		m_startY = y;
	update();
	emit(startYChanged(m_startY));
}

void DocumentViewInternal::paintEvent(QPaintEvent *event)
{
	QRect rect = event->rect();
	QPainter paint(this);

	unsigned int fontHeight = fontMetrics().height();
	int lineYStart = (startY() % fontHeight);
	int lineNumStart = lineAt(startY());
	int numLines = (height() / fontHeight) + 2;
	int i;
	// Paint the text
	Document *doc = &m_view->document();
	for(i = 0;i < numLines;i++,lineNumStart++)
	{
		QRect bound = QRect(0, (i*fontHeight) - lineYStart, rect.width(), fontHeight);
		paint.fillRect(bound, Qt::white);
		paint.drawText(bound, doc->text(lineNumStart));
	}

	paintCaret(paint);
}

void DocumentViewInternal::resizeEvent(QResizeEvent *event)
{
	emit(sizeChanged(event->size().width(), event->size().height()));
}

void DocumentViewInternal::keyPressEvent(QKeyEvent *event)
{
	m_view->controller().keyPressEvent(event);
}

void DocumentViewInternal::mousePressEvent(QMouseEvent *event)
{
	unsigned int pressLine = lineAt(event->y()+startY());
	unsigned int pressColumn;
	if(pressLine >= m_view->document().lineCount())
	{
		pressLine = m_view->document().lineCount()-1;
	}

	QString line = m_view->document().text(pressLine);
	// Find the column were at
	int i;
	for(i = 0;i < line.size();i++)
	{
		if(event->x() <= fontMetrics().width(line.left(i+1)))
			break;
	}
	pressColumn = i;
	m_caret->setLine(pressLine);
	m_caret->setColumn(pressColumn);
	m_caret->is_visible = true;
	update();
}

void DocumentViewInternal::wheelEvent(QWheelEvent *event)
{
	event->ignore();
	if(event->orientation() == Qt::Vertical)
	{
		setStartY(startY() - (event->delta() / 30));
	}
}

void DocumentViewInternal::paintCaret(QPainter &paint)
{
	unsigned int startLine = lineAt(startY());
	if(startLine > m_caret->line()
	   || lineAt(startY()+height()) <= m_caret->line())
		return;
	// Text printed infront of cursor
	QString prevline = m_view->document().text(m_caret->line()).left(m_caret->column());
	int xStart = fontMetrics().width(prevline);
	QRect bound = QRect(xStart, (fontMetrics().height()*m_caret->line()) - startY(), 1, fontMetrics().height());

	if(m_caret->is_visible)
	{
		if(m_caret->column() >= (unsigned int)m_view->document().text(m_caret->line()).size())
			paint.fillRect(bound, Qt::white);
		else
			paint.drawText(bound, QString(m_view->document().text(m_caret->line())[m_caret->column()]));
	}
	else
	{
		paint.fillRect(bound, Qt::black);
	}
}

void DocumentViewInternal::toggleCaretVisibility()
{
	m_caret->is_visible = !m_caret->is_visible;
	update();
}

unsigned int DocumentViewInternal::lineAt(unsigned int x) const
{
	return x / fontMetrics().height();
}

}
<|endoftext|>


<commit_before>#include "MusicMessages.h"
#include <dmusic/Structs.h>
#include <dmusic/PlayingContext.h>
#include <cassert>
#include <cstdlib>
#include <cmath>

using namespace DirectMusic;

void MusicMessage::changeTempo(PlayingContext& ctx, double tempo) {
    ctx.m_tempo = tempo;
}

std::shared_ptr<InstrumentPlayer> MusicMessage::createInstrument(PlayingContext& ctx,
    std::uint8_t bank_lo, std::uint8_t bank_hi, std::uint8_t patch,
    const DirectMusic::DLS::DownloadableSound& dls, float volume, float pan) {
    return ctx.m_instrumentFactory(bank_lo, bank_hi, patch, dls, ctx.m_sampleRate, ctx.m_audioChannels, volume, pan);
}

void MusicMessage::setInstrument(PlayingContext& ctx, std::uint32_t channel, std::shared_ptr<InstrumentPlayer> instr) {
    ctx.m_performanceChannels[channel] = instr;
}

// From the Microsoft DX8 SDK docs
static int StoredRangeToActualRange(std::uint8_t bRange) {
    int nResult = 0;
    if (0 <= bRange && bRange <= 190) {
        nResult = bRange;
    } else if (191 <= bRange && bRange <= 212) {
        nResult = ((bRange - 190) * 5) + 190;
    } else if (213 <= bRange && bRange <= 232) {
        nResult = ((bRange - 212) * 10) + 300;
    } else // bRange > 232
    {
        nResult = ((bRange - 232) * 50) + 500;
    }
    return nResult;
}

enum class Subdivision {
    Grid,
    Beat,
    Measure
};

// Returns the nearest subdivision starting from time 'musicTime' according to 'timeSignature'
static std::uint32_t getNextGridSubdivision(std::uint32_t musicTime, Subdivision subd, DMUS_IO_TIMESIG timeSignature) {
    std::uint32_t divisionCoeff;
    if (timeSignature.bBeat == 0) {
        divisionCoeff = PlayingContext::PulsesPerQuarterNote / 64;
    } else if (timeSignature.bBeat > 4) {
        divisionCoeff = (PlayingContext::PulsesPerQuarterNote * 4) / timeSignature.bBeat;
    } else {
        divisionCoeff = PlayingContext::PulsesPerQuarterNote * (4 / timeSignature.bBeat);
    }
    divisionCoeff *= timeSignature.wGridsPerBeat;

    std::uint32_t nextGridDivision = (std::uint32_t)(ceil((double)musicTime / divisionCoeff));
    std::uint32_t nextBeatDivision = nextGridDivision * timeSignature.wGridsPerBeat;
    std::uint32_t nextMeasureDivision = nextBeatDivision * timeSignature.bBeatsPerMeasure;

    switch (subd) {
    case Subdivision::Beat:
        return nextBeatDivision * divisionCoeff;
    case Subdivision::Grid:
        return nextGridDivision * divisionCoeff;
    case Subdivision::Measure:
        return nextMeasureDivision * divisionCoeff;
    }
}

static std::uint32_t getMeasureLength(DMUS_IO_TIMESIG timeSignature) {
    return (timeSignature.bBeatsPerMeasure * PlayingContext::PulsesPerQuarterNote * 4) / timeSignature.bBeat;
}

// From the Microsoft DX8 SDK docs
static std::uint32_t getMusicOffset(std::uint32_t mtGridStart, std::int16_t nTimeOffset, DMUS_IO_TIMESIG TimeSig) {
    const std::uint32_t DMUS_PPQ = PlayingContext::PulsesPerQuarterNote;
    return nTimeOffset +
        (
        (mtGridStart / TimeSig.wGridsPerBeat) * ((DMUS_PPQ * 4) / TimeSig.bBeat)
            +
            (mtGridStart % TimeSig.wGridsPerBeat) * (((DMUS_PPQ * 4) / TimeSig.bBeat) / TimeSig.wGridsPerBeat)
            );
}

// Returns true if the specified degree is present in a certain scale, and puts the distance from the
// root note into offset. If not present, returns false.
static bool getOffsetFromScale(std::uint8_t degree, std::uint32_t scale, std::uint8_t* offset) {
    assert(offset != nullptr);

    // FIXME: There is probably a faster way to do this
    std::vector<int> degrees;
    for (int i = 0; i < 24; i++) {
        if (scale & (0x00000001 << i)) {
            degrees.push_back(i);
        }
    }

    if (degree < degrees.size()) {
        *offset = degrees[degree];
        return true;
    } else {
        *offset = degrees[degrees.size() - 1];
        return false;
    }
}

static bool MusicValueToMIDI(std::uint32_t chord, const std::vector<DMUS_IO_SUBCHORD>& subchords, DMUS_IO_STYLENOTE note, DMUS_IO_STYLEPART part, std::uint8_t* value) {
    assert(value != nullptr);

    if (note.bPlayModeFlags == DMUS_PLAYMODE_FIXED) {
        // In the original Gothic sountrack this is not used, but it might be useful for modding purposes
        *value = (std::uint8_t)(note.wMusicValue);
        return true;
    }

    assert(note.bPlayModeFlags == DMUS_PLAYMODE_CHORD_ROOT | DMUS_PLAYMODE_CHORD_INTERVALS | DMUS_PLAYMODE_SCALE_INTERVALS);
    // TODO: The subchord level should be obtained from the part reference,
    // but in Gothic's soundtrack it's always set to the first level.

    /*
    A chord (or a scale) has the following structure: the first 8 bits represent the
    root of the chord (scale), with the standard MIDI notation:
    0 -> C
    1 -> C#
    ....
    23 -> B

    The following 24 bits represent which notes are present in the chord, with each
    bit meaning a semitone from the root (the LSB). Example taken from G2:

    chord = 0x00AB5AB5
    0   0   A   B   5   A   B   5
    00000000101010110101101010110101
    |-------|-|-|-||-|-||-|-|-||-|-|
    C    W W W HW W HW W W HW W -----> C Major scale
    */
    DMUS_IO_SUBCHORD subchord = subchords[0];

    /* Had to dig hard for this one: https://msdn.microsoft.com/en-us/library/ms898477.aspx
    Here is how to interpret wMusicValue:
    First nibble: octave (from -2 to 14 (??? I guess they meant -2 to 13) )
    Second nibble: chord tone (0 to 15)
    Third nibble: scale offset (0 to 15)
    Fourth nibble: accidentals (-8 to 7)
    */

    int octave = ((note.wMusicValue & 0xF000) >> 12);
    int chordTone = ((note.wMusicValue & 0x0F00) >> 8);
    int scaleTone = ((note.wMusicValue & 0x00F0) >> 4);

    // Explanation: the accidentals are represented as a two's complement 4bit value.
    // We first take only the last four bits from the word, then we shift it left
    // while keeping it unsigned, so that when we convert it into a signed byte
    // it has the correct sign. Then we divide by 16 to simulate an arithmetic
    // right shift of 4, to bring the value back into the correct range
    int accidentals = (std::int8_t)(note.wMusicValue & 0x000F);
    if (accidentals > 7) { accidentals = (accidentals - 16); }

    int noteValue = ((chord & 0xFF000000) >> 24) + 12 * octave;
    std::uint8_t chordOffset = 0;
    std::uint8_t scaleOffset = 0;
    if (getOffsetFromScale(chordTone, subchord.dwChordPattern, &chordOffset)) {
        noteValue += chordOffset;
        // Is it possible to not find chordTone?
        // } else if (getOffsetFromScale(chordTone, subchord.dwScalePattern, &scaleOffset)) {
        //     noteValue += scaleOffset;
    } else {
        return false;
    }

    // Start scale offset from resolved chordTone.
    // Let's draw an example:
    //      * chordTone is 2, scaleTone is 1
    //      * in normal chord chordTone 2 is resolved to 5th grade in scale
    //      * plus scaleTone 1 it makes 6th grade of the scale
    //      * depending on scale, it might be 8 semitones (minor) or 9 semitones (major)
    // So, scaleOffset depends on chord's grade
    // That works even if chord's grade doesn't fit into scale
    // Chord grade might be off of the scale, so if scaleOffset = 0, don't ruin it
    if (scaleTone && getOffsetFromScale(scaleTone, subchord.dwScalePattern >> chordOffset, &scaleOffset)) {
        noteValue += scaleOffset;
    }

    noteValue += accidentals;
    while (noteValue < 0) {
        noteValue += 12;
    }
    while (noteValue > 127) {
        noteValue -= 12;
    }

    *value = noteValue;
    TRACE_VERBOSE((int)value);
    return true;
}

void MusicMessage::playPattern(PlayingContext& ctx) {
    ctx.m_patternMessageQueue = MessageQueue();
    for (const auto& kvpair : ctx.m_performanceChannels) {
        kvpair.second->allNotesOff();
    }

    if (ctx.m_primarySegment != nullptr && ctx.m_performanceChannels.size() > 0 && ctx.m_subchords.size() > 0) {
        PlayingContext::Pattern pttn;
        if (ctx.m_primarySegment->getRandomPattern(ctx.m_grooveLevel, &pttn)) {
            std::uint32_t patternLength = pttn.header.wNbrMeasures * getMeasureLength(pttn.header.timeSig);
            for (const auto& partTuple: pttn.parts) {
                const auto& partRef = partTuple.first;
                const auto& part = partTuple.second;

                for (const auto& note: part.getNotes()) {
                    std::uint8_t midiNote;
                    std::uint32_t timeStart = getMusicOffset(note.mtGridStart, note.nTimeOffset, part.getHeader().timeSig);
                    if (MusicValueToMIDI(ctx.m_chord, ctx.m_subchords, note, part.getHeader(), &midiNote)) {
                        auto noteOnMessage = std::make_shared<NoteOnMessage>(ctx.m_musicTime + timeStart, midiNote, note.bVelocity, 0, partRef.wLogicalPartID);
                        assert(noteOnMessage != nullptr);
                        ctx.m_patternMessageQueue.push(noteOnMessage);

                        auto noteOffMessage = std::make_shared<NoteOffMessage>(ctx.m_musicTime + timeStart + note.mtDuration, midiNote, partRef.wLogicalPartID);
                        assert(noteOffMessage != nullptr);
                        ctx.m_patternMessageQueue.push(noteOffMessage);
                    }
                }
            }

            auto patternEndMessage = std::make_shared<PatternEndMessage>(ctx.m_musicTime + patternLength);
            ctx.m_patternMessageQueue.push(patternEndMessage);
        }
    }
}

void MusicMessage::setGrooveLevel(PlayingContext& ctx, std::uint8_t level) {
    ctx.m_grooveLevel = level;

    playPattern(ctx);
}

const std::map<std::uint32_t, std::shared_ptr<InstrumentPlayer>>& MusicMessage::getChannels(PlayingContext& ctx) {
    return ctx.m_performanceChannels;
}

void MusicMessage::changeChord(PlayingContext& ctx, std::uint32_t chord, const std::vector<DMUS_IO_SUBCHORD>& subchords) {
    ctx.m_chord = chord;
    ctx.m_subchords = subchords;
}

void MusicMessage::enqueueNextSegment(PlayingContext& ctx) {
    if (ctx.m_primarySegment == nullptr) {
        if (ctx.m_nextSegment != nullptr) {
            ctx.enqueueSegment(ctx.m_nextSegment);
            ctx.m_primarySegment = std::move(ctx.m_nextSegment);
            ctx.m_nextSegment = nullptr;
        }
    } else {
        if (ctx.m_nextSegment != nullptr) {
            ctx.enqueueSegment(ctx.m_nextSegment);
            ctx.m_primarySegment = std::move(ctx.m_nextSegment);
            ctx.m_nextSegment = nullptr;
        } else {
            ctx.enqueueSegment(ctx.m_primarySegment);
        }
    }
}

void TempoChangeMessage::Execute(PlayingContext& ctx) {
    TRACE("Tempo change");
    this->changeTempo(ctx, m_tempo);
}

BandChangeMessage::BandChangeMessage(PlayingContext& ctx, std::uint32_t time, const BandForm& form)
    : MusicMessage(time) {
    for (const auto& instr : form.getInstruments()) {
        const auto& header = instr.getHeader();
        const auto ref = instr.getReference();
        if (ref != nullptr) {
            std::uint8_t bankHi = (header.dwPatch & 0x00FF0000) >> 0x10;
            std::uint8_t bankLo = (header.dwPatch & 0x0000FF00) >> 0x8;
            std::uint8_t patch = (header.dwPatch & 0x000000FF);
            float volume = header.bVolume / 255.0f;
            float pan = ((float)(header.bPan) - 63.0f) / 64.0f;

            auto dls = ctx.loadInstrumentCollection(ref->getFile());

            assert(dls != nullptr);
            instruments[header.dwPChannel] = createInstrument(ctx, bankLo, bankHi, patch, *dls, volume, pan);
        }
    }
}

void BandChangeMessage::Execute(PlayingContext& ctx) {
    TRACE("Band change");
    for (const auto& kvpair : instruments) {
        setInstrument(ctx, kvpair.first, kvpair.second);
    }
}

void GrooveLevelMessage::Execute(PlayingContext& ctx) {
    TRACE("Groove change");
    if (m_range == 0) {
        setGrooveLevel(ctx, m_level);
    } else {
        std::int8_t offset = (std::rand() % m_range) - (m_range / 2);
        setGrooveLevel(ctx, m_level - offset);
    }
}

void ChordMessage::Execute(PlayingContext& ctx) {
    TRACE("Chord change");
    changeChord(ctx, this->m_chord, this->m_subchords);
}

void NoteOnMessage::Execute(PlayingContext& ctx) {
    TRACE_VERBOSE("Note on");
    const auto& channels = getChannels(ctx);
    assert(channels.find(m_channel) != channels.end());
    if (m_velRange == 0) {
        channels.at(m_channel)->noteOn(m_note, m_vel);
    } else {
        std::int8_t offset = (std::rand() % m_velRange) - (m_velRange / 2);
        channels.at(m_channel)->noteOn(m_note, m_vel - offset);
    }
}

void NoteOffMessage::Execute(PlayingContext& ctx) {
    TRACE_VERBOSE("Note off");
    const auto& channels = getChannels(ctx);
    assert(channels.find(m_channel) != channels.end());
    channels.at(m_channel)->noteOff(m_note, 0);
}

void SegmentEndMessage::Execute(PlayingContext& ctx) {
    TRACE("Segment end");
    enqueueNextSegment(ctx);
}

void PatternEndMessage::Execute(PlayingContext& ctx) {
    TRACE("Pattern end");
    playPattern(ctx);
}<commit_msg>Add missing note trace<commit_after>#include "MusicMessages.h"
#include <dmusic/Structs.h>
#include <dmusic/PlayingContext.h>
#include <cassert>
#include <cstdlib>
#include <cmath>

using namespace DirectMusic;

void MusicMessage::changeTempo(PlayingContext& ctx, double tempo) {
    ctx.m_tempo = tempo;
}

std::shared_ptr<InstrumentPlayer> MusicMessage::createInstrument(PlayingContext& ctx,
    std::uint8_t bank_lo, std::uint8_t bank_hi, std::uint8_t patch,
    const DirectMusic::DLS::DownloadableSound& dls, float volume, float pan) {
    return ctx.m_instrumentFactory(bank_lo, bank_hi, patch, dls, ctx.m_sampleRate, ctx.m_audioChannels, volume, pan);
}

void MusicMessage::setInstrument(PlayingContext& ctx, std::uint32_t channel, std::shared_ptr<InstrumentPlayer> instr) {
    ctx.m_performanceChannels[channel] = instr;
}

// From the Microsoft DX8 SDK docs
static int StoredRangeToActualRange(std::uint8_t bRange) {
    int nResult = 0;
    if (0 <= bRange && bRange <= 190) {
        nResult = bRange;
    } else if (191 <= bRange && bRange <= 212) {
        nResult = ((bRange - 190) * 5) + 190;
    } else if (213 <= bRange && bRange <= 232) {
        nResult = ((bRange - 212) * 10) + 300;
    } else // bRange > 232
    {
        nResult = ((bRange - 232) * 50) + 500;
    }
    return nResult;
}

enum class Subdivision {
    Grid,
    Beat,
    Measure
};

// Returns the nearest subdivision starting from time 'musicTime' according to 'timeSignature'
static std::uint32_t getNextGridSubdivision(std::uint32_t musicTime, Subdivision subd, DMUS_IO_TIMESIG timeSignature) {
    std::uint32_t divisionCoeff;
    if (timeSignature.bBeat == 0) {
        divisionCoeff = PlayingContext::PulsesPerQuarterNote / 64;
    } else if (timeSignature.bBeat > 4) {
        divisionCoeff = (PlayingContext::PulsesPerQuarterNote * 4) / timeSignature.bBeat;
    } else {
        divisionCoeff = PlayingContext::PulsesPerQuarterNote * (4 / timeSignature.bBeat);
    }
    divisionCoeff *= timeSignature.wGridsPerBeat;

    std::uint32_t nextGridDivision = (std::uint32_t)(ceil((double)musicTime / divisionCoeff));
    std::uint32_t nextBeatDivision = nextGridDivision * timeSignature.wGridsPerBeat;
    std::uint32_t nextMeasureDivision = nextBeatDivision * timeSignature.bBeatsPerMeasure;

    switch (subd) {
    case Subdivision::Beat:
        return nextBeatDivision * divisionCoeff;
    case Subdivision::Grid:
        return nextGridDivision * divisionCoeff;
    case Subdivision::Measure:
        return nextMeasureDivision * divisionCoeff;
    }
}

static std::uint32_t getMeasureLength(DMUS_IO_TIMESIG timeSignature) {
    return (timeSignature.bBeatsPerMeasure * PlayingContext::PulsesPerQuarterNote * 4) / timeSignature.bBeat;
}

// From the Microsoft DX8 SDK docs
static std::uint32_t getMusicOffset(std::uint32_t mtGridStart, std::int16_t nTimeOffset, DMUS_IO_TIMESIG TimeSig) {
    const std::uint32_t DMUS_PPQ = PlayingContext::PulsesPerQuarterNote;
    return nTimeOffset +
        (
        (mtGridStart / TimeSig.wGridsPerBeat) * ((DMUS_PPQ * 4) / TimeSig.bBeat)
            +
            (mtGridStart % TimeSig.wGridsPerBeat) * (((DMUS_PPQ * 4) / TimeSig.bBeat) / TimeSig.wGridsPerBeat)
            );
}

// Returns true if the specified degree is present in a certain scale, and puts the distance from the
// root note into offset. If not present, returns false.
static bool getOffsetFromScale(std::uint8_t degree, std::uint32_t scale, std::uint8_t* offset) {
    assert(offset != nullptr);

    // FIXME: There is probably a faster way to do this
    std::vector<int> degrees;
    for (int i = 0; i < 24; i++) {
        if (scale & (0x00000001 << i)) {
            degrees.push_back(i);
        }
    }

    if (degree < degrees.size()) {
        *offset = degrees[degree];
        return true;
    } else {
        *offset = degrees[degrees.size() - 1];
        return false;
    }
}

static bool MusicValueToMIDI(std::uint32_t chord, const std::vector<DMUS_IO_SUBCHORD>& subchords, DMUS_IO_STYLENOTE note, DMUS_IO_STYLEPART part, std::uint8_t* value) {
    assert(value != nullptr);

    if (note.bPlayModeFlags == DMUS_PLAYMODE_FIXED) {
        // In the original Gothic sountrack this is not used, but it might be useful for modding purposes
        *value = (std::uint8_t)(note.wMusicValue);
        return true;
    }

    assert(note.bPlayModeFlags == DMUS_PLAYMODE_CHORD_ROOT | DMUS_PLAYMODE_CHORD_INTERVALS | DMUS_PLAYMODE_SCALE_INTERVALS);
    // TODO: The subchord level should be obtained from the part reference,
    // but in Gothic's soundtrack it's always set to the first level.

    /*
    A chord (or a scale) has the following structure: the first 8 bits represent the
    root of the chord (scale), with the standard MIDI notation:
    0 -> C
    1 -> C#
    ....
    23 -> B

    The following 24 bits represent which notes are present in the chord, with each
    bit meaning a semitone from the root (the LSB). Example taken from G2:

    chord = 0x00AB5AB5
    0   0   A   B   5   A   B   5
    00000000101010110101101010110101
    |-------|-|-|-||-|-||-|-|-||-|-|
    C    W W W HW W HW W W HW W -----> C Major scale
    */
    DMUS_IO_SUBCHORD subchord = subchords[0];

    /* Had to dig hard for this one: https://msdn.microsoft.com/en-us/library/ms898477.aspx
    Here is how to interpret wMusicValue:
    First nibble: octave (from -2 to 14 (??? I guess they meant -2 to 13) )
    Second nibble: chord tone (0 to 15)
    Third nibble: scale offset (0 to 15)
    Fourth nibble: accidentals (-8 to 7)
    */

    int octave = ((note.wMusicValue & 0xF000) >> 12);
    int chordTone = ((note.wMusicValue & 0x0F00) >> 8);
    int scaleTone = ((note.wMusicValue & 0x00F0) >> 4);

    // Explanation: the accidentals are represented as a two's complement 4bit value.
    // We first take only the last four bits from the word, then we shift it left
    // while keeping it unsigned, so that when we convert it into a signed byte
    // it has the correct sign. Then we divide by 16 to simulate an arithmetic
    // right shift of 4, to bring the value back into the correct range
    int accidentals = (std::int8_t)(note.wMusicValue & 0x000F);
    if (accidentals > 7) { accidentals = (accidentals - 16); }

    int noteValue = ((chord & 0xFF000000) >> 24) + 12 * octave;
    std::uint8_t chordOffset = 0;
    std::uint8_t scaleOffset = 0;
    if (getOffsetFromScale(chordTone, subchord.dwChordPattern, &chordOffset)) {
        noteValue += chordOffset;
        // Is it possible to not find chordTone?
        // } else if (getOffsetFromScale(chordTone, subchord.dwScalePattern, &scaleOffset)) {
        //     noteValue += scaleOffset;
    } else {
        TRACE_VERBOSE("Note not found: " << noteValue);
        return false;
    }

    // Start scale offset from resolved chordTone.
    // Let's draw an example:
    //      * chordTone is 2, scaleTone is 1
    //      * in normal chord chordTone 2 is resolved to 5th grade in scale
    //      * plus scaleTone 1 it makes 6th grade of the scale
    //      * depending on scale, it might be 8 semitones (minor) or 9 semitones (major)
    // So, scaleOffset depends on chord's grade
    // That works even if chord's grade doesn't fit into scale
    // Chord grade might be off of the scale, so if scaleOffset = 0, don't ruin it
    if (scaleTone && getOffsetFromScale(scaleTone, subchord.dwScalePattern >> chordOffset, &scaleOffset)) {
        noteValue += scaleOffset;
    }

    noteValue += accidentals;
    while (noteValue < 0) {
        noteValue += 12;
    }
    while (noteValue > 127) {
        noteValue -= 12;
    }

    *value = noteValue;
    TRACE_VERBOSE((int)value);
    return true;
}

void MusicMessage::playPattern(PlayingContext& ctx) {
    ctx.m_patternMessageQueue = MessageQueue();
    for (const auto& kvpair : ctx.m_performanceChannels) {
        kvpair.second->allNotesOff();
    }

    if (ctx.m_primarySegment != nullptr && ctx.m_performanceChannels.size() > 0 && ctx.m_subchords.size() > 0) {
        PlayingContext::Pattern pttn;
        if (ctx.m_primarySegment->getRandomPattern(ctx.m_grooveLevel, &pttn)) {
            std::uint32_t patternLength = pttn.header.wNbrMeasures * getMeasureLength(pttn.header.timeSig);
            for (const auto& partTuple: pttn.parts) {
                const auto& partRef = partTuple.first;
                const auto& part = partTuple.second;

                for (const auto& note: part.getNotes()) {
                    std::uint8_t midiNote;
                    std::uint32_t timeStart = getMusicOffset(note.mtGridStart, note.nTimeOffset, part.getHeader().timeSig);
                    if (MusicValueToMIDI(ctx.m_chord, ctx.m_subchords, note, part.getHeader(), &midiNote)) {
                        auto noteOnMessage = std::make_shared<NoteOnMessage>(ctx.m_musicTime + timeStart, midiNote, note.bVelocity, 0, partRef.wLogicalPartID);
                        assert(noteOnMessage != nullptr);
                        ctx.m_patternMessageQueue.push(noteOnMessage);

                        auto noteOffMessage = std::make_shared<NoteOffMessage>(ctx.m_musicTime + timeStart + note.mtDuration, midiNote, partRef.wLogicalPartID);
                        assert(noteOffMessage != nullptr);
                        ctx.m_patternMessageQueue.push(noteOffMessage);
                    }
                }
            }

            auto patternEndMessage = std::make_shared<PatternEndMessage>(ctx.m_musicTime + patternLength);
            ctx.m_patternMessageQueue.push(patternEndMessage);
        }
    }
}

void MusicMessage::setGrooveLevel(PlayingContext& ctx, std::uint8_t level) {
    ctx.m_grooveLevel = level;

    playPattern(ctx);
}

const std::map<std::uint32_t, std::shared_ptr<InstrumentPlayer>>& MusicMessage::getChannels(PlayingContext& ctx) {
    return ctx.m_performanceChannels;
}

void MusicMessage::changeChord(PlayingContext& ctx, std::uint32_t chord, const std::vector<DMUS_IO_SUBCHORD>& subchords) {
    ctx.m_chord = chord;
    ctx.m_subchords = subchords;
}

void MusicMessage::enqueueNextSegment(PlayingContext& ctx) {
    if (ctx.m_primarySegment == nullptr) {
        if (ctx.m_nextSegment != nullptr) {
            ctx.enqueueSegment(ctx.m_nextSegment);
            ctx.m_primarySegment = std::move(ctx.m_nextSegment);
            ctx.m_nextSegment = nullptr;
        }
    } else {
        if (ctx.m_nextSegment != nullptr) {
            ctx.enqueueSegment(ctx.m_nextSegment);
            ctx.m_primarySegment = std::move(ctx.m_nextSegment);
            ctx.m_nextSegment = nullptr;
        } else {
            ctx.enqueueSegment(ctx.m_primarySegment);
        }
    }
}

void TempoChangeMessage::Execute(PlayingContext& ctx) {
    TRACE("Tempo change");
    this->changeTempo(ctx, m_tempo);
}

BandChangeMessage::BandChangeMessage(PlayingContext& ctx, std::uint32_t time, const BandForm& form)
    : MusicMessage(time) {
    for (const auto& instr : form.getInstruments()) {
        const auto& header = instr.getHeader();
        const auto ref = instr.getReference();
        if (ref != nullptr) {
            std::uint8_t bankHi = (header.dwPatch & 0x00FF0000) >> 0x10;
            std::uint8_t bankLo = (header.dwPatch & 0x0000FF00) >> 0x8;
            std::uint8_t patch = (header.dwPatch & 0x000000FF);
            float volume = header.bVolume / 255.0f;
            float pan = ((float)(header.bPan) - 63.0f) / 64.0f;

            auto dls = ctx.loadInstrumentCollection(ref->getFile());

            assert(dls != nullptr);
            instruments[header.dwPChannel] = createInstrument(ctx, bankLo, bankHi, patch, *dls, volume, pan);
        }
    }
}

void BandChangeMessage::Execute(PlayingContext& ctx) {
    TRACE("Band change");
    for (const auto& kvpair : instruments) {
        setInstrument(ctx, kvpair.first, kvpair.second);
    }
}

void GrooveLevelMessage::Execute(PlayingContext& ctx) {
    TRACE("Groove change");
    if (m_range == 0) {
        setGrooveLevel(ctx, m_level);
    } else {
        std::int8_t offset = (std::rand() % m_range) - (m_range / 2);
        setGrooveLevel(ctx, m_level - offset);
    }
}

void ChordMessage::Execute(PlayingContext& ctx) {
    TRACE("Chord change");
    changeChord(ctx, this->m_chord, this->m_subchords);
}

void NoteOnMessage::Execute(PlayingContext& ctx) {
    TRACE_VERBOSE("Note on");
    const auto& channels = getChannels(ctx);
    assert(channels.find(m_channel) != channels.end());
    if (m_velRange == 0) {
        channels.at(m_channel)->noteOn(m_note, m_vel);
    } else {
        std::int8_t offset = (std::rand() % m_velRange) - (m_velRange / 2);
        channels.at(m_channel)->noteOn(m_note, m_vel - offset);
    }
}

void NoteOffMessage::Execute(PlayingContext& ctx) {
    TRACE_VERBOSE("Note off");
    const auto& channels = getChannels(ctx);
    assert(channels.find(m_channel) != channels.end());
    channels.at(m_channel)->noteOff(m_note, 0);
}

void SegmentEndMessage::Execute(PlayingContext& ctx) {
    TRACE("Segment end");
    enqueueNextSegment(ctx);
}

void PatternEndMessage::Execute(PlayingContext& ctx) {
    TRACE("Pattern end");
    playPattern(ctx);
}<|endoftext|>


<commit_before>#include <iostream>
#include <SDL2/SDL.h>

#include "cpu/intel8080.hpp"
#include "gfx/screen.hpp"
#include "machine/machine.hpp"
#include "program/program.hpp"

#define SCREEN_WIDTH 64
#define SCREEN_HEIGHT 32
#define SCALE 10

Intel8080 cpu;
Screen screen(SCREEN_WIDTH, SCREEN_HEIGHT, SCALE);

int main(int argc, char **argv) {
	program_t *program = getProgram(argv[1]);
	cpu.loadProgram(program);
	free_program(program);

	Machine machine(cpu, screen);
	machine.start();

	return 0;
}
<commit_msg>Updated screen size to actual Space Invaders dimensions<commit_after>#include <iostream>
#include <SDL2/SDL.h>

#include "cpu/intel8080.hpp"
#include "gfx/screen.hpp"
#include "machine/machine.hpp"
#include "program/program.hpp"

#define SCREEN_WIDTH 256
#define SCREEN_HEIGHT 244
#define SCALE 10

Intel8080 cpu;
Screen screen(SCREEN_WIDTH, SCREEN_HEIGHT, SCALE);

int main(int argc, char **argv) {
	program_t *program = getProgram(argv[1]);
	cpu.loadProgram(program);
	free_program(program);

	Machine machine(cpu, screen);
	machine.start();

	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) Wiesław Šoltés. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "StdAfx.h"
#include "OptionsParser.h"

COptionsParser::COptionsParser()
{
    this->Clean();
}

COptionsParser::COptionsParser(int nArgc, LPTSTR *pArgv)
{
    (void) this->Init(nArgc, pArgv);
}

COptionsParser::COptionsParser(const CL_OPTIONS *pclOptions)
{
    (void) this->Init(pclOptions);
}

COptionsParser::COptionsParser(int nArgc, LPTSTR *pArgv, const CL_OPTIONS *pclOptions)
{
    (void) this->Init(nArgc, pArgv, pclOptions);
}

COptionsParser::~COptionsParser(void)
{
    this->Clean();
}

bool COptionsParser::InitDone(void)
{
    if ((this->bInit == true) && (this->pArgv != NULL))
        return true;

    if ((this->bHaveArgv == true) &&
        (this->bHaveOptions == true))
    {
        this->nPosArgv = 0;
        this->nParams = 0;
        this->nParamsPos = -1;
        this->nOptionId = -2;
        this->bInit = true;

        return true;
    }
    return false;
}

bool COptionsParser::Init(int nArgc, LPTSTR *pArgv)
{
    if ((pArgv != NULL) && (nArgc >= 2))
    {
        this->nArgc = nArgc;
        this->pArgv = pArgv;
        this->bHaveArgv = true;
    }

    return this->InitDone();
}

bool COptionsParser::Init(const CL_OPTIONS *pclOptions)
{
    if (pclOptions != NULL)
    {
        this->pclOptions = pclOptions;
        this->bHaveOptions = true;
    }

    return this->InitDone();
}

bool COptionsParser::Init(int nArgc, LPTSTR *pArgv, const CL_OPTIONS *pclOptions)
{
    if ((pArgv != NULL) && (nArgc >= 2))
    {
        this->nPosArgv = 0;
        this->nArgc = nArgc;
        this->pArgv = pArgv;
        this->bHaveArgv = true;
    }

    if (pclOptions != NULL)
    {
        this->pclOptions = pclOptions;
        this->bHaveOptions = true;
    }

    return this->InitDone();
}

void COptionsParser::Clean(void)
{
    this->nPosArgv = -1;
    this->nParams = -1;
    this->nParamsPos = -1;
    this->nOptionId = -2;

    this->nArgc = -1;
    this->pArgv = NULL;
    this->pclOptions = NULL;

    bool bHaveArgv = false;
    bool bHaveOptions = false;

    this->bInit = false;
}

bool COptionsParser::IsOpt(LPTSTR szOption)
{
    if (szOption == NULL)
        return false;

    if (this->IsLongOpt(szOption) == true)
        return true;
    else if (this->IsShortOpt(szOption) == true)
        return true;
    else
        return false;
}

bool COptionsParser::IsLongOpt(LPTSTR szOption)
{
    if (szOption == NULL)
        return false;

    // is argument in long option format: --option, --o, --opt, etc...
    if (lstrlen(szOption) >= 3)
    {
        if ((szOption[0] == '-') && (szOption[1] == '-'))
            return true;
        else
            return false;
    }

    return false;
}

bool COptionsParser::IsShortOpt(LPTSTR szOption)
{
    if (szOption == NULL)
        return false;

    // is argument in short option format: -o, /o, etc...
    if (lstrlen(szOption) == 2)
    {
        if ((szOption[0] == '-') || (szOption[0] == '/'))
            return true;
        else
            return false;
    }

    return false;
}

int COptionsParser::Next(void)
{
    // Return values:
    // 0...N - valid option
    // -1    - finished
    // -2    - error (invalid option or param)

    if (this->InitDone() == false)
        return(-2); // ERROR

    // clean last options status
    this->nParams = 0;
    this->nParamsPos = -1;
    this->nOptionId = -2;

    // increment args counter
    this->nPosArgv++;

    // check if are at end of args
    if (this->nPosArgv >= nArgc)
        return(-1); // SUCCESS

    LPTSTR szOption = NULL;
    szOption = this->pArgv[this->nPosArgv];

    // get valid option name
    if (this->IsLongOpt(szOption) == true)
    {
        // long option format
        szOption = (LPTSTR)(szOption + 2);
    }
    else if (this->IsShortOpt(szOption) == true)
    {
        // short option format
        szOption = (LPTSTR)(szOption + 1);
    }
    else
    {
        // invalid option format
        return(-2); // ERROR
    }

    int nOptionId = -2;
    int i = 0;

#ifdef USE_PARSER_TOKENS
    const TCHAR szTokenSeps[] = _T(" ;|,\t");
#endif // USE_PARSER_TOKENS

    while (pclOptions[i].szOptionName != NULL)
    {
#ifdef USE_PARSER_TOKENS
        LPTSTR szOptionName = NULL;
        size_t nLen = _tcslen(pclOptions[i].szOptionName);
        szOptionName = (LPTSTR)malloc((nLen + 1) * sizeof(TCHAR));
        if (szOptionName == NULL)
            return(-2); // ERROR

        memcpy(szOptionName, pclOptions[i].szOptionName, nLen * sizeof(TCHAR));
        szOptionName[nLen] = '\0';
        LPTSTR szToken = _tcstok(szOptionName, szTokenSeps);
        while (szToken != NULL)
        {
            // check next token
            if (lstrcmp(szOption, szToken) == 0)
#else
        if (lstrcmp(szOption, pclOptions[i].szOptionName) == 0)
#endif // USE_PARSER_TOKENS
        {
            nOptionId = pclOptions[i].nOptionId;
            if (pclOptions[i].bParamsRequired == false)
            {
                if (pclOptions[i].nParams == 1)
                {
                    // check if we have 1 param that is not required
                    if ((this->nPosArgv + 1) >= nArgc)
                    {
                        // no more args available
                        this->nParams = 0;
                        this->nParamsPos = -1;
                    }
                    else
                    {
                        // if next arg is not an option then we have 1 param
                        if (this->IsOpt(this->pArgv[this->nPosArgv + 1]) == false)
                        {
                            this->nParams = 1;
                            this->nParamsPos = this->nPosArgv + 1;

                            // update position
                            this->nPosArgv++;
                        }
                    }
                }

#ifdef USE_PARSER_TOKENS
                free(szOptionName);
#endif // USE_PARSER_TOKENS

                // SUCCESS
                this->nOptionId = nOptionId;
                return nOptionId;
            }
            else
            {
                // check if we have all required params
                if (pclOptions[i].nParams > 0)
                {
                    // check if we have all params
                    if ((this->nPosArgv + pclOptions[i].nParams) >= nArgc)
                    {
#ifdef USE_PARSER_TOKENS
                        free(szOptionName);
#endif // USE_PARSER_TOKENS

                        // out off args
                        return(-2); // ERROR
                    }

                    // check if all params are valid
                    for (int j = 0; j < pclOptions[i].nParams; j++)
                    {
                        if (this->IsOpt(this->pArgv[this->nPosArgv + 1 + j]) == true)
                        {
#ifdef USE_PARSER_TOKENS
                            free(szOptionName);
#endif // USE_PARSER_TOKENS
                            return(-2); // ERROR
                        }
                    }

                    // we have all params
                    this->nParams = pclOptions[i].nParams;
                    this->nParamsPos = this->nPosArgv + 1;

                    // update position
                    this->nPosArgv += this->nParams;
                }

#ifdef USE_PARSER_TOKENS
                free(szOptionName);
#endif // USE_PARSER_TOKENS

                // SUCCESS
                this->nOptionId = nOptionId;
                return nOptionId;
            }
        }

#ifdef USE_PARSER_TOKENS
        // get next token
        szToken = _tcstok(NULL, szTokenSeps);
        }

    // free memory used for tokens options
    free(szOptionName);
#endif // USE_PARSER_TOKENS

    // check next option
    i++;
    }

return(-2); // ERROR
}

bool COptionsParser::GetParam(CString &szParam, int nNum)
{
    if (this->InitDone() == false)
        return false;

    if ((this->nParams == 0) || (this->nParamsPos < 0))
        return false;

    if ((nNum < 0) || (nNum >= this->nParams))
        return false;

    szParam = this->pArgv[this->nParamsPos + nNum];
    return true;
}
<commit_msg>Removed unused file<commit_after><|endoftext|>


<commit_before>/* vim:set noet sts=0 sw=2 ts=2: */
#include <string>
#include <cctype>
#include <array>
#include <utility>

#include <iostream>

#include "ftoken.hpp"

namespace fusion {
	const std::array<std::string, 29> tokens = {
		"else", "if", "true", "false", "nil", "while", "in", "new", "extends",
		"for", "async", "yield"
		/* */
		"&&", "||", ">>", "<<", "==", "!=", ">=", "<=", "..", "_/",
		/* */
		"...", "[num]", "[str]", "[name]", "[eof]", "[white]"
	};

	char get_next(uint32_t position, std::string input) {
		/* this should ONLY be used when the input is pre-verified */
		if (input.length() < position + 1)
			return input.at(position); // position is already incremented
		else
			return '\0'; // there should not ever be a '\0' in input
	}

	std::pair<bool, std::string> try_parse_num(TokenizerState *ts) {
		std::string input = ts->input;
		char first = input.at(ts->position);
		if (first == '0' && (f_check_next(1, 'x') || f_check_next(1, 'X'))) {
			std::string hexable = "0123456789ABCDEF";
			std::string scanned = "";
			uint32_t pos = ts->position + 2;
			while (input.find_first_of(hexable, pos) == pos)
				scanned += get_next(++pos, input);
			if (scanned.length() == 0)
				return std::pair<bool, std::string>(false, "");
			char exponent = get_next(pos);
			if (exponent == 'p' || exponent == 'P') {
				scanned += exponent;
				if (get_next(pos + 1, input) == '-' ||
						get_next(pos + 1, input) == '+')
					scanned += get_next(++pos, input);
				while (input.find_first_of("0123456789", pos) == pos)
					scanned += get_next(++pos, input);
			}
			ts->position = pos;
			return std::pair<bool, std::string>(true, scanned);
		} else if (input.find_first_of("0123456789.", ts->position)) {
			// period included for decimals
			std::string scanned = "";
			uint32_t pos = ts->position;
			while (input.find_first_of("0123456789", pos) == pos)
				scanned += get_next(++pos, input);
			if (get_next(pos, input) == '.' &&
					(input.find("0123456789", pos) == pos + 1)) {
				pos++;
				while (input.find_first_of("0123456789", pos) == pos)
					scanned += get_next(++pos, input);
			}
			if (scanned != ".") { // it can potentially match just a period so don't do that
				ts->position = pos;
				return std::pair<bool, std::string>(true, scanned);
			}
		}
		return std::pair<bool, std::string>(false, "");
	}

	void tokenize(TokenizerState *ts, std::string input) {
		/* initialize the tokenizer state */
		/* search through string for a token */
		while (true) {
			try {
				switch (input.at(ts->position)) {
					case '\r': {
						/* ignore \r for Windows support */
						ts->position++;
						break;
					}
					case '\n': {
						ts->current_line++;
						ts->position++;
						break;
					}
					case ' ': case '\t': case '\v': case '\f': {
						std::string whitespace_token = "";
						while (f_iswhitespace(get_next(ts->position))) {
							whitespace_token += get_next(ts->position);
							ts->position++;
						}
						ts->tokens.push_back({token::TOK_WHITE, whitespace_token});
						break;
					}
					case '&': {
						/* check for && otherwise & */
						if (f_check_next(1, '&')) {
							/* generate token for && and incr position to skip over */
							ts->tokens.push_back({
								token::TOK_BOOLAND, /* token::type */
								"&&"                /* string self */
							});
							ts->position += 2;
						break;
						} /* end if */ /* do not process just & */
						/* the nonbreaking default will process single-char tokens */
					} /* end case */
					case '|': {
						/* same as above, check for || otherwise | */
						if (f_check_next(1, '|')) {
							/* generate || token and incr */
							ts->tokens.push_back({token::TOK_BOOLOR, "||"});
							ts->position += 2;
							break;
						}
					} /* end case */
					case '>': {
						/* check for >>, check for >=, or DON'T break */
						/* the char lives as itself as a token if nobreak */
						bool is_single_char = false;
						switch (get_next(ts->position + 1, input)) {
							case '>':
								ts->tokens.push_back({token::TOK_RSHIFT, ">>"});
								break;
							case '=':
								ts->tokens.push_back({token::TOK_GE, ">="});
								break;
							default:
								is_single_char = true;
						} /* end switch */
						if (!is_single_char) {
							ts->position += 2;
							break;
						}
					} /* end case */
					case '<': {
						/* duplicate above again, but with < */
						bool is_single_char = false;
						switch(get_next(ts->position + 1, input)) {
							case '<':
								ts->tokens.push_back({token::TOK_LSHIFT, "<<"});
								break;
							case '=':
								ts->tokens.push_back({token::TOK_LE, "<="});
								break;
							default:
								is_single_char = true;
						} /* end switch */
						if (!is_single_char) {
							ts->position += 2;
							break;
						}
					} /* end case */
					case '=': {
						/* check == else = */
						if (f_check_next(1, '=')) {
							ts->tokens.push_back({token::TOK_EQ, "=="});
							ts->position += 2;
							break;
						}
					}
					case '!': {
						/* check != else = */
						if (f_check_next(1, '=')) {
							ts->tokens.push_back({token::TOK_NEQ, "!="});
							ts->position += 2;
							break;
						}
					}
					case '.': {
						/* check .., then ...; if not ... then ..; then . */
						auto result = try_parse_num(ts);
						if (std::get<0>(result)) { /* true if number, false if not */
							/* get<1>(result) should return a std::string */
							std::string result_num = std::get<1>(result);
							ts->tokens.push_back({token::TOK_NUM, result_num});
							ts->position += result_num.length();
						} else if (f_check_next(1, '.')) {
							if (f_check_next(2, '.')) {
								/* ... */
								ts->tokens.push_back({token::TOK_VARARG, "..."});
								ts->position += 3;
								break;
							} else {
								/* .. */
								ts->tokens.push_back({token::TOK_CONCAT, ".."});
								ts->position += 3;
								break;
							}
						} /* end else if */
					} /* end case */
					case '_': { /* floor division */
						if (f_check_next(1, '/')) {
							ts->tokens.push_back({token::TOK_FLOORDIV, "_/"});
							ts->position += 2;
							break;
						}
					}
					case '"': {
						std::string buffer = "\"";
						ts->position++; /* increment position past the " */
						char current = '\0'; /* current in string */
						while (current != '"') { /* we can pass over '\"' during the loop */
							current = get_next(ts->position, input);
							if (current == '\\') { /* process escape code, C-style */
								buffer += "\\" + get_next(ts->position + 2, input);
								// TODO verify not '\0'
								ts->position += 2;
							} else if (current == '"') {
								buffer += '"';
								ts->position++;
								break;
							} else {
								buffer += current;
								ts->position++;
							}
						} /* end while */
						ts->tokens.push_back({token::TOK_STRING, buffer});
						break;
					} /* end case */
					case '\'': {
						std::string buffer = "'";
						ts->position++; /* incr past ' */
						char current = '\0';
						while (current != '\'') {
							buffer += get_next(ts->position++, input); // TODO verify !'\0'
						} /* end while */
						ts->tokens.push_back({token::TOK_STRING, buffer});
						break;
					}
					case '0': case '1': case '2': case '3': case '4':
					case '5': case '6': case '7': case '8': case '9': {
						/* no matter what, this should return a number */
						/* we don't need to test try_parse_num[1] */
						std::string result_num = std::get<1>(try_parse_num(ts));
						ts->tokens.push_back({token::TOK_NUM, result_num});
						ts->position += result_num.length();
						break;
					} /* end case */
					default: {
						char current_char = get_next(ts->position, input);
						if (isalpha(current_char) || current_char == '_') {
							std::string word = "";
							while (isalnum(current_char) || current_char == '_') {
								word += current_char;
								current_char = get_next(++ts->position);
								if (current_char == '\0')
									break;
							} /* end while */
								/* fully captured word, check if reserved keyword */
							bool is_reserved = false;
							for (auto iter = tokens.begin();
									iter != tokens.end(); ++iter) {
								/* find reserved word and push token */
								if (word == *iter) {
									/* found reserved word */
									uint16_t pos = iter - tokens.begin();
									ts->tokens.push_back({
										static_cast<token::token_t>(pos + FIRST_TOKEN),
										word
									});
									is_reserved = true;
								} /* end if */
							} /* end for */
							if (!is_reserved) ts->tokens.push_back({token::TOK_NAME, word});
								/* used as an identifier */
						} else {
							ts->tokens.push_back({
								static_cast<token::token_t>(get_next(ts->position, input)),
								std::string(1, get_next(ts->position, input))
							});
							ts->position++;
						} /* end else */
					} /* end default */
				} /* end switch */
			} catch (std::out_of_range const &ignored_exception) {
				ts->tokens.push_back({token::TOK_END, ""});
				return;
			} /* end try */ /* end of input, return final token */
		} /* end while */
	} /* end function */
} /* end namespace */

int main() {
	fusion::TokenizerState ts = fusion::TOKENIZER_STATE_DEFAULT;
	fusion::tokenize(&ts, "print(    \"hi\" )");
	for (auto token = ts.tokens.begin(); token != ts.tokens.end(); token++) {
		if (token->type > 256)
			std::cout << fusion::tokens[token->type - 256] << " ";
		else
			std::cout << token->type << " ";
		std::cout << token->self << std::endl;
	}
	return 0;
}
<commit_msg>src/ftoken: not working, uploading to test<commit_after>/* vim:set noet sts=0 sw=2 ts=2: */
#include <string>
#include <cctype>
#include <array>
#include <utility>

#include <iostream>

#include "ftoken.hpp"

namespace fusion {
	const std::array<std::string, 28> tokens = {
		"else", "if", "true", "false", "nil", "while", "in", "new", "extends",
		"for", "async", "yield"
		/* */
		"&&", "||", ">>", "<<", "==", "!=", ">=", "<=", "..", "_/",
		/* */
		"...", "[num]", "[str]", "[name]", "[eof]", "[white]"
	};

	char get_next(uint32_t position, std::string input) {
		/* this should ONLY be used when the input is pre-verified */
		if (input.length() > position) {
			return input.at(position); // position is already incremented
		} else
			return '\0'; // there should not ever be a '\0' in input
	}

	std::pair<bool, std::string> try_parse_num(TokenizerState *ts) {
		std::string input = ts->input;
		char first = get_next(ts->position, input);
		if (first == '0' && (f_check_next(1, 'x') || f_check_next(1, 'X'))) {
			std::string hexable = "0123456789ABCDEF";
			std::string scanned = "";
			uint32_t pos = ts->position + 2;
			while (input.find_first_of(hexable, pos) == pos)
				scanned += get_next(++pos, input);
			if (scanned.length() == 0)
				return std::pair<bool, std::string>(false, "");
			char exponent = get_next(pos, input);
			if (exponent == 'p' || exponent == 'P') {
				scanned += exponent;
				if (get_next(pos + 1, input) == '-' ||
						get_next(pos + 1, input) == '+')
					scanned += get_next(++pos, input);
				while (input.find_first_of("0123456789", pos) == pos)
					scanned += get_next(++pos, input);
			}
			ts->position = pos;
			return std::pair<bool, std::string>(true, scanned);
		} else if (input.find_first_of("0123456789.", ts->position) ==
				ts->position) {
			// period included for decimals
			std::string scanned = "";
			uint32_t pos = ts->position;
			while (input.find_first_of("0123456789", pos) == pos) {
				scanned += get_next(++pos, input);
				std::cout << scanned << "\n";
			}
			if (get_next(pos, input) == '.' &&
					(input.find("0123456789", pos) == pos + 1)) {
				pos++;
				while (input.find_first_of("0123456789", pos) == pos) {
					scanned += get_next(++pos, input);
				}
			}
			if (scanned != ".") { // it can potentially match just a period so don't do that
				ts->position = pos;
				return std::pair<bool, std::string>(true, scanned);
			}
		}
		return std::pair<bool, std::string>(false, "");
	}

	void tokenize(TokenizerState *ts, std::string input) {
		/* initialize the tokenizer state */
		/* search through string for a token */
		ts->input = input;
		while (true) {
			try {
				switch (input.at(ts->position)) {
					case '\r': {
						/* ignore \r for Windows support */
						ts->position++;
						break;
					}
					case '\n': {
						ts->current_line++;
						ts->position++;
						break;
					}
					case ' ': case '\t': case '\v': case '\f': {
						std::string whitespace_token = "";
						while (f_iswhitespace(get_next(ts->position, input))) {
							whitespace_token += get_next(ts->position, input);
							ts->position++;
						}
						ts->tokens.push_back({token::TOK_WHITE, whitespace_token});
						break;
					}
					case '&': {
						/* check for && otherwise & */
						if (f_check_next(1, '&')) {
							/* generate token for && and incr position to skip over */
							ts->tokens.push_back({
								token::TOK_BOOLAND, /* token::type */
								"&&"                /* string self */
							});
							ts->position += 2;
						break;
						} /* end if */ /* do not process just & */
						/* the nonbreaking default will process single-char tokens */
					} /* end case */
					case '|': {
						/* same as above, check for || otherwise | */
						if (f_check_next(1, '|')) {
							/* generate || token and incr */
							ts->tokens.push_back({token::TOK_BOOLOR, "||"});
							ts->position += 2;
							break;
						}
					} /* end case */
					case '>': {
						/* check for >>, check for >=, or DON'T break */
						/* the char lives as itself as a token if nobreak */
						bool is_single_char = false;
						switch (get_next(ts->position + 1, input)) {
							case '>':
								ts->tokens.push_back({token::TOK_RSHIFT, ">>"});
								break;
							case '=':
								ts->tokens.push_back({token::TOK_GE, ">="});
								break;
							default:
								is_single_char = true;
						} /* end switch */
						if (!is_single_char) {
							ts->position += 2;
							break;
						}
					} /* end case */
					case '<': {
						/* duplicate above again, but with < */
						bool is_single_char = false;
						switch(get_next(ts->position + 1, input)) {
							case '<':
								ts->tokens.push_back({token::TOK_LSHIFT, "<<"});
								break;
							case '=':
								ts->tokens.push_back({token::TOK_LE, "<="});
								break;
							default:
								is_single_char = true;
						} /* end switch */
						if (!is_single_char) {
							ts->position += 2;
							break;
						}
					} /* end case */
					case '=': {
						/* check == else = */
						if (f_check_next(1, '=')) {
							ts->tokens.push_back({token::TOK_EQ, "=="});
							ts->position += 2;
							break;
						}
					}
					case '!': {
						/* check != else = */
						if (f_check_next(1, '=')) {
							ts->tokens.push_back({token::TOK_NEQ, "!="});
							ts->position += 2;
							break;
						}
					}
					case '.': {
						/* check .., then ...; if not ... then ..; then . */
						auto result = try_parse_num(ts);
						if (std::get<0>(result)) { /* true if number, false if not */
							/* get<1>(result) should return a std::string */
							std::string result_num = std::get<1>(result);
							ts->tokens.push_back({token::TOK_NUM, result_num});
							ts->position += result_num.length();
						} else if (f_check_next(1, '.')) {
							if (f_check_next(2, '.')) {
								/* ... */
								ts->tokens.push_back({token::TOK_VARARG, "..."});
								ts->position += 3;
								break;
							} else {
								/* .. */
								ts->tokens.push_back({token::TOK_CONCAT, ".."});
								ts->position += 3;
								break;
							}
						} /* end else if */
					} /* end case */
					case '_': { /* floor division */
						if (f_check_next(1, '/')) {
							ts->tokens.push_back({token::TOK_FLOORDIV, "_/"});
							ts->position += 2;
							break;
						}
					}
					case '"': {
						std::string buffer = "\"";
						ts->position++; /* increment position past the " */
						char current = '\0'; /* current in string */
						while (current != '"') { /* we can pass over '\"' during the loop */
							current = get_next(ts->position, input);
							if (current == '\\') { /* process escape code, C-style */
								buffer += "\\" + get_next(ts->position + 2, input);
								// TODO verify not '\0'
								ts->position += 2;
							} else if (current == '"') {
								buffer += '"';
								ts->position++;
								break;
							} else {
								buffer += current;
								ts->position++;
							}
						} /* end while */
						ts->tokens.push_back({token::TOK_STRING, buffer});
						break;
					} /* end case */
					case '\'': {
						std::string buffer = "'";
						ts->position++; /* incr past ' */
						char current = '\0';
						while (current != '\'') {
							buffer += get_next(ts->position++, input); // TODO verify !'\0'
						} /* end while */
						ts->tokens.push_back({token::TOK_STRING, buffer});
						break;
					}
					case '0': case '1': case '2': case '3': case '4':
					case '5': case '6': case '7': case '8': case '9': {
						/* no matter what, this should return a number */
						/* we don't need to test try_parse_num[1] */
						std::string result_num = std::get<1>(try_parse_num(ts));
						ts->tokens.push_back({token::TOK_NUM, result_num});
						ts->position += result_num.length();
						break;
					} /* end case */
					default: {
						char current_char = get_next(ts->position, input);
						if (isalpha(current_char) || current_char == '_') {
							std::string word = "";
							while (isalnum(current_char) || current_char == '_') {
								word += current_char;
								current_char = get_next(++ts->position, input);
								if (current_char == '\0')
									break;
							} /* end while */
								/* fully captured word, check if reserved keyword */
							bool is_reserved = false;
							for (auto iter = tokens.begin();
									iter != tokens.end(); ++iter) {
								/* find reserved word and push token */
								if (word == *iter) {
									/* found reserved word */
									uint16_t pos = iter - tokens.begin();
									ts->tokens.push_back({
										static_cast<token::token_t>(pos + FIRST_TOKEN),
										word
									});
									is_reserved = true;
								} /* end if */
							} /* end for */
							if (!is_reserved) ts->tokens.push_back({token::TOK_NAME, word});
								/* used as an identifier */
						} else {
							ts->tokens.push_back({
								static_cast<token::token_t>(get_next(ts->position, input)),
								std::string(1, get_next(ts->position, input))
							});
							ts->position++;
						} /* end else */
					} /* end default */
				} /* end switch */
			} catch (std::out_of_range const &ignored_exception) {
				ts->tokens.push_back({token::TOK_END, ""});
				return;
			} /* end try */ /* end of input, return final token */
		} /* end while */
	} /* end function */
} /* end namespace */

int main() {
	fusion::TokenizerState ts = fusion::TOKENIZER_STATE_DEFAULT;
	fusion::tokenize(&ts, "42");
	for (auto token = ts.tokens.begin(); token != ts.tokens.end(); token++) {
		if (token->type >= FIRST_TOKEN)
			std::cout << fusion::tokens[token->type - FIRST_TOKEN - 1] << " ";
		else
			std::cout << token->type << " ";
		std::cout << token->self << std::endl;
	}
	return 0;
}
<|endoftext|>