<commit_before>// ======================================================================== //
// Copyright 2009-2015 Intel Corporation                                    //
//                                                                          //
// Licensed under the Apache License, Version 2.0 (the "License");          //
// you may not use this file except in compliance with the License.         //
// You may obtain a copy of the License at                                  //
//                                                                          //
//     http://www.apache.org/licenses/LICENSE-2.0                           //
//                                                                          //
// Unless required by applicable law or agreed to in writing, software      //
// distributed under the License is distributed on an "AS IS" BASIS,        //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and      //
// limitations under the License.                                           //
// ======================================================================== //

#include "network.h"
#include "string.h"
#include "mutex.h"

////////////////////////////////////////////////////////////////////////////////
/// Platforms supporting Socket interface
////////////////////////////////////////////////////////////////////////////////

#if defined(__WIN32__)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#include <winsock2.h>
//#include <io.h>
typedef int socklen_t;
#define SHUT_RDWR 0x2
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#define SOCKET int
#define closesocket close
#endif

/*! ignore if not supported */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

#define BUFFERING 1

namespace embree
{
  namespace network
  {
    __forceinline void initialize() {
#ifdef __WIN32__
      static bool initialized = false;
      static MutexSys initMutex;
      Lock<MutexSys> lock(initMutex);
      WSADATA wsaData;
      short version = MAKEWORD(1,1);
      if (WSAStartup(version,&wsaData) != 0)
        THROW_RUNTIME_ERROR("Winsock initialization failed");
      initialized = true;
#endif
    }

    struct buffered_socket_t
    {
      buffered_socket_t (SOCKET fd, size_t isize = 64*1024, size_t osize = 64*1024)
        : fd(fd),
          ibuf(new char[isize]), isize(isize), istart(0), iend(0),
          obuf(new char[osize]), osize(osize), oend(0) {
      }

      ~buffered_socket_t () {
        delete[] ibuf; ibuf = nullptr;
        delete[] obuf; obuf = nullptr;
      }

      SOCKET fd;               //!< file descriptor of the socket
      char* ibuf;
      size_t isize;
      size_t istart,iend;
      char* obuf;
      size_t osize;
      size_t oend;
    };

    socket_t connect(const char* host, unsigned short port)
    {
      initialize();

      /*! create a new socket */
      SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
      if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket");

      /*! perform DNS lookup */
      struct hostent* server = ::gethostbyname(host);
      if (server == nullptr) THROW_RUNTIME_ERROR("server "+std::string(host)+" not found");

      /*! perform connection */
      struct sockaddr_in serv_addr;
      memset((char*)&serv_addr, 0, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = (unsigned short) htons(port);
      memcpy((char*)&serv_addr.sin_addr.s_addr, (char*)server->h_addr, server->h_length);

      if (::connect(sockfd,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) < 0)
        THROW_RUNTIME_ERROR("connection to "+std::string(host)+":"+std::to_string((long long)port)+" failed");

      /*! enable TCP_NODELAY */
#ifdef TCP_NODELAY
      { int flag = 1; ::setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(int)); }
#endif

      /*! we do not want SIGPIPE to be thrown */
#ifdef SO_NOSIGPIPE
      { int flag = 1; setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (const char*) &flag, sizeof(int)); }
#endif

      return (socket_t) new buffered_socket_t(sockfd);
    }

    socket_t bind(unsigned short port)
    {
      initialize();

      /*! create a new socket */
      SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
      if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket");

      /* When the server completes, the server socket enters a time-wait state during which the local
      address and port used by the socket are believed to be in use by the OS. The wait state may
      last several minutes. This socket option allows bind() to reuse the port immediately. */
#ifdef SO_REUSEADDR
      { int flag = true; ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, sizeof(int)); }
#endif

      /*! bind socket to port */
      struct sockaddr_in serv_addr;
      memset((char *) &serv_addr, 0, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = (unsigned short) htons(port);
      serv_addr.sin_addr.s_addr = INADDR_ANY;

      if (::bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0)
        THROW_RUNTIME_ERROR("binding to port "+std::to_string((long long)port)+" failed");

      /*! listen to port, up to 5 pending connections */
      if (::listen(sockfd,5) < 0)
        THROW_RUNTIME_ERROR("listening on socket failed");

      return (socket_t) new buffered_socket_t(sockfd);
    }

    socket_t listen(socket_t hsock)
    {
      SOCKET sockfd = ((buffered_socket_t*) hsock)->fd;

      /*! accept incoming connection */
      struct sockaddr_in addr;
      socklen_t len = sizeof(addr);
      SOCKET fd = ::accept(sockfd, (struct sockaddr *) &addr, &len);
      if (fd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot accept connection");

      /*! enable TCP_NODELAY */
#ifdef TCP_NODELAY
      { int flag = 1; ::setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char*)&flag,sizeof(int)); }
#endif

      /*! we do not want SIGPIPE to be thrown */
#ifdef SO_NOSIGPIPE
      { int flag = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&flag, sizeof(int)); }
#endif

      return (socket_t) new buffered_socket_t(fd);
    }

    void read(socket_t hsock_i, void* data_i, size_t bytes)
    {
#if BUFFERING
      char* data = (char*)data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        if (hsock->istart == hsock->iend) {
          ssize_t n = ::recv(hsock->fd,hsock->ibuf,hsock->isize,MSG_NOSIGNAL);
          if      (n == 0) throw Disconnect();
          else if (n  < 0) THROW_RUNTIME_ERROR("error reading from socket");
          hsock->istart = 0;
          hsock->iend = n;
        }
        size_t bsize = hsock->iend-hsock->istart;
        if (bytes < bsize) bsize = bytes;
        memcpy(data,hsock->ibuf+hsock->istart,bsize);
        data += bsize;
        hsock->istart += bsize;
        bytes -= bsize;
      }
#else
      char* data = (char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        ssize_t n = ::read(hsock->fd,data,bytes);
        if      (n == 0) throw Disconnect();
        else if (n  < 0) THROW_RUNTIME_ERROR("error reading from socket");
        data+=n;
        bytes-=n;
      }
#endif
    }

    void write(socket_t hsock_i, const void* data_i, size_t bytes)
    {
#if BUFFERING
      const char* data = (const char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        if (hsock->oend == hsock->osize) flush(hsock_i);
        size_t bsize = hsock->osize-hsock->oend;
        if (bytes < bsize) bsize = bytes;
        memcpy(hsock->obuf+hsock->oend,data,bsize);
        data += bsize;
        hsock->oend += bsize;
        bytes -= bsize;
      }
#else
      const char* data = (const char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        ssize_t n = ::write(hsock->fd,data,bytes);
        if (n  < 0) THROW_RUNTIME_ERROR("error writing to socket");
        data+=n;
        bytes-=n;
      }
#endif
    }

    void flush(socket_t hsock_i)
    {
#if BUFFERING
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      char* data = hsock->obuf;
      size_t bytes = hsock->oend;
      while (bytes > 0) {
        ssize_t n = ::send(hsock->fd,data,(int)bytes,MSG_NOSIGNAL);
        if (n < 0) THROW_RUNTIME_ERROR("error writing to socket");
        bytes -= n;
        data += n;
      }
      hsock->oend = 0;
#endif
    }

    void close(socket_t hsock_i) {
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      ::shutdown(hsock->fd,SHUT_RDWR);
      delete hsock;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
/// All Platforms
////////////////////////////////////////////////////////////////////////////////

namespace embree
{
  namespace network
  {
    bool read_bool(socket_t socket)
    {
      bool value = 0;
      read(socket,&value,sizeof(bool));
      return value;
    }

    char read_char(socket_t socket)
    {
      char value = 0;
      read(socket,&value,sizeof(char));
      return value;
    }

    int read_int(socket_t socket)
    {
      int value = 0;
      read(socket,&value,sizeof(int));
      return value;
    }

    float read_float(socket_t socket)
    {
      float value = 0.0f;
      read(socket,&value,sizeof(float));
      return value;
    }

    std::string read_string(socket_t socket)
    {
      int bytes = read_int(socket);
      char* str = new char[bytes+1];
      read(socket,str,bytes);
      str[bytes] = 0x00;
      std::string s(str);
      delete[] str;
      return s;
    }

    void write(socket_t socket, bool value) {
      write(socket,&value,sizeof(bool));
    }

    void write(socket_t socket, char value) {
      write(socket,&value,sizeof(char));
    }

    void write(socket_t socket, int value) {
      write(socket,&value,sizeof(int));
    }

    void write(socket_t socket, float value) {
      write(socket,&value,sizeof(float));
    }

    void write(socket_t socket, const std::string& str) {
      write(socket,(int)str.size());
      write(socket,str.c_str(),str.size());
    }
  }
}
<commit_msg>compile fixed for linux<commit_after>// ======================================================================== //
// Copyright 2009-2015 Intel Corporation                                    //
//                                                                          //
// Licensed under the Apache License, Version 2.0 (the "License");          //
// you may not use this file except in compliance with the License.         //
// You may obtain a copy of the License at                                  //
//                                                                          //
//     http://www.apache.org/licenses/LICENSE-2.0                           //
//                                                                          //
// Unless required by applicable law or agreed to in writing, software      //
// distributed under the License is distributed on an "AS IS" BASIS,        //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and      //
// limitations under the License.                                           //
// ======================================================================== //

#include "network.h"
#include "string.h"
#include "mutex.h"

////////////////////////////////////////////////////////////////////////////////
/// Platforms supporting Socket interface
////////////////////////////////////////////////////////////////////////////////

#if defined(__WIN32__)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#include <winsock2.h>
//#include <io.h>
typedef int socklen_t;
#define SHUT_RDWR 0x2
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#define SOCKET int
#define INVALID_SOCKET -1
#define closesocket close
#endif

/*! ignore if not supported */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

#define BUFFERING 1

namespace embree
{
  namespace network
  {
    __forceinline void initialize() {
#ifdef __WIN32__
      static bool initialized = false;
      static MutexSys initMutex;
      Lock<MutexSys> lock(initMutex);
      WSADATA wsaData;
      short version = MAKEWORD(1,1);
      if (WSAStartup(version,&wsaData) != 0)
        THROW_RUNTIME_ERROR("Winsock initialization failed");
      initialized = true;
#endif
    }

    struct buffered_socket_t
    {
      buffered_socket_t (SOCKET fd, size_t isize = 64*1024, size_t osize = 64*1024)
        : fd(fd),
          ibuf(new char[isize]), isize(isize), istart(0), iend(0),
          obuf(new char[osize]), osize(osize), oend(0) {
      }

      ~buffered_socket_t () {
        delete[] ibuf; ibuf = nullptr;
        delete[] obuf; obuf = nullptr;
      }

      SOCKET fd;               //!< file descriptor of the socket
      char* ibuf;
      size_t isize;
      size_t istart,iend;
      char* obuf;
      size_t osize;
      size_t oend;
    };

    socket_t connect(const char* host, unsigned short port)
    {
      initialize();

      /*! create a new socket */
      SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
      if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket");

      /*! perform DNS lookup */
      struct hostent* server = ::gethostbyname(host);
      if (server == nullptr) THROW_RUNTIME_ERROR("server "+std::string(host)+" not found");

      /*! perform connection */
      struct sockaddr_in serv_addr;
      memset((char*)&serv_addr, 0, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = (unsigned short) htons(port);
      memcpy((char*)&serv_addr.sin_addr.s_addr, (char*)server->h_addr, server->h_length);

      if (::connect(sockfd,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) < 0)
        THROW_RUNTIME_ERROR("connection to "+std::string(host)+":"+std::to_string((long long)port)+" failed");

      /*! enable TCP_NODELAY */
#ifdef TCP_NODELAY
      { int flag = 1; ::setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(int)); }
#endif

      /*! we do not want SIGPIPE to be thrown */
#ifdef SO_NOSIGPIPE
      { int flag = 1; setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (const char*) &flag, sizeof(int)); }
#endif

      return (socket_t) new buffered_socket_t(sockfd);
    }

    socket_t bind(unsigned short port)
    {
      initialize();

      /*! create a new socket */
      SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
      if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket");

      /* When the server completes, the server socket enters a time-wait state during which the local
      address and port used by the socket are believed to be in use by the OS. The wait state may
      last several minutes. This socket option allows bind() to reuse the port immediately. */
#ifdef SO_REUSEADDR
      { int flag = true; ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, sizeof(int)); }
#endif

      /*! bind socket to port */
      struct sockaddr_in serv_addr;
      memset((char *) &serv_addr, 0, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = (unsigned short) htons(port);
      serv_addr.sin_addr.s_addr = INADDR_ANY;

      if (::bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0)
        THROW_RUNTIME_ERROR("binding to port "+std::to_string((long long)port)+" failed");

      /*! listen to port, up to 5 pending connections */
      if (::listen(sockfd,5) < 0)
        THROW_RUNTIME_ERROR("listening on socket failed");

      return (socket_t) new buffered_socket_t(sockfd);
    }

    socket_t listen(socket_t hsock)
    {
      SOCKET sockfd = ((buffered_socket_t*) hsock)->fd;

      /*! accept incoming connection */
      struct sockaddr_in addr;
      socklen_t len = sizeof(addr);
      SOCKET fd = ::accept(sockfd, (struct sockaddr *) &addr, &len);
      if (fd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot accept connection");

      /*! enable TCP_NODELAY */
#ifdef TCP_NODELAY
      { int flag = 1; ::setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char*)&flag,sizeof(int)); }
#endif

      /*! we do not want SIGPIPE to be thrown */
#ifdef SO_NOSIGPIPE
      { int flag = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&flag, sizeof(int)); }
#endif

      return (socket_t) new buffered_socket_t(fd);
    }

    void read(socket_t hsock_i, void* data_i, size_t bytes)
    {
#if BUFFERING
      char* data = (char*)data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        if (hsock->istart == hsock->iend) {
          ssize_t n = ::recv(hsock->fd,hsock->ibuf,hsock->isize,MSG_NOSIGNAL);
          if      (n == 0) throw Disconnect();
          else if (n  < 0) THROW_RUNTIME_ERROR("error reading from socket");
          hsock->istart = 0;
          hsock->iend = n;
        }
        size_t bsize = hsock->iend-hsock->istart;
        if (bytes < bsize) bsize = bytes;
        memcpy(data,hsock->ibuf+hsock->istart,bsize);
        data += bsize;
        hsock->istart += bsize;
        bytes -= bsize;
      }
#else
      char* data = (char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        ssize_t n = ::read(hsock->fd,data,bytes);
        if      (n == 0) throw Disconnect();
        else if (n  < 0) THROW_RUNTIME_ERROR("error reading from socket");
        data+=n;
        bytes-=n;
      }
#endif
    }

    void write(socket_t hsock_i, const void* data_i, size_t bytes)
    {
#if BUFFERING
      const char* data = (const char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        if (hsock->oend == hsock->osize) flush(hsock_i);
        size_t bsize = hsock->osize-hsock->oend;
        if (bytes < bsize) bsize = bytes;
        memcpy(hsock->obuf+hsock->oend,data,bsize);
        data += bsize;
        hsock->oend += bsize;
        bytes -= bsize;
      }
#else
      const char* data = (const char*) data_i;
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      while (bytes) {
        ssize_t n = ::write(hsock->fd,data,bytes);
        if (n  < 0) THROW_RUNTIME_ERROR("error writing to socket");
        data+=n;
        bytes-=n;
      }
#endif
    }

    void flush(socket_t hsock_i)
    {
#if BUFFERING
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      char* data = hsock->obuf;
      size_t bytes = hsock->oend;
      while (bytes > 0) {
        ssize_t n = ::send(hsock->fd,data,(int)bytes,MSG_NOSIGNAL);
        if (n < 0) THROW_RUNTIME_ERROR("error writing to socket");
        bytes -= n;
        data += n;
      }
      hsock->oend = 0;
#endif
    }

    void close(socket_t hsock_i) {
      buffered_socket_t* hsock = (buffered_socket_t*) hsock_i;
      ::shutdown(hsock->fd,SHUT_RDWR);
      delete hsock;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
/// All Platforms
////////////////////////////////////////////////////////////////////////////////

namespace embree
{
  namespace network
  {
    bool read_bool(socket_t socket)
    {
      bool value = 0;
      read(socket,&value,sizeof(bool));
      return value;
    }

    char read_char(socket_t socket)
    {
      char value = 0;
      read(socket,&value,sizeof(char));
      return value;
    }

    int read_int(socket_t socket)
    {
      int value = 0;
      read(socket,&value,sizeof(int));
      return value;
    }

    float read_float(socket_t socket)
    {
      float value = 0.0f;
      read(socket,&value,sizeof(float));
      return value;
    }

    std::string read_string(socket_t socket)
    {
      int bytes = read_int(socket);
      char* str = new char[bytes+1];
      read(socket,str,bytes);
      str[bytes] = 0x00;
      std::string s(str);
      delete[] str;
      return s;
    }

    void write(socket_t socket, bool value) {
      write(socket,&value,sizeof(bool));
    }

    void write(socket_t socket, char value) {
      write(socket,&value,sizeof(char));
    }

    void write(socket_t socket, int value) {
      write(socket,&value,sizeof(int));
    }

    void write(socket_t socket, float value) {
      write(socket,&value,sizeof(float));
    }

    void write(socket_t socket, const std::string& str) {
      write(socket,(int)str.size());
      write(socket,str.c_str(),str.size());
    }
  }
}
<|endoftext|>


<commit_before>//
//  OricTAP.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 10/10/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#include "OricTAP.hpp"

#include <sys/stat.h>

using namespace Storage::Tape;

OricTAP::OricTAP(const char *file_name) : _file(NULL)
{
	struct stat file_stats;
	stat(file_name, &file_stats);
	_file_length = (size_t)file_stats.st_size;

	_file = fopen(file_name, "rb");

	if(!_file)
		throw ErrorNotOricTAP;

	// read and check the file signature
	uint8_t signature[4];
	if(fread(signature, 1, 4, _file) != 4)
		throw ErrorNotOricTAP;

	if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24)
		throw ErrorNotOricTAP;

	// then rewind and start again
	virtual_reset();
}

OricTAP::~OricTAP()
{
	if(_file) fclose(_file);
}

void OricTAP::virtual_reset()
{
//	fseek(_file, 0, SEEK_SET);
	_bit_count = 13;
	_phase = _next_phase = LeadIn;
	_phase_counter = 0;
	_pulse_counter = 0;
}

Tape::Pulse OricTAP::virtual_get_next_pulse()
{
	// Each byte byte is written as 13 bits: 0, eight bits of data, parity, three 1s.
	if(_bit_count == 13)
	{
		if(_next_phase != _phase)
		{
			_phase = _next_phase;
			_phase_counter = 0;
		}

		_bit_count = 0;
		uint8_t next_byte = 0;
		switch(_phase)
		{
			case LeadIn:
				next_byte = _phase_counter < 256 ? 0x16 : 0x24;
				_phase_counter++;
				if(_phase_counter == 259)	// 256 artificial bytes plus the three in the file = 259
				{
					_next_phase = Header;
				}
			break;

			case Header:
				// Counts are relative to:
				// [0, 1]:		"two bytes unused" (on the Oric 1)
				// 2:			program type
				// 3:			auto indicator
				// [4, 5]:		end address of data
				// [6, 7]:		start address of data
				// 8:			"unused" (on the Oric 1)
				// [9...]:		filename, up to NULL byte
				next_byte = (uint8_t)fgetc(_file);

				if(_phase_counter == 4)	_data_end_address = (uint16_t)(next_byte << 8);
				if(_phase_counter == 5)	_data_end_address |= next_byte;
				if(_phase_counter == 6)	_data_start_address = (uint16_t)(next_byte << 8);
				if(_phase_counter == 7)	_data_start_address |= next_byte;

				_phase_counter++;
				if(_phase_counter >= 9 && !next_byte)	// advance after the filename-ending NULL byte
				{
					_next_phase = Gap;
				}
			break;

			case Gap:
				_phase_counter++;
				if(_phase_counter == 8)
				{
					_next_phase = Data;
				}
			break;

			case Data:
				next_byte = (uint8_t)fgetc(_file);
				_phase_counter++;
				if(_phase_counter == (_data_end_address - _data_start_address))
				{
					_phase_counter = 0;
					if((size_t)ftell(_file) == _file_length)
					{
						_next_phase = End;
					}
					else
					{
						_next_phase = LeadIn;
					}
				}
			break;

			case End:
			break;
		}

		uint8_t parity = next_byte;
		parity ^= (parity >> 4);
		parity ^= (parity >> 2);
		parity ^= (parity >> 1);
		_current_value = (uint16_t)(((uint16_t)next_byte << 1) | ((parity&1) << 9) | (7 << 10));
	}

	// In slow mode, a 0 is 4 periods of 1200 Hz, a 1 is 8 periods at 2400 Hz.
	// In fast mode, a 1 is a single period of 2400 Hz, a 0 is a 2400 Hz pulse followed by a 1200 Hz pulse.
	// This code models fast mode.
	Tape::Pulse pulse;
	pulse.length.clock_rate = 4800;
	int next_bit;

	switch(_phase)
	{
		case End:
			pulse.type = Pulse::Zero;
			pulse.length.length = 4800;
		return pulse;

		case Gap:
			next_bit = 1;
		break;

		default:
			next_bit = _current_value & 1;
		break;
	}

	if(next_bit)
	{
		pulse.length.length = 1;
	}
	else
	{
		pulse.length.length = _pulse_counter ? 2 : 1;
	}
	pulse.type = _pulse_counter ? Pulse::High : Pulse::Low;	// TODO

	_pulse_counter ^= 1;
	if(!_pulse_counter)
	{
		_current_value >>= 1;
		_bit_count++;
	}
	return pulse;
}

bool OricTAP::is_at_end()
{
	return _phase == End;
}
<commit_msg>First successful game loaded! It turns out exactly one '$' is correct. Probably.<commit_after>//
//  OricTAP.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 10/10/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#include "OricTAP.hpp"

#include <sys/stat.h>

using namespace Storage::Tape;

OricTAP::OricTAP(const char *file_name) : _file(NULL)
{
	struct stat file_stats;
	stat(file_name, &file_stats);
	_file_length = (size_t)file_stats.st_size;

	_file = fopen(file_name, "rb");

	if(!_file)
		throw ErrorNotOricTAP;

	// read and check the file signature
	uint8_t signature[4];
	if(fread(signature, 1, 4, _file) != 4)
		throw ErrorNotOricTAP;

	if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24)
		throw ErrorNotOricTAP;

	// then rewind and start again
	virtual_reset();
}

OricTAP::~OricTAP()
{
	if(_file) fclose(_file);
}

void OricTAP::virtual_reset()
{
//	fseek(_file, 0, SEEK_SET);
	_bit_count = 13;
	_phase = _next_phase = LeadIn;
	_phase_counter = 0;
	_pulse_counter = 0;
}

Tape::Pulse OricTAP::virtual_get_next_pulse()
{
	// Each byte byte is written as 13 bits: 0, eight bits of data, parity, three 1s.
	if(_bit_count == 13)
	{
		if(_next_phase != _phase)
		{
			_phase = _next_phase;
			_phase_counter = 0;
		}

		_bit_count = 0;
		uint8_t next_byte = 0;
		switch(_phase)
		{
			case LeadIn:
				next_byte = _phase_counter < 258 ? 0x16 : 0x24;
				_phase_counter++;
				if(_phase_counter == 259)	// 256 artificial bytes plus the three in the file = 259
				{
					_next_phase = Header;
				}
			break;

			case Header:
				// Counts are relative to:
				// [0, 1]:		"two bytes unused" (on the Oric 1)
				// 2:			program type
				// 3:			auto indicator
				// [4, 5]:		end address of data
				// [6, 7]:		start address of data
				// 8:			"unused" (on the Oric 1)
				// [9...]:		filename, up to NULL byte
				next_byte = (uint8_t)fgetc(_file);

				if(_phase_counter == 4)	_data_end_address = (uint16_t)(next_byte << 8);
				if(_phase_counter == 5)	_data_end_address |= next_byte;
				if(_phase_counter == 6)	_data_start_address = (uint16_t)(next_byte << 8);
				if(_phase_counter == 7)	_data_start_address |= next_byte;

				_phase_counter++;
				if(_phase_counter >= 9 && !next_byte)	// advance after the filename-ending NULL byte
				{
					_next_phase = Gap;
				}
			break;

			case Gap:
				_phase_counter++;
				if(_phase_counter == 8)
				{
					_next_phase = Data;
				}
			break;

			case Data:
				next_byte = (uint8_t)fgetc(_file);
				_phase_counter++;
				if(_phase_counter == (_data_end_address - _data_start_address))
				{
					_phase_counter = 0;
					if((size_t)ftell(_file) == _file_length)
					{
						_next_phase = End;
					}
					else
					{
						_next_phase = LeadIn;
					}
				}
			break;

			case End:
			break;
		}

		uint8_t parity = next_byte;
		parity ^= (parity >> 4);
		parity ^= (parity >> 2);
		parity ^= (parity >> 1);
		_current_value = (uint16_t)(((uint16_t)next_byte << 1) | ((parity&1) << 9) | (7 << 10));
	}

	// In slow mode, a 0 is 4 periods of 1200 Hz, a 1 is 8 periods at 2400 Hz.
	// In fast mode, a 1 is a single period of 2400 Hz, a 0 is a 2400 Hz pulse followed by a 1200 Hz pulse.
	// This code models fast mode.
	Tape::Pulse pulse;
	pulse.length.clock_rate = 4800;
	int next_bit;

	switch(_phase)
	{
		case End:
			pulse.type = Pulse::Zero;
			pulse.length.length = 4800;
		return pulse;

		case Gap:
			next_bit = 1;
		break;

		default:
			next_bit = _current_value & 1;
		break;
	}

	if(next_bit)
	{
		pulse.length.length = 1;
	}
	else
	{
		pulse.length.length = _pulse_counter ? 2 : 1;
	}
	pulse.type = _pulse_counter ? Pulse::High : Pulse::Low;	// TODO

	_pulse_counter ^= 1;
	if(!_pulse_counter)
	{
		_current_value >>= 1;
		_bit_count++;
	}
	return pulse;
}

bool OricTAP::is_at_end()
{
	return _phase == End;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009-2010, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#ifndef LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX
# define LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX

# include <vector>

# include <boost/optional.hpp>

# include <libport/arpa/inet.h>
# include <libport/foreach.hh>
# include <libport/hierarchy.hh>
# include <libport/meta.hh>
# include <libport/symbol.hh>
# include <serialize/fwd.hh>

namespace libport
{
  namespace serialize
  {
    /*----------------.
    | Generic class.  |
    `----------------*/
    template <typename T>
    struct ICImpl
    {
      static T
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        return T(ser);
      }
    };

    /*------------.
    | Hierarchy.  |
    `------------*/
    template <typename T>
    struct ISerialize
    {
      static void
      res(const std::pair<typename T::root**, BinaryISerializer*>& c)
      {
        *c.first = reinterpret_cast<typename T::root*>(new T(*c.second));
      }
    };

    template <typename T>
    struct IHImpl
    {
      static T*
      get(const std::string&,
          std::istream&, BinaryISerializer& ser)
      {
        unsigned id = ser.unserialize<unsigned char>("id");
        typename T::root* res = 0;
        typedef std::pair<typename T::root**, BinaryISerializer*> Cookie;
        Cookie c(&res, &ser);
        T::template dispatch<ISerialize, Cookie>(id, c);
        return reinterpret_cast<T*>(res);
      }
    };

    /*-----------.
    | Fallback.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::Impl
    {
      static typename meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
                               T*, T>::res
      get(const std::string& name,
          std::istream& output, BinaryISerializer& ser)
      {
        return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
          IHImpl<T>, ICImpl<T> >::res::get(name, output, ser);
      }
    };

    /*-------.
    | char.  |
    `-------*/
    template <>
    struct BinaryISerializer::Impl<char>
    {
      static char get(const std::string&, std::istream& input,
                      BinaryISerializer&)
      {
        char res;
        input.read(&res, sizeof(char));
        return res;
      }
    };

    /*---------------------.
    | unsigned int/short.  |
    `---------------------*/
# define SERIALIZE_NET_INTEGRAL(Type, Function)                         \
    template <>                                                         \
    struct BinaryISerializer::Impl<Type>                                \
    {                                                                   \
      static Type                                                       \
      get(const std::string&, std::istream& input,                      \
          BinaryISerializer&)                                           \
      {                                                                 \
        Type normalized;                                                \
        input.read(reinterpret_cast<char*>(&normalized), sizeof(Type)); \
        return Function(normalized);                                    \
      }                                                                 \
    };

    SERIALIZE_NET_INTEGRAL(unsigned int,   ntohl);
    SERIALIZE_NET_INTEGRAL(unsigned short, ntohs);
#undef SERIALIZE_NET_INTEGRAL

    /*---------.
    | double.  |
    `---------*/
    template <>
    struct BinaryISerializer::Impl<double>
    {
      static double get(const std::string&, std::istream& input,
                        BinaryISerializer&)
      {
        // FIXME: non-portable
        double res;
        input.read(reinterpret_cast<char*>(&res), sizeof(double));
        return res;
      }
    };

/// Define the handling of To using the implementation for From.
#define BOUNCE(From, To)                                                \
    template <>                                                         \
    struct BinaryISerializer::Impl<From>                                \
    {                                                                   \
      static From get(const std::string& name,                          \
                      std::istream& input, BinaryISerializer& ser)      \
      {                                                                 \
        return static_cast<From>(Impl<To>::get(name, input, ser));      \
      }                                                                 \
    }

    BOUNCE(bool,          char);
    BOUNCE(unsigned char, char);
    BOUNCE(int,           unsigned);
    BOUNCE(short,         unsigned short);
#undef BOUNCE

    /*-----------.
    | Pointers.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::PCImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        T* res = reinterpret_cast<T*>(new char[sizeof(T)]);
        ser.ptr_map_.push_back(res);
        // FIXME: copy ctor
        new (res) T(BinaryISerializer::Impl<T>::get("value", input, ser));
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::PHImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        unsigned id = ser.ptr_map_.size();
        ser.ptr_map_.push_back(0);
        // FIXME: loops
        T* res = BinaryISerializer::Impl<T>::get("value", input, ser);
        ser.ptr_map_[id] = res;
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::Impl<T*>
    {
      static T* get(const std::string&, std::istream& input,
                    BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
          {
            unsigned id = Impl<unsigned>::get("id", input, ser);
            assert_lt(id, ser.ptr_map_.size());
            return reinterpret_cast<T*>(ser.ptr_map_[id]);
          }
          case serialized:
          {
            return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
              PHImpl<T>, PCImpl<T> >::res
              ::res(ser, input);
          }
        }
        // GCC bug prevents this: unreachable();
        // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44580
        abort();
      }
    };

    /*-----------------.
    | Boost optional.  |
    `-----------------*/
    template <typename T>
    struct BinaryISerializer::Impl<boost::optional<T> >
    {
      typedef boost::optional<T> type;
      static type get(const std::string&, std::istream& input,
                      BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
            assert(!"Impossible 'cached' value "
                   "for a serialized boost::optional.");
          case serialized:
            return BinaryISerializer::Impl<T>::get("value", input, ser);
        }
        abort();
      }
    };

    /*------------.
    | std::pair.  |
    `------------*/
    template <typename A, typename B>
    struct BinaryISerializer::Impl<std::pair<A, B> >
    {
      typedef std::pair<A, B> type;
      static type get(const std::string&, std::istream& input,
                      BinaryISerializer& ser)
      {
        A first  = BinaryISerializer::Impl<A>::get("first",  input, ser);
        B second = BinaryISerializer::Impl<A>::get("second", input, ser);
        return type(first, second);
      }
    };

    /*--------------.
    | std::string.  |
    `--------------*/
    template <>
    struct BinaryISerializer::Impl<std::string>
    {
      static std::string get(const std::string& name, std::istream& input,
                             BinaryISerializer& ser)
      {
        size_t l = Impl<unsigned short>::get(name, input, ser);
        // FIXME: alloca
        char* buf = new char[l];
        input.read(buf, std::streamsize(l));
        std::string res(buf, l);
        delete [] buf;
        return res;
      }
    };

    /*--------------.
    | std::vector.  |
    `--------------*/
    template <typename T, typename A>
    struct BinaryISerializer::Impl<std::vector<T, A> >
    {
      static std::vector<T, A>
      get(const std::string& name,
          std::istream& input, BinaryISerializer& ser)
      {
        unsigned short size = Impl<unsigned short>::get(name, input, ser);
        std::vector<T, A> res;
        for (unsigned i = 0; i < size; ++i)
          res.push_back(Impl<T>::get(name, input, ser));
        return res;
      }
    };

    // Hash and Symbol serialization is defined here because of
    // serialization/hash/symbol dependency loop.

    /*------------------.
    | libport::Symbol.  |
    `------------------*/
    template <>
    struct BinaryISerializer::Impl<libport::Symbol>
    {
      static libport::Symbol
      get(const std::string& name, std::istream& input,
          BinaryISerializer& ser)
      {
        bool cached = Impl<bool>::get("opt", input, ser);
        if (cached)
        {
          unsigned id = Impl<unsigned>::get("id", input, ser);
          return ser.sym_map_[id];
        }
        Symbol res(Impl<std::string>::get(name, input, ser));
        ser.sym_map_.push_back(res);
        return res;
      }
    };

    /*----------------.
    | libport::hash.  |
    `----------------*/
    template <typename K, typename V>
    struct BinaryISerializer::Impl<boost::unordered_map<K, V> >
    {
      typedef boost::unordered_map<K, V> result_type;
      static result_type
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        typedef typename result_type::value_type Value;
        size_t size = ser.unserialize<unsigned short>("size");
        result_type res;
        for (unsigned i = 0; i < size; ++i)
        {
          K k = ser.template unserialize<K>("key");
          V v = ser.template unserialize<V>("value");
          res[k] = v;
        }
        return res;
      }
    };
  }
}

#endif
<commit_msg>serializer: fix typo.<commit_after>/*
 * Copyright (C) 2009-2010, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#ifndef LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX
# define LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX

# include <vector>

# include <boost/optional.hpp>

# include <libport/arpa/inet.h>
# include <libport/foreach.hh>
# include <libport/hierarchy.hh>
# include <libport/meta.hh>
# include <libport/symbol.hh>
# include <serialize/fwd.hh>

namespace libport
{
  namespace serialize
  {
    /*----------------.
    | Generic class.  |
    `----------------*/
    template <typename T>
    struct ICImpl
    {
      static T
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        return T(ser);
      }
    };

    /*------------.
    | Hierarchy.  |
    `------------*/
    template <typename T>
    struct ISerialize
    {
      static void
      res(const std::pair<typename T::root**, BinaryISerializer*>& c)
      {
        *c.first = reinterpret_cast<typename T::root*>(new T(*c.second));
      }
    };

    template <typename T>
    struct IHImpl
    {
      static T*
      get(const std::string&,
          std::istream&, BinaryISerializer& ser)
      {
        unsigned id = ser.unserialize<unsigned char>("id");
        typename T::root* res = 0;
        typedef std::pair<typename T::root**, BinaryISerializer*> Cookie;
        Cookie c(&res, &ser);
        T::template dispatch<ISerialize, Cookie>(id, c);
        return reinterpret_cast<T*>(res);
      }
    };

    /*-----------.
    | Fallback.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::Impl
    {
      static typename meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
                               T*, T>::res
      get(const std::string& name,
          std::istream& output, BinaryISerializer& ser)
      {
        return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
          IHImpl<T>, ICImpl<T> >::res::get(name, output, ser);
      }
    };

    /*-------.
    | char.  |
    `-------*/
    template <>
    struct BinaryISerializer::Impl<char>
    {
      static char get(const std::string&, std::istream& input,
                      BinaryISerializer&)
      {
        char res;
        input.read(&res, sizeof(char));
        return res;
      }
    };

    /*---------------------.
    | unsigned int/short.  |
    `---------------------*/
# define SERIALIZE_NET_INTEGRAL(Type, Function)                         \
    template <>                                                         \
    struct BinaryISerializer::Impl<Type>                                \
    {                                                                   \
      static Type                                                       \
      get(const std::string&, std::istream& input,                      \
          BinaryISerializer&)                                           \
      {                                                                 \
        Type normalized;                                                \
        input.read(reinterpret_cast<char*>(&normalized), sizeof(Type)); \
        return Function(normalized);                                    \
      }                                                                 \
    };

    SERIALIZE_NET_INTEGRAL(unsigned int,   ntohl);
    SERIALIZE_NET_INTEGRAL(unsigned short, ntohs);
#undef SERIALIZE_NET_INTEGRAL

    /*---------.
    | double.  |
    `---------*/
    template <>
    struct BinaryISerializer::Impl<double>
    {
      static double get(const std::string&, std::istream& input,
                        BinaryISerializer&)
      {
        // FIXME: non-portable
        double res;
        input.read(reinterpret_cast<char*>(&res), sizeof(double));
        return res;
      }
    };

/// Define the handling of To using the implementation for From.
#define BOUNCE(From, To)                                                \
    template <>                                                         \
    struct BinaryISerializer::Impl<From>                                \
    {                                                                   \
      static From get(const std::string& name,                          \
                      std::istream& input, BinaryISerializer& ser)      \
      {                                                                 \
        return static_cast<From>(Impl<To>::get(name, input, ser));      \
      }                                                                 \
    }

    BOUNCE(bool,          char);
    BOUNCE(unsigned char, char);
    BOUNCE(int,           unsigned);
    BOUNCE(short,         unsigned short);
#undef BOUNCE

    /*-----------.
    | Pointers.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::PCImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        T* res = reinterpret_cast<T*>(new char[sizeof(T)]);
        ser.ptr_map_.push_back(res);
        // FIXME: copy ctor
        new (res) T(BinaryISerializer::Impl<T>::get("value", input, ser));
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::PHImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        unsigned id = ser.ptr_map_.size();
        ser.ptr_map_.push_back(0);
        // FIXME: loops
        T* res = BinaryISerializer::Impl<T>::get("value", input, ser);
        ser.ptr_map_[id] = res;
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::Impl<T*>
    {
      static T* get(const std::string&, std::istream& input,
                    BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
          {
            unsigned id = Impl<unsigned>::get("id", input, ser);
            assert_lt(id, ser.ptr_map_.size());
            return reinterpret_cast<T*>(ser.ptr_map_[id]);
          }
          case serialized:
          {
            return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
              PHImpl<T>, PCImpl<T> >::res
              ::res(ser, input);
          }
        }
        // GCC bug prevents this: unreachable();
        // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44580
        abort();
      }
    };

    /*-----------------.
    | Boost optional.  |
    `-----------------*/
    template <typename T>
    struct BinaryISerializer::Impl<boost::optional<T> >
    {
      typedef boost::optional<T> type;
      static type get(const std::string&, std::istream& input,
                      BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
            assert(!"Impossible 'cached' value "
                   "for a serialized boost::optional.");
          case serialized:
            return BinaryISerializer::Impl<T>::get("value", input, ser);
        }
        abort();
      }
    };

    /*------------.
    | std::pair.  |
    `------------*/
    template <typename A, typename B>
    struct BinaryISerializer::Impl<std::pair<A, B> >
    {
      typedef std::pair<A, B> type;
      static type get(const std::string&, std::istream& input,
                      BinaryISerializer& ser)
      {
        A first  = BinaryISerializer::Impl<A>::get("first",  input, ser);
        B second = BinaryISerializer::Impl<B>::get("second", input, ser);
        return type(first, second);
      }
    };

    /*--------------.
    | std::string.  |
    `--------------*/
    template <>
    struct BinaryISerializer::Impl<std::string>
    {
      static std::string get(const std::string& name, std::istream& input,
                             BinaryISerializer& ser)
      {
        size_t l = Impl<unsigned short>::get(name, input, ser);
        // FIXME: alloca
        char* buf = new char[l];
        input.read(buf, std::streamsize(l));
        std::string res(buf, l);
        delete [] buf;
        return res;
      }
    };

    /*--------------.
    | std::vector.  |
    `--------------*/
    template <typename T, typename A>
    struct BinaryISerializer::Impl<std::vector<T, A> >
    {
      static std::vector<T, A>
      get(const std::string& name,
          std::istream& input, BinaryISerializer& ser)
      {
        unsigned short size = Impl<unsigned short>::get(name, input, ser);
        std::vector<T, A> res;
        for (unsigned i = 0; i < size; ++i)
          res.push_back(Impl<T>::get(name, input, ser));
        return res;
      }
    };

    // Hash and Symbol serialization is defined here because of
    // serialization/hash/symbol dependency loop.

    /*------------------.
    | libport::Symbol.  |
    `------------------*/
    template <>
    struct BinaryISerializer::Impl<libport::Symbol>
    {
      static libport::Symbol
      get(const std::string& name, std::istream& input,
          BinaryISerializer& ser)
      {
        bool cached = Impl<bool>::get("opt", input, ser);
        if (cached)
        {
          unsigned id = Impl<unsigned>::get("id", input, ser);
          return ser.sym_map_[id];
        }
        Symbol res(Impl<std::string>::get(name, input, ser));
        ser.sym_map_.push_back(res);
        return res;
      }
    };

    /*----------------.
    | libport::hash.  |
    `----------------*/
    template <typename K, typename V>
    struct BinaryISerializer::Impl<boost::unordered_map<K, V> >
    {
      typedef boost::unordered_map<K, V> result_type;
      static result_type
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        typedef typename result_type::value_type Value;
        size_t size = ser.unserialize<unsigned short>("size");
        result_type res;
        for (unsigned i = 0; i < size; ++i)
        {
          K k = ser.template unserialize<K>("key");
          V v = ser.template unserialize<V>("value");
          res[k] = v;
        }
        return res;
      }
    };
  }
}

#endif
<|endoftext|>


<commit_before>#ifndef __CERBERUS_COMMON_HPP__
#define __CERBERUS_COMMON_HPP__

#include <chrono>

#define VERSION "0.7.11-2017-03-29"

namespace cerb {

    using byte = unsigned char;
    using rint = int64_t;
    using slot = unsigned int;
    using msize_t = uint64_t;

    typedef std::chrono::high_resolution_clock Clock;
    typedef Clock::time_point Time;
    typedef std::chrono::duration<double> Interval;

    constexpr msize_t CLUSTER_SLOT_COUNT = 16384;

}

#endif /* __CERBERUS_COMMON_HPP__ */
<commit_msg>Version: 0.0.8<commit_after>#ifndef __CERBERUS_COMMON_HPP__
#define __CERBERUS_COMMON_HPP__

#include <chrono>

#define VERSION "0.8.0-2018-05-02"

namespace cerb {

    using byte = unsigned char;
    using rint = int64_t;
    using slot = unsigned int;
    using msize_t = uint64_t;

    typedef std::chrono::high_resolution_clock Clock;
    typedef Clock::time_point Time;
    typedef std::chrono::duration<double> Interval;

    constexpr msize_t CLUSTER_SLOT_COUNT = 16384;

}

#endif /* __CERBERUS_COMMON_HPP__ */
<|endoftext|>


<commit_before>/*
 *  Author:
 *     Guido Tack <guido.tack@monash.edu>
 *
 *  Copyright:
 *     NICTA 2013
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "project.h"
#include "moocsubmission.h"
#include "solverdialog.h"
#include "process.h"

#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QMessageBox>
#include <QSortFilterProxyModel>
#include <QJsonDocument>
#include <QJsonArray>

Project::Project(const QList<SolverConfiguration*>& configs, QObject* parent) : QObject(parent), solverConfigs(configs)
{
    itemModel = new QStandardItemModel(this);
    itemModel->setColumnCount(1);

    connect(itemModel, &QStandardItemModel::itemChanged, this, &Project::on_itemChanged);

    rootItem = new QStandardItem(QIcon(":/images/mznicon.png"), "Untitled Project");
    rootItem->setData(NodeType::ProjectFile, Role::Type);
    rootItem->setData("Untitled Project", Role::OriginalLabel);
    rootItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);

    auto font = rootItem->font();
    font.setBold(true);

    modelsItem = new QStandardItem("Models");
    modelsItem->setData(NodeType::Group, Role::Type);
    modelsItem->setFlags(Qt::NoItemFlags);
    modelsItem->setFont(font);

    dataItem = new QStandardItem("Data (right click to run)");
    dataItem->setData(NodeType::Group, Role::Type);
    dataItem->setFlags(Qt::NoItemFlags);
    dataItem->setFont(font);

    checkersItem = new QStandardItem("Checkers (right click to run)");
    checkersItem->setData(NodeType::Group, Role::Type);
    checkersItem->setFlags(Qt::NoItemFlags);
    checkersItem->setFont(font);

    configsItem = new QStandardItem("Solver configurations");
    configsItem->setData(NodeType::Group, Role::Type);
    configsItem->setFlags(Qt::NoItemFlags);
    configsItem->setFont(font);

    otherItem = new QStandardItem("Other files");
    otherItem->setData(NodeType::Group, Role::Type);
    otherItem->setFlags(Qt::NoItemFlags);
    otherItem->setFont(font);

    rootItem->appendRow(modelsItem);
    rootItem->appendRow(dataItem);
    rootItem->appendRow(checkersItem);
    rootItem->appendRow(configsItem);
    rootItem->appendRow(otherItem);
    itemModel->appendRow(rootItem);
}

QStringList Project::loadProject(const QString& file, ConfigWindow* configWindow)
{
    clear();

    projectFile(file);

    QStringList warnings;

    QFile f(file);
    QFileInfo fi(f);
    if (!f.open(QFile::ReadOnly)) {
        throw FileError("Failed to open project file");
    }

    auto doc = QJsonDocument::fromJson(f.readAll());

    if (doc.isObject()) {
        loadJSON(doc.object(), fi, configWindow, warnings);
    } else {
        f.reset();
        QDataStream in(&f);
        loadLegacy(in, fi, configWindow, warnings);
    }

    f.close();

    for (auto& sc : solverConfigurationFiles()) {
        configWindow->addConfig(sc);
    }

    if (!selectedBuiltinConfigId.isEmpty()) {
        int index = configWindow->findBuiltinConfig(selectedBuiltinConfigId, selectedBuiltinConfigVersion);
        if (index == -1) {
            warnings << "Could not find solver " + selectedBuiltinConfigId + "@" + selectedBuiltinConfigVersion;
        } else {
            configWindow->setCurrentIndex(index);
        }
    } else if (!selectedSolverConfigFile.isEmpty()) {
        int index = configWindow->findConfigFile(rootDir().absolutePath() + "/" + selectedSolverConfigFile);
        configWindow->setCurrentIndex(index);
    }

    setModified(false);

    return warnings;
}

void Project::loadJSON(const QJsonObject& obj, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    int version = obj["version"].toInt();

    QString basePath = fi.absolutePath() + "/";

    auto of = obj["openFiles"].toArray();
    for (auto file : of) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            openTabs << path;
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }

    openTabIndex = obj["openTab"].toInt();

    QList<SolverConfiguration*> configs;
    if (obj["builtinSolverConfigs"].isArray()) {
        for (auto config : obj["builtinSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver builtin solver config";
                continue;
            }
            SolverConfiguration* loaded;
            if (version >= 106) {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadJSON(QJsonDocument(config.toObject())));
            } else {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            }
            loaded->isBuiltin = true;
            configs << loaded;
        }
    }

    if (obj["projectSolverConfigs"].isArray()) {
        for (auto config : obj["projectSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver project solver config";
                continue;
            }
            auto loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            loaded->modified = true;
            configs << loaded;
        }
    }

    configWindow->mergeConfigs(configs);

    if (obj["selectedBuiltinConfigId"].isString()) {
        selectedBuiltinConfigId = obj["selectedBuiltinConfigId"].toString();
        selectedBuiltinConfigVersion = obj["selectedBuiltinConfigVersion"].toString();
        selectedSolverConfigFile = "";

    } else if (obj["selectedSolverConfigFile"].isString()) {
        selectedSolverConfigFile = obj["selectedSolverConfigFile"].toString();
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    } /*else {
        warnings << "No selected solver config in project";
    }*/

    for (auto file : obj["projectFiles"].toArray()) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            add(path);
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }
}

void Project::loadLegacy(QDataStream& in, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    // Load old binary format of project
    throw InternalError("This project format is no longer supported. Please use MiniZinc IDE version 2.4 to upgrade it.");
}

void Project::saveProject()
{
    QJsonObject confObject;
    confObject["version"] = 106;

    // Save the currently open tabs
    QStringList of;
    for (auto& f: openTabs) {
         of << relativeToProject(f);
    }
    confObject["openFiles"] = QJsonArray::fromStringList(of);
    confObject["openTab"] = openTabIndex;

    // Save paths of all project files
    QStringList relativeFilePaths;
    for (auto& file : files()) {
        relativeFilePaths << relativeToProject(file);
    }
    confObject["projectFiles"] = QJsonArray::fromStringList(relativeFilePaths);

    // Save which config is currently selected
    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        confObject["selectedBuiltinConfigId"] = selectedBuiltinConfigId;
        confObject["selectedBuiltinConfigVersion"] = selectedBuiltinConfigVersion;
    } else if (!selectedSolverConfigFile.isEmpty()){
        confObject["selectedSolverConfigFile"] = selectedSolverConfigFile;
    }

    // Write project file
    QJsonDocument doc(confObject);
    QFile file(projectFile());
    if (!file.open(QFile::WriteOnly)) {
        throw FileError("Failed to write file");
    }
    file.write(doc.toJson());
    file.close();

    setModified(false);
}

void Project::add(const QString& fileName)
{
    auto abs = QFileInfo(fileName).absoluteFilePath();
    auto path = relativeToProject(fileName);
    if (contains(abs)) {
        return;
    }

    auto parts = path.split("/", QString::SkipEmptyParts); // Qt always uses / as the path separator
    auto file = parts.takeLast();

    QStandardItem* node = otherItem;
    QString icon;
    NodeType type = NodeType::Other;
    if (file.endsWith(".mzc.mzn") || file.endsWith(".mzc")) {
        node = checkersItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Checker;
    } else if (file.endsWith(".mzn")) {
        node = modelsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Model;
    } else if (file.endsWith(".dzn") || file.endsWith(".json")) {
        node = dataItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Data;
    } else if (file.endsWith(".mpc")) {
        node = configsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::SolverConfig;
    } else if (file == "_mooc" || file == "_coursera") {
        if (mooc) {
            delete mooc;
        }
        mooc = new MOOCAssignment(fileName);
        emit moocChanged(mooc);
    }

    node->setFlags(Qt::ItemIsEnabled);

    // Traverse existing path items
    int i = 0;
    while (!parts.empty() && i < node->rowCount()) {
        if (getType(node->child(i)->index()) == NodeType::Dir &&
                node->child(i)->text() == parts.first()) {
            parts.pop_front();
            node = node->child(i);
            i = 0;
        } else {
            i++;
        }
    }
    // Create new path items
    for (auto& part : parts) {
        auto dir = new QStandardItem(QIcon(":/icons/images/folder.png"), part);
        dir->setData(NodeType::Dir, Role::Type);
        dir->setFlags(Qt::ItemIsEnabled);
        node->appendRow(dir);
        node->sortChildren(0);
        node = dir;
    }
    // Add file item
    auto item = new QStandardItem(QIcon(icon), file);
    item->setData(type, Role::Type);
    item->setData(abs, Role::Path);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    node->appendRow(item);
    node->sortChildren(0);

    entries.insert(abs, item);

    if (!projectFile().isEmpty()) {
        setModified(true);
    }
}

void Project::add(const QStringList& fileNames)
{
    for (auto& fileName : fileNames) {
        add(fileName);
    }
}

void Project::remove(const QString &fileName)
{
    auto path = QFileInfo(fileName).absoluteFilePath();
    if (!contains(path)) {
        return;
    }

    // Make sure we also remove any unused dirs
    auto node = entries[path];
    if (node->text() == "_mooc" || node->text() == "_coursera") {
        delete mooc;
        mooc = nullptr;
        emit moocChanged(mooc);
    }
    while (getType(node->parent()->index()) == NodeType::Dir && node->parent()->rowCount() <= 1) {
        node = node->parent();
    }
    auto parent = node->parent();
    parent->removeRow(node->row());
    if (parent->data(Role::Type) == "group" && !parent->hasChildren()) {
        parent->setFlags(Qt::NoItemFlags);
    }
    entries.remove(path);

    if (!projectFile().isEmpty()) {
        setModified(true);
    }
}

void Project::remove(const QStringList &fileNames)
{
    for (auto& fileName : fileNames) {
        remove(fileName);
    }
}

void Project::remove(const QModelIndex& index)
{
    remove(getFileName(index));
}

void Project::remove(const QModelIndexList& indexes)
{
    for (auto& index : indexes) {
        remove(index);
    }
}

void Project::clear()
{
    modelsItem->removeRows(0, modelsItem->rowCount());
    dataItem->removeRows(0, dataItem->rowCount());
    checkersItem->removeRows(0, checkersItem->rowCount());
    otherItem->removeRows(0, otherItem->rowCount());
    entries.clear();
    setModified(true);
}

QStringList Project::files(void) const {
    return entries.keys();
}

QStringList Project::modelFiles(void) const {
    return getFiles(NodeType::Model);
}

QStringList Project::solverConfigurationFiles(void) const {
    return getFiles(NodeType::SolverConfig);
}

QStringList Project::dataFiles(void) const {
    return getFiles(NodeType::Data);
}

bool Project::contains(const QString &fileName)
{
    QFileInfo fi(fileName);
    return entries.contains(fi.absoluteFilePath());
}

void Project::setModified(bool m)
{
    modified = m;
    auto label = rootItem->data(Role::OriginalLabel).toString();
    if (modified) {
        rootItem->setText(label + " *");
    } else {
        rootItem->setText(label);
    }
}

void Project::projectFile(const QString& fileName)
{
    QStringList files = entries.keys();
    clear();
    if (fileName.isEmpty()) {
        rootItem->setText("Untitled Project");
        rootItem->setData("Untitled Project", Role::OriginalLabel);
        projFile = "";
    } else {
        QFileInfo fi(fileName);
        projFile = fi.absoluteFilePath();
        rootItem->setText(fi.fileName());
        rootItem->setData(fi.fileName(), Role::OriginalLabel);
    }
    add(files);
}

QDir Project::rootDir()
{
    QFileInfo fi(projectFile());
    return QDir(fi.absolutePath());
}

bool Project::hasProjectFile()
{
    return !projectFile().isEmpty();
}

Project::NodeType Project::getType(const QModelIndex& index)
{
    return static_cast<Project::NodeType>(model()->data(index, Role::Type).toInt());
}

QString Project::getFileName(const QModelIndex& index)
{
    return model()->data(index, Role::Path).toString();
}

QStringList Project::getFileNames(const QModelIndexList& indices)
{
    QStringList result;
    for (auto& index : indices) {
        result << getFileName(index);
    }
    return result;
}

QStringList Project::getFiles(NodeType type) const
{
    QStringList ret;
    for (auto it = entries.begin(); it != entries.end(); it++) {
        auto t = static_cast<NodeType>(it.value()->data(Role::Type).toInt());
        if (t == type) {
            ret << it.key();
        }
    }
    return ret;
}

QString Project::relativeToProject(const QString& fileName)
{
    QFileInfo fi(fileName);
    auto abs = fi.absoluteFilePath();
    return hasProjectFile() ? rootDir().relativeFilePath(abs) : abs;
}

void Project::openTabsChanged(const QStringList& files, int currentTab)
{
    openTabs.clear();
    for (auto& file : files) {
        auto abs = QFileInfo(file).absoluteFilePath();
        if (contains(abs)) {
            openTabs << abs;
        }
    }

    if (currentTab >= 0) {
        openTabIndex = openTabs.indexOf(QFileInfo(files[currentTab]).absoluteFilePath());
    } else {
        openTabIndex = -1;
    }

//    setModified(true);
}

void Project::activeSolverConfigChanged(const SolverConfiguration* sc)
{
    if (!sc) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    }
    if (sc->isBuiltin) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = sc->solverDefinition.id;
        selectedBuiltinConfigVersion = sc->solverDefinition.version;
    } else {
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
        selectedSolverConfigFile = relativeToProject(sc->paramFile);
    }
//    setModified(true);
}

void Project::on_itemChanged(QStandardItem* item)
{
    auto oldPath = item->data(Qt::UserRole + 1).toString();
    auto newName = item->text();
    if (oldPath.isEmpty() || oldPath.endsWith(newName)) {
        return;
    }
    QFileInfo fi(oldPath);
    auto target = fi.path() + "/" + item->text();
    if (QFile::rename(oldPath, target)) {
        remove(oldPath);
        add(target);
        emit fileRenamed(oldPath, target);
    } else {
        item->setText(fi.fileName());
    }
}
<commit_msg>Fix reading of currently selected solver config<commit_after>/*
 *  Author:
 *     Guido Tack <guido.tack@monash.edu>
 *
 *  Copyright:
 *     NICTA 2013
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "project.h"
#include "moocsubmission.h"
#include "solverdialog.h"
#include "process.h"

#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QMessageBox>
#include <QSortFilterProxyModel>
#include <QJsonDocument>
#include <QJsonArray>

Project::Project(const QList<SolverConfiguration*>& configs, QObject* parent) : QObject(parent), solverConfigs(configs)
{
    itemModel = new QStandardItemModel(this);
    itemModel->setColumnCount(1);

    connect(itemModel, &QStandardItemModel::itemChanged, this, &Project::on_itemChanged);

    rootItem = new QStandardItem(QIcon(":/images/mznicon.png"), "Untitled Project");
    rootItem->setData(NodeType::ProjectFile, Role::Type);
    rootItem->setData("Untitled Project", Role::OriginalLabel);
    rootItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);

    auto font = rootItem->font();
    font.setBold(true);

    modelsItem = new QStandardItem("Models");
    modelsItem->setData(NodeType::Group, Role::Type);
    modelsItem->setFlags(Qt::NoItemFlags);
    modelsItem->setFont(font);

    dataItem = new QStandardItem("Data (right click to run)");
    dataItem->setData(NodeType::Group, Role::Type);
    dataItem->setFlags(Qt::NoItemFlags);
    dataItem->setFont(font);

    checkersItem = new QStandardItem("Checkers (right click to run)");
    checkersItem->setData(NodeType::Group, Role::Type);
    checkersItem->setFlags(Qt::NoItemFlags);
    checkersItem->setFont(font);

    configsItem = new QStandardItem("Solver configurations");
    configsItem->setData(NodeType::Group, Role::Type);
    configsItem->setFlags(Qt::NoItemFlags);
    configsItem->setFont(font);

    otherItem = new QStandardItem("Other files");
    otherItem->setData(NodeType::Group, Role::Type);
    otherItem->setFlags(Qt::NoItemFlags);
    otherItem->setFont(font);

    rootItem->appendRow(modelsItem);
    rootItem->appendRow(dataItem);
    rootItem->appendRow(checkersItem);
    rootItem->appendRow(configsItem);
    rootItem->appendRow(otherItem);
    itemModel->appendRow(rootItem);
}

QStringList Project::loadProject(const QString& file, ConfigWindow* configWindow)
{
    clear();

    projectFile(file);

    QStringList warnings;

    QFile f(file);
    QFileInfo fi(f);
    if (!f.open(QFile::ReadOnly)) {
        throw FileError("Failed to open project file");
    }

    auto doc = QJsonDocument::fromJson(f.readAll());

    if (doc.isObject()) {
        loadJSON(doc.object(), fi, configWindow, warnings);
    } else {
        f.reset();
        QDataStream in(&f);
        loadLegacy(in, fi, configWindow, warnings);
    }

    f.close();

    // Save these because adding the configs can change them
    auto projectBuiltinConfigId = selectedBuiltinConfigId;
    auto projectBuiltinConfigVersion = selectedBuiltinConfigVersion;
    auto projectselectedSolverConfigFile = selectedSolverConfigFile;

    for (auto& sc : solverConfigurationFiles()) {
        configWindow->addConfig(sc);
    }

    if (!projectBuiltinConfigId.isEmpty()) {
        int index = configWindow->findBuiltinConfig(projectBuiltinConfigId, projectBuiltinConfigVersion);
        if (index == -1) {
            warnings << "Could not find solver " + projectBuiltinConfigId + "@" + projectBuiltinConfigVersion;
        } else {
            configWindow->setCurrentIndex(index);
        }
    } else if (!projectselectedSolverConfigFile.isEmpty()) {
        int index = configWindow->findConfigFile(rootDir().absolutePath() + "/" + projectselectedSolverConfigFile);
        configWindow->setCurrentIndex(index);
    }

    setModified(false);

    return warnings;
}

void Project::loadJSON(const QJsonObject& obj, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    int version = obj["version"].toInt();

    QString basePath = fi.absolutePath() + "/";

    auto of = obj["openFiles"].toArray();
    for (auto file : of) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            openTabs << path;
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }

    openTabIndex = obj["openTab"].toInt();

    QList<SolverConfiguration*> configs;
    if (obj["builtinSolverConfigs"].isArray()) {
        for (auto config : obj["builtinSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver builtin solver config";
                continue;
            }
            SolverConfiguration* loaded;
            if (version >= 106) {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadJSON(QJsonDocument(config.toObject())));
            } else {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            }
            loaded->isBuiltin = true;
            configs << loaded;
        }
    }

    if (obj["projectSolverConfigs"].isArray()) {
        for (auto config : obj["projectSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver project solver config";
                continue;
            }
            auto loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            loaded->modified = true;
            configs << loaded;
        }
    }

    configWindow->mergeConfigs(configs);

    if (obj["selectedBuiltinConfigId"].isString()) {
        selectedBuiltinConfigId = obj["selectedBuiltinConfigId"].toString();
        selectedBuiltinConfigVersion = obj["selectedBuiltinConfigVersion"].toString();
        selectedSolverConfigFile = "";

    } else if (obj["selectedSolverConfigFile"].isString()) {
        selectedSolverConfigFile = obj["selectedSolverConfigFile"].toString();
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    } /*else {
        warnings << "No selected solver config in project";
    }*/

    for (auto file : obj["projectFiles"].toArray()) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            add(path);
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }
}

void Project::loadLegacy(QDataStream& in, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    // Load old binary format of project
    throw InternalError("This project format is no longer supported. Please use MiniZinc IDE version 2.4 to upgrade it.");
}

void Project::saveProject()
{
    QJsonObject confObject;
    confObject["version"] = 106;

    // Save the currently open tabs
    QStringList of;
    for (auto& f: openTabs) {
         of << relativeToProject(f);
    }
    confObject["openFiles"] = QJsonArray::fromStringList(of);
    confObject["openTab"] = openTabIndex;

    // Save paths of all project files
    QStringList relativeFilePaths;
    for (auto& file : files()) {
        relativeFilePaths << relativeToProject(file);
    }
    confObject["projectFiles"] = QJsonArray::fromStringList(relativeFilePaths);

    // Save which config is currently selected
    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        confObject["selectedBuiltinConfigId"] = selectedBuiltinConfigId;
        confObject["selectedBuiltinConfigVersion"] = selectedBuiltinConfigVersion;
    } else if (!selectedSolverConfigFile.isEmpty()){
        confObject["selectedSolverConfigFile"] = selectedSolverConfigFile;
    }

    // Write project file
    QJsonDocument doc(confObject);
    QFile file(projectFile());
    if (!file.open(QFile::WriteOnly)) {
        throw FileError("Failed to write file");
    }
    file.write(doc.toJson());
    file.close();

    setModified(false);
}

void Project::add(const QString& fileName)
{
    auto abs = QFileInfo(fileName).absoluteFilePath();
    auto path = relativeToProject(fileName);
    if (contains(abs)) {
        return;
    }

    auto parts = path.split("/", QString::SkipEmptyParts); // Qt always uses / as the path separator
    auto file = parts.takeLast();

    QStandardItem* node = otherItem;
    QString icon;
    NodeType type = NodeType::Other;
    if (file.endsWith(".mzc.mzn") || file.endsWith(".mzc")) {
        node = checkersItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Checker;
    } else if (file.endsWith(".mzn")) {
        node = modelsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Model;
    } else if (file.endsWith(".dzn") || file.endsWith(".json")) {
        node = dataItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Data;
    } else if (file.endsWith(".mpc")) {
        node = configsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::SolverConfig;
    } else if (file == "_mooc" || file == "_coursera") {
        if (mooc) {
            delete mooc;
        }
        mooc = new MOOCAssignment(fileName);
        emit moocChanged(mooc);
    }

    node->setFlags(Qt::ItemIsEnabled);

    // Traverse existing path items
    int i = 0;
    while (!parts.empty() && i < node->rowCount()) {
        if (getType(node->child(i)->index()) == NodeType::Dir &&
                node->child(i)->text() == parts.first()) {
            parts.pop_front();
            node = node->child(i);
            i = 0;
        } else {
            i++;
        }
    }
    // Create new path items
    for (auto& part : parts) {
        auto dir = new QStandardItem(QIcon(":/icons/images/folder.png"), part);
        dir->setData(NodeType::Dir, Role::Type);
        dir->setFlags(Qt::ItemIsEnabled);
        node->appendRow(dir);
        node->sortChildren(0);
        node = dir;
    }
    // Add file item
    auto item = new QStandardItem(QIcon(icon), file);
    item->setData(type, Role::Type);
    item->setData(abs, Role::Path);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    node->appendRow(item);
    node->sortChildren(0);

    entries.insert(abs, item);

    if (!projectFile().isEmpty()) {
        setModified(true);
    }
}

void Project::add(const QStringList& fileNames)
{
    for (auto& fileName : fileNames) {
        add(fileName);
    }
}

void Project::remove(const QString &fileName)
{
    auto path = QFileInfo(fileName).absoluteFilePath();
    if (!contains(path)) {
        return;
    }

    // Make sure we also remove any unused dirs
    auto node = entries[path];
    if (node->text() == "_mooc" || node->text() == "_coursera") {
        delete mooc;
        mooc = nullptr;
        emit moocChanged(mooc);
    }
    while (getType(node->parent()->index()) == NodeType::Dir && node->parent()->rowCount() <= 1) {
        node = node->parent();
    }
    auto parent = node->parent();
    parent->removeRow(node->row());
    if (parent->data(Role::Type) == "group" && !parent->hasChildren()) {
        parent->setFlags(Qt::NoItemFlags);
    }
    entries.remove(path);

    if (!projectFile().isEmpty()) {
        setModified(true);
    }
}

void Project::remove(const QStringList &fileNames)
{
    for (auto& fileName : fileNames) {
        remove(fileName);
    }
}

void Project::remove(const QModelIndex& index)
{
    remove(getFileName(index));
}

void Project::remove(const QModelIndexList& indexes)
{
    for (auto& index : indexes) {
        remove(index);
    }
}

void Project::clear()
{
    modelsItem->removeRows(0, modelsItem->rowCount());
    dataItem->removeRows(0, dataItem->rowCount());
    checkersItem->removeRows(0, checkersItem->rowCount());
    otherItem->removeRows(0, otherItem->rowCount());
    entries.clear();
    setModified(true);
}

QStringList Project::files(void) const {
    return entries.keys();
}

QStringList Project::modelFiles(void) const {
    return getFiles(NodeType::Model);
}

QStringList Project::solverConfigurationFiles(void) const {
    return getFiles(NodeType::SolverConfig);
}

QStringList Project::dataFiles(void) const {
    return getFiles(NodeType::Data);
}

bool Project::contains(const QString &fileName)
{
    QFileInfo fi(fileName);
    return entries.contains(fi.absoluteFilePath());
}

void Project::setModified(bool m)
{
    modified = m;
    auto label = rootItem->data(Role::OriginalLabel).toString();
    if (modified) {
        rootItem->setText(label + " *");
    } else {
        rootItem->setText(label);
    }
}

void Project::projectFile(const QString& fileName)
{
    QStringList files = entries.keys();
    clear();
    if (fileName.isEmpty()) {
        rootItem->setText("Untitled Project");
        rootItem->setData("Untitled Project", Role::OriginalLabel);
        projFile = "";
    } else {
        QFileInfo fi(fileName);
        projFile = fi.absoluteFilePath();
        rootItem->setText(fi.fileName());
        rootItem->setData(fi.fileName(), Role::OriginalLabel);
    }
    add(files);
}

QDir Project::rootDir()
{
    QFileInfo fi(projectFile());
    return QDir(fi.absolutePath());
}

bool Project::hasProjectFile()
{
    return !projectFile().isEmpty();
}

Project::NodeType Project::getType(const QModelIndex& index)
{
    return static_cast<Project::NodeType>(model()->data(index, Role::Type).toInt());
}

QString Project::getFileName(const QModelIndex& index)
{
    return model()->data(index, Role::Path).toString();
}

QStringList Project::getFileNames(const QModelIndexList& indices)
{
    QStringList result;
    for (auto& index : indices) {
        result << getFileName(index);
    }
    return result;
}

QStringList Project::getFiles(NodeType type) const
{
    QStringList ret;
    for (auto it = entries.begin(); it != entries.end(); it++) {
        auto t = static_cast<NodeType>(it.value()->data(Role::Type).toInt());
        if (t == type) {
            ret << it.key();
        }
    }
    return ret;
}

QString Project::relativeToProject(const QString& fileName)
{
    QFileInfo fi(fileName);
    auto abs = fi.absoluteFilePath();
    return hasProjectFile() ? rootDir().relativeFilePath(abs) : abs;
}

void Project::openTabsChanged(const QStringList& files, int currentTab)
{
    openTabs.clear();
    for (auto& file : files) {
        auto abs = QFileInfo(file).absoluteFilePath();
        if (contains(abs)) {
            openTabs << abs;
        }
    }

    if (currentTab >= 0) {
        openTabIndex = openTabs.indexOf(QFileInfo(files[currentTab]).absoluteFilePath());
    } else {
        openTabIndex = -1;
    }

//    setModified(true);
}

void Project::activeSolverConfigChanged(const SolverConfiguration* sc)
{
    if (!sc) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    }
    if (sc->isBuiltin) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = sc->solverDefinition.id;
        selectedBuiltinConfigVersion = sc->solverDefinition.version;
    } else {
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
        selectedSolverConfigFile = relativeToProject(sc->paramFile);
    }
//    setModified(true);
}

void Project::on_itemChanged(QStandardItem* item)
{
    auto oldPath = item->data(Qt::UserRole + 1).toString();
    auto newName = item->text();
    if (oldPath.isEmpty() || oldPath.endsWith(newName)) {
        return;
    }
    QFileInfo fi(oldPath);
    auto target = fi.path() + "/" + item->text();
    if (QFile::rename(oldPath, target)) {
        remove(oldPath);
        add(target);
        emit fileRenamed(oldPath, target);
    } else {
        item->setText(fi.fileName());
    }
}
<|endoftext|>