<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libvisio project.
 *
 * 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 "VSDMetaData.h"
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <unicode/ucnv.h>
#include <ctime>

libvisio::VSDMetaData::VSDMetaData()
  : m_idsAndOffsets(), m_typedPropertyValues(), m_metaData()
{
}

libvisio::VSDMetaData::~VSDMetaData()
{
}

enum PIDDSI
{
  PIDDSI_CODEPAGE          = 0x00000001,
  PIDDSI_CATEGORY          = 0x00000002,
  PIDDSI_PRESFORMAT        = 0x00000003,
  PIDDSI_BYTECOUNT         = 0x00000004,
  PIDDSI_LINECOUNT         = 0x00000005,
  PIDDSI_PARACOUNT         = 0x00000006,
  PIDDSI_SLIDECOUNT        = 0x00000007,
  PIDDSI_NOTECOUNT         = 0x00000008,
  PIDDSI_HIDDENCOUNT       = 0x00000009,
  PIDDSI_MMCLIPCOUNT       = 0x0000000A,
  PIDDSI_SCALE             = 0x0000000B,
  PIDDSI_HEADINGPAIR       = 0x0000000C,
  PIDDSI_DOCPARTS          = 0x0000000D,
  PIDDSI_MANAGER           = 0x0000000E,
  PIDDSI_COMPANY           = 0x0000000F,
  PIDDSI_LINKSDIRTY        = 0x00000010,
  PIDDSI_CCHWITHSPACES     = 0x00000011,
  PIDDSI_SHAREDDOC         = 0x00000013,
  PIDDSI_LINKBASE          = 0x00000014,
  PIDDSI_HLINKS            = 0x00000015,
  PIDDSI_HYPERLINKSCHANGED = 0x00000016,
  PIDDSI_VERSION           = 0x00000017,
  PIDDSI_DIGSIG            = 0x00000018,
  PIDDSI_CONTENTTYPE       = 0x0000001A,
  PIDDSI_CONTENTSTATUS     = 0x0000001B,
  PIDDSI_LANGUAGE          = 0x0000001C,
  PIDDSI_DOCVERSION        = 0x0000001D
};

enum PIDSI
{
  CODEPAGE_PROPERTY_IDENTIFIER = 0x00000001,
  PIDSI_TITLE                  = 0x00000002,
  PIDSI_SUBJECT                = 0x00000003,
  PIDSI_AUTHOR                 = 0x00000004,
  PIDSI_KEYWORDS               = 0x00000005,
  PIDSI_COMMENTS               = 0x00000006,
  PIDSI_TEMPLATE               = 0x00000007,
  PIDSI_LASTAUTHOR             = 0x00000008,
  PIDSI_REVNUMBER              = 0x00000009,
  PIDSI_EDITTIME               = 0x0000000A,
  PIDSI_LASTPRINTED            = 0x0000000B,
  PIDSI_CREATE_DTM             = 0x0000000C,
  PIDSI_LASTSAVE_DTM           = 0x0000000D,
  PIDSI_PAGECOUNT              = 0x0000000E,
  PIDSI_WORDCOUNT              = 0x0000000F,
  PIDSI_CHARCOUNT              = 0x00000010,
  PIDSI_THUMBNAIL              = 0x00000011,
  PIDSI_APPNAME                = 0x00000012,
  PIDSI_DOC_SECURITY           = 0x00000013
};

bool libvisio::VSDMetaData::parse(librevenge::RVNGInputStream *input)
{
  if (!input)
    return false;

  readPropertySetStream(input);

  return true;
}

void libvisio::VSDMetaData::readPropertySetStream(librevenge::RVNGInputStream *input)
{
  // ByteOrder
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // Version
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // SystemIdentifier
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // CLSID
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  // NumPropertySets
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // FMTID0
  //input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t data1 = readU32(input);
  uint16_t data2 = readU16(input);
  uint16_t data3 = readU16(input);
  uint8_t data4[8];
  for (unsigned char &i : data4)
  {
    i = readU8(input);
  }
  // Pretty-printed GUID is 36 bytes + the terminating null-character.
  char FMTID0[37];
  sprintf(FMTID0, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", data1, data2, data3,
          data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]);

  uint32_t offset0 = readU32(input);
  readPropertySet(input, offset0, FMTID0);
}

void libvisio::VSDMetaData::readPropertySet(librevenge::RVNGInputStream *input, uint32_t offset, char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);

  // Size
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  uint32_t numProperties = readU32(input);
  for (uint32_t i = 0; i < numProperties; ++i)
    readPropertyIdentifierAndOffset(input);
  for (uint32_t i = 0; i < numProperties; ++i)
  {
    if (i >= m_idsAndOffsets.size())
      break;
    readTypedPropertyValue(input, i, offset + m_idsAndOffsets[i].second, FMTID);
  }
}

uint32_t libvisio::VSDMetaData::getCodePage()
{
  for (size_t i = 0; i < m_idsAndOffsets.size(); ++i)
  {
    if (m_idsAndOffsets[i].first == CODEPAGE_PROPERTY_IDENTIFIER)
    {
      if (i >= m_typedPropertyValues.size())
        break;
      return m_typedPropertyValues[i];
    }
  }

  return 0;
}

void libvisio::VSDMetaData::readPropertyIdentifierAndOffset(librevenge::RVNGInputStream *input)
{
  uint32_t propertyIdentifier = readU32(input);
  uint32_t offset = readU32(input);
  m_idsAndOffsets.push_back(std::make_pair(propertyIdentifier, offset));
}

#define VT_I2 0x0002
#define VT_LPSTR 0x001E

void libvisio::VSDMetaData::readTypedPropertyValue(librevenge::RVNGInputStream *input,
                                                   uint32_t index,
                                                   uint32_t offset,
                                                   char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);
  uint16_t type = readU16(input);
  // Padding
  input->seek(2, librevenge::RVNG_SEEK_CUR);

  if (type == VT_I2)
  {
    uint16_t value = readU16(input);
    m_typedPropertyValues[index] = value;
  }
  else if (type == VT_LPSTR)
  {
    librevenge::RVNGString string = readCodePageString(input);
    if (!string.empty())
    {
      if (index >= m_idsAndOffsets.size())
        return;

      if (!strcmp(FMTID, "f29f85e0-4ff9-1068-ab91-08002b27b3d9"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDSI_TITLE:
          m_metaData.insert("dc:title", string);
          break;
        case PIDSI_SUBJECT:
          m_metaData.insert("dc:subject", string);
          break;
        case PIDSI_AUTHOR:
          m_metaData.insert("meta:initial-creator", string);
          m_metaData.insert("dc:creator", string);
          break;
        case PIDSI_KEYWORDS:
          m_metaData.insert("meta:keyword", string);
          break;
        case PIDSI_COMMENTS:
          m_metaData.insert("dc:description", string);
          break;
        case PIDSI_TEMPLATE:
          std::string templateHref(string.cstr());
          size_t found = templateHref.find_last_of("/\\");
          if (found != std::string::npos)
            string = librevenge::RVNGString(templateHref.substr(found+1).c_str());
          m_metaData.insert("librevenge:template", string);
          break;
        }
      }
      else if (!strcmp(FMTID,"d5cdd502-2e9c-101b-9397-08002b2cf9ae"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDDSI_CATEGORY:
          m_metaData.insert("librevenge:category", string);
          break;
        case PIDDSI_LINECOUNT:
          // this should actually be PIDDSI_COMPANY but this
          // is what company is mapped to
          m_metaData.insert("librevenge:company", string);
          break;
        case PIDDSI_LANGUAGE:
          m_metaData.insert("dc:language", string);
          break;
        }
      }
    }
  }
}

librevenge::RVNGString libvisio::VSDMetaData::readCodePageString(librevenge::RVNGInputStream *input)
{
  uint32_t size = readU32(input);
  if (size > getRemainingLength(input))
    size = getRemainingLength(input);

  if (size == 0)
    return librevenge::RVNGString();

  std::vector<unsigned char> characters;
  for (uint32_t i = 0; i < size; ++i)
    characters.push_back(readU8(input));

  uint32_t codepage = getCodePage();
  librevenge::RVNGString string;

  if (codepage == 65001)
  {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130%28v=vs.85%29.aspx
    // says this is UTF-8.
    characters.push_back(0);
    string.append(reinterpret_cast<const char *>(characters.data()));
  }
  else
  {
    UErrorCode status = U_ZERO_ERROR;
    UConverter *conv = nullptr;

    switch (codepage)
    {
    case 1252:
      // http://msdn.microsoft.com/en-us/goglobal/bb964654
      conv = ucnv_open("windows-1252", &status);
      break;
    }

    if (U_SUCCESS(status) && conv)
    {
      assert(!characters.empty());
      const char *src = (const char *)&characters[0];
      const char *srcLimit = (const char *)src + characters.size();
      while (src < srcLimit)
      {
        UChar32 ucs4Character = ucnv_getNextUChar(conv, &src, srcLimit, &status);
        if (U_SUCCESS(status) && U_IS_UNICODE_CHAR(ucs4Character))
          appendUCS4(string, ucs4Character);
      }
    }

    if (conv)
      ucnv_close(conv);
  }

  return string;
}

bool libvisio::VSDMetaData::parseTimes(librevenge::RVNGInputStream *input)
{
  // Parse the header
  // HeaderSignature: 8 bytes
  // HeaderCLSID: 16 bytes
  // MinorVersion: 2 bytes
  // MajorVersion: 2 bytes
  // ByteOrder: 2 bytes
  input->seek(30, librevenge::RVNG_SEEK_CUR);
  uint16_t sectorShift = readU16(input);
  // MiniSectorShift: 2 bytes
  // Reserved: 6 bytes
  // NumDirectorySectors: 4 bytes
  // NumFATSectors: 4 bytes
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t firstDirSectorLocation = readU32(input);

  // Seek to the Root Directory Entry
  size_t sectorSize = std::pow(2, sectorShift);
  input->seek((firstDirSectorLocation + 1) * sectorSize, librevenge::RVNG_SEEK_SET);
  // DirectoryEntryName: 64 bytes
  // DirectoryEntryNameLength: 2 bytes
  // ObjectType: 1 byte
  // ColorFlag: 1 byte
  // LeftSiblingID: 4 bytes
  // RightSiblingID: 4 bytes
  // ChildID: 4 bytes
  // CLSID: 16 bytes
  // StateBits: 4 bytes
  // CreationTime: 8 bytes
  input->seek(108, librevenge::RVNG_SEEK_CUR);
  uint64_t modifiedTime = readU64(input);

  // modifiedTime is number of 100ns since Jan 1 1601
  const uint64_t epoch = uint64_t(116444736UL) * 100;
  time_t sec = (modifiedTime / 10000000) - epoch;
  const struct tm *time = localtime(&sec);
  if (time)
  {
    static const int MAX_BUFFER = 1024;
    char buffer[MAX_BUFFER];
    strftime(&buffer[0], MAX_BUFFER-1, "%Y-%m-%dT%H:%M:%SZ", time);
    librevenge::RVNGString result;
    result.append(buffer);
    // Visio UI uses modifiedTime for both purposes.
    m_metaData.insert("meta:creation-date", result);
    m_metaData.insert("dc:date", result);
    return true;
  }
  return false;
}

const librevenge::RVNGPropertyList &libvisio::VSDMetaData::getMetaData()
{
  return m_metaData;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>cid#1312142 sanitize loop bound<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libvisio project.
 *
 * 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 "VSDMetaData.h"
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <unicode/ucnv.h>
#include <ctime>

libvisio::VSDMetaData::VSDMetaData()
  : m_idsAndOffsets(), m_typedPropertyValues(), m_metaData()
{
}

libvisio::VSDMetaData::~VSDMetaData()
{
}

enum PIDDSI
{
  PIDDSI_CODEPAGE          = 0x00000001,
  PIDDSI_CATEGORY          = 0x00000002,
  PIDDSI_PRESFORMAT        = 0x00000003,
  PIDDSI_BYTECOUNT         = 0x00000004,
  PIDDSI_LINECOUNT         = 0x00000005,
  PIDDSI_PARACOUNT         = 0x00000006,
  PIDDSI_SLIDECOUNT        = 0x00000007,
  PIDDSI_NOTECOUNT         = 0x00000008,
  PIDDSI_HIDDENCOUNT       = 0x00000009,
  PIDDSI_MMCLIPCOUNT       = 0x0000000A,
  PIDDSI_SCALE             = 0x0000000B,
  PIDDSI_HEADINGPAIR       = 0x0000000C,
  PIDDSI_DOCPARTS          = 0x0000000D,
  PIDDSI_MANAGER           = 0x0000000E,
  PIDDSI_COMPANY           = 0x0000000F,
  PIDDSI_LINKSDIRTY        = 0x00000010,
  PIDDSI_CCHWITHSPACES     = 0x00000011,
  PIDDSI_SHAREDDOC         = 0x00000013,
  PIDDSI_LINKBASE          = 0x00000014,
  PIDDSI_HLINKS            = 0x00000015,
  PIDDSI_HYPERLINKSCHANGED = 0x00000016,
  PIDDSI_VERSION           = 0x00000017,
  PIDDSI_DIGSIG            = 0x00000018,
  PIDDSI_CONTENTTYPE       = 0x0000001A,
  PIDDSI_CONTENTSTATUS     = 0x0000001B,
  PIDDSI_LANGUAGE          = 0x0000001C,
  PIDDSI_DOCVERSION        = 0x0000001D
};

enum PIDSI
{
  CODEPAGE_PROPERTY_IDENTIFIER = 0x00000001,
  PIDSI_TITLE                  = 0x00000002,
  PIDSI_SUBJECT                = 0x00000003,
  PIDSI_AUTHOR                 = 0x00000004,
  PIDSI_KEYWORDS               = 0x00000005,
  PIDSI_COMMENTS               = 0x00000006,
  PIDSI_TEMPLATE               = 0x00000007,
  PIDSI_LASTAUTHOR             = 0x00000008,
  PIDSI_REVNUMBER              = 0x00000009,
  PIDSI_EDITTIME               = 0x0000000A,
  PIDSI_LASTPRINTED            = 0x0000000B,
  PIDSI_CREATE_DTM             = 0x0000000C,
  PIDSI_LASTSAVE_DTM           = 0x0000000D,
  PIDSI_PAGECOUNT              = 0x0000000E,
  PIDSI_WORDCOUNT              = 0x0000000F,
  PIDSI_CHARCOUNT              = 0x00000010,
  PIDSI_THUMBNAIL              = 0x00000011,
  PIDSI_APPNAME                = 0x00000012,
  PIDSI_DOC_SECURITY           = 0x00000013
};

bool libvisio::VSDMetaData::parse(librevenge::RVNGInputStream *input)
{
  if (!input)
    return false;

  readPropertySetStream(input);

  return true;
}

void libvisio::VSDMetaData::readPropertySetStream(librevenge::RVNGInputStream *input)
{
  // ByteOrder
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // Version
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  // SystemIdentifier
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // CLSID
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  // NumPropertySets
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  // FMTID0
  //input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t data1 = readU32(input);
  uint16_t data2 = readU16(input);
  uint16_t data3 = readU16(input);
  uint8_t data4[8];
  for (unsigned char &i : data4)
  {
    i = readU8(input);
  }
  // Pretty-printed GUID is 36 bytes + the terminating null-character.
  char FMTID0[37];
  sprintf(FMTID0, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", data1, data2, data3,
          data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]);

  uint32_t offset0 = readU32(input);
  readPropertySet(input, offset0, FMTID0);
}

void libvisio::VSDMetaData::readPropertySet(librevenge::RVNGInputStream *input, uint32_t offset, char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);

  // Size
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  uint32_t numProperties = readU32(input);
  // The exact size of a property is not known beforehand: check upper bound
  if (numProperties > getRemainingLength(input) / 12)
    numProperties = getRemainingLength(input) / 12;
  for (uint32_t i = 0; i < numProperties; ++i)
    readPropertyIdentifierAndOffset(input);
  for (uint32_t i = 0; i < numProperties; ++i)
  {
    if (i >= m_idsAndOffsets.size())
      break;
    readTypedPropertyValue(input, i, offset + m_idsAndOffsets[i].second, FMTID);
  }
}

uint32_t libvisio::VSDMetaData::getCodePage()
{
  for (size_t i = 0; i < m_idsAndOffsets.size(); ++i)
  {
    if (m_idsAndOffsets[i].first == CODEPAGE_PROPERTY_IDENTIFIER)
    {
      if (i >= m_typedPropertyValues.size())
        break;
      return m_typedPropertyValues[i];
    }
  }

  return 0;
}

void libvisio::VSDMetaData::readPropertyIdentifierAndOffset(librevenge::RVNGInputStream *input)
{
  uint32_t propertyIdentifier = readU32(input);
  uint32_t offset = readU32(input);
  m_idsAndOffsets.push_back(std::make_pair(propertyIdentifier, offset));
}

#define VT_I2 0x0002
#define VT_LPSTR 0x001E

void libvisio::VSDMetaData::readTypedPropertyValue(librevenge::RVNGInputStream *input,
                                                   uint32_t index,
                                                   uint32_t offset,
                                                   char *FMTID)
{
  input->seek(offset, librevenge::RVNG_SEEK_SET);
  uint16_t type = readU16(input);
  // Padding
  input->seek(2, librevenge::RVNG_SEEK_CUR);

  if (type == VT_I2)
  {
    uint16_t value = readU16(input);
    m_typedPropertyValues[index] = value;
  }
  else if (type == VT_LPSTR)
  {
    librevenge::RVNGString string = readCodePageString(input);
    if (!string.empty())
    {
      if (index >= m_idsAndOffsets.size())
        return;

      if (!strcmp(FMTID, "f29f85e0-4ff9-1068-ab91-08002b27b3d9"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDSI_TITLE:
          m_metaData.insert("dc:title", string);
          break;
        case PIDSI_SUBJECT:
          m_metaData.insert("dc:subject", string);
          break;
        case PIDSI_AUTHOR:
          m_metaData.insert("meta:initial-creator", string);
          m_metaData.insert("dc:creator", string);
          break;
        case PIDSI_KEYWORDS:
          m_metaData.insert("meta:keyword", string);
          break;
        case PIDSI_COMMENTS:
          m_metaData.insert("dc:description", string);
          break;
        case PIDSI_TEMPLATE:
          std::string templateHref(string.cstr());
          size_t found = templateHref.find_last_of("/\\");
          if (found != std::string::npos)
            string = librevenge::RVNGString(templateHref.substr(found+1).c_str());
          m_metaData.insert("librevenge:template", string);
          break;
        }
      }
      else if (!strcmp(FMTID,"d5cdd502-2e9c-101b-9397-08002b2cf9ae"))
      {
        switch (m_idsAndOffsets[index].first)
        {
        case PIDDSI_CATEGORY:
          m_metaData.insert("librevenge:category", string);
          break;
        case PIDDSI_LINECOUNT:
          // this should actually be PIDDSI_COMPANY but this
          // is what company is mapped to
          m_metaData.insert("librevenge:company", string);
          break;
        case PIDDSI_LANGUAGE:
          m_metaData.insert("dc:language", string);
          break;
        }
      }
    }
  }
}

librevenge::RVNGString libvisio::VSDMetaData::readCodePageString(librevenge::RVNGInputStream *input)
{
  uint32_t size = readU32(input);
  if (size > getRemainingLength(input))
    size = getRemainingLength(input);

  if (size == 0)
    return librevenge::RVNGString();

  std::vector<unsigned char> characters;
  for (uint32_t i = 0; i < size; ++i)
    characters.push_back(readU8(input));

  uint32_t codepage = getCodePage();
  librevenge::RVNGString string;

  if (codepage == 65001)
  {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130%28v=vs.85%29.aspx
    // says this is UTF-8.
    characters.push_back(0);
    string.append(reinterpret_cast<const char *>(characters.data()));
  }
  else
  {
    UErrorCode status = U_ZERO_ERROR;
    UConverter *conv = nullptr;

    switch (codepage)
    {
    case 1252:
      // http://msdn.microsoft.com/en-us/goglobal/bb964654
      conv = ucnv_open("windows-1252", &status);
      break;
    }

    if (U_SUCCESS(status) && conv)
    {
      assert(!characters.empty());
      const char *src = (const char *)&characters[0];
      const char *srcLimit = (const char *)src + characters.size();
      while (src < srcLimit)
      {
        UChar32 ucs4Character = ucnv_getNextUChar(conv, &src, srcLimit, &status);
        if (U_SUCCESS(status) && U_IS_UNICODE_CHAR(ucs4Character))
          appendUCS4(string, ucs4Character);
      }
    }

    if (conv)
      ucnv_close(conv);
  }

  return string;
}

bool libvisio::VSDMetaData::parseTimes(librevenge::RVNGInputStream *input)
{
  // Parse the header
  // HeaderSignature: 8 bytes
  // HeaderCLSID: 16 bytes
  // MinorVersion: 2 bytes
  // MajorVersion: 2 bytes
  // ByteOrder: 2 bytes
  input->seek(30, librevenge::RVNG_SEEK_CUR);
  uint16_t sectorShift = readU16(input);
  // MiniSectorShift: 2 bytes
  // Reserved: 6 bytes
  // NumDirectorySectors: 4 bytes
  // NumFATSectors: 4 bytes
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  uint32_t firstDirSectorLocation = readU32(input);

  // Seek to the Root Directory Entry
  size_t sectorSize = std::pow(2, sectorShift);
  input->seek((firstDirSectorLocation + 1) * sectorSize, librevenge::RVNG_SEEK_SET);
  // DirectoryEntryName: 64 bytes
  // DirectoryEntryNameLength: 2 bytes
  // ObjectType: 1 byte
  // ColorFlag: 1 byte
  // LeftSiblingID: 4 bytes
  // RightSiblingID: 4 bytes
  // ChildID: 4 bytes
  // CLSID: 16 bytes
  // StateBits: 4 bytes
  // CreationTime: 8 bytes
  input->seek(108, librevenge::RVNG_SEEK_CUR);
  uint64_t modifiedTime = readU64(input);

  // modifiedTime is number of 100ns since Jan 1 1601
  const uint64_t epoch = uint64_t(116444736UL) * 100;
  time_t sec = (modifiedTime / 10000000) - epoch;
  const struct tm *time = localtime(&sec);
  if (time)
  {
    static const int MAX_BUFFER = 1024;
    char buffer[MAX_BUFFER];
    strftime(&buffer[0], MAX_BUFFER-1, "%Y-%m-%dT%H:%M:%SZ", time);
    librevenge::RVNGString result;
    result.append(buffer);
    // Visio UI uses modifiedTime for both purposes.
    m_metaData.insert("meta:creation-date", result);
    m_metaData.insert("dc:date", result);
    return true;
  }
  return false;
}

const librevenge::RVNGPropertyList &libvisio::VSDMetaData::getMetaData()
{
  return m_metaData;
}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<|endoftext|>


<commit_before>/**
@file Datastore.hpp
@brief Datastore class.

@author Tim Howard
@copyright 2013 Tim Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef HORD_DATASTORE_HPP_
#define HORD_DATASTORE_HPP_

#include "./config.hpp"
#include "./traits.hpp"
#include "./common_types.hpp"
#include "./common_enums.hpp"
#include "./Prop.hpp"
#include "./Hive.hpp"

#include <functional>
#include <iosfwd>

namespace Hord {

// Forward declarations
class Datastore;

/**
	@addtogroup serialization
	@{
*/
/**
	@addtogroup datastore
	@{
*/

/**
	Base datastore.

	This is the data interface for hives.

	@note %Datastores must be locked when a single prop stream is
	active.

	@remarks The datastore's hive should not be used by
	an implementation to lookup objects within the datastore. The
	hive is not guaranteed to contain the entire idset at runtime.

	@remarks The hive will always have a valid ID, but
	note that the hive's ID is not within the ID
	space of its children -- see @ref hive.
*/
class Datastore {
public:
	/**
		Type info.
	*/
	struct type_info final {
		/**
			Construct a datastore of this type.

			@returns
			- The constructed datastore;
			- or @c nullptr if construction failed.
			@param root_path Root path.
			@param id %Hive ID.
		*/
		Datastore* (&construct)(String root_path, HiveID const id) noexcept;
	};

	/**
		Ensure traits for deriving classes.

		@tparam D Deriving class.
	*/
	template<typename D>
	struct ensure_traits :
		traits::require_t<
			D,
			tw::capture_post<std::is_base_of, Datastore>::type,
			// FIXME: libstdc++ 4.7.3 doesn't have the nothrow version
			std::is_destructible,
			tw::is_fully_moveable
		>,
		traits::disallow_t<
			D,
			std::is_default_constructible,
			tw::is_fully_copyable
		>
	{};

private:
	unsigned m_states;
	String m_root_path;
	Hive m_hive;

	Datastore()=delete;
	Datastore(Datastore const&)=delete;
	Datastore& operator=(Datastore const&)=delete;

protected:
/** @name Implementation */ /// @{
	/**
		open() implementation.
	*/
	virtual void open_impl()=0;
	/**
		close() implementation.

		@remarks This is not called if @c is_open()==false.
	*/
	virtual void close_impl()=0;

	/**
		Acquire raw stream implementation.
		@{
	*/
	virtual std::istream& acquire_input_stream_impl(
		PropInfo const& prop_info
	)=0;
	virtual std::ostream& acquire_output_stream_impl(
		PropInfo const& prop_info
	)=0;
	/** @} */

	/**
		Release raw stream implementation.
		@{
	*/
	virtual void release_input_stream_impl(
		PropInfo const& prop_info
	)=0;
	virtual void release_output_stream_impl(
		PropInfo const& prop_info
	)=0;
	/** @} */
/// @}

/** @name Internal state */ /// @{
	/**
		States.

		Implementations are permitted to define states @c 1<<8
		to @c 1<<31.
	*/
	enum class State : uint32_t {
		/** %Datastore is open. */
		opened	=1<<0,
		/** %Datastore is locked. */
		locked	=1<<1,

		/** First reserved state. */
		RESERVED_FIRST=1<<2,
		/** Last reserved state. */
		RESERVED_LAST=1<<7
	};

	/**
		Enable state.
		@param state %State to enable.
	*/
	void enable_state(State const state) noexcept;
	/**
		Disable state.
		@param state %State to disable.
	*/
	void disable_state(State const state) noexcept;
	/**
		Check if a state is enabled.

		@returns
		- @c true if the state is enabled;
		- @c false if the state is disabled.
		@param state %State to test.
	*/
	bool has_state(State const state) const noexcept;
/// @}

/** @name Constructors and destructor */ /// @{
	/**
		Constructor with root path and hive ID.

		@param root_path Root path.
		@param id %Hive ID.
	*/
	Datastore(String root_path, HiveID const id) noexcept;
	/** Move constructor. */
	Datastore(Datastore&&);
public:
	/** Destructor. */
	virtual ~Datastore()=0;
/// @}

protected:
/** @name Operators */ /// @{
	/** Move assignment operator. */
	Datastore& operator=(Datastore&&);
/// @}

public:
/** @name Properties */ /// @{
	/**
		Set root path.

		@throws Error{ErrorCode::datastore_property_immutable}
		If the datastore is open.

		@param root_path New root path.
	*/
	void set_root_path(String root_path);
	/**
		Get root path.
		@returns Root path.
	*/
	String const& get_root_path() const noexcept
		{ return m_root_path; }

	/**
		Get hive.
		@returns %Hive.
	*/
	Hive const& get_hive() const noexcept
		{ return m_hive; }

	/**
		Get mutable hive.
		@returns Mutable hive.
	*/
	Hive& get_hive() noexcept
		{ return m_hive; }

	/**
		Check if the datastore is open.
		@returns
		- @c true if the datastore is open;
		- @c false if it is closed.
	*/
	bool is_open() const noexcept
		{ return has_state(State::opened); }

	/**
		Check if the datastore is locked.
		@returns
		- @c true if the datastore is locked;
		- @c false if it is not locked.
	*/
	bool is_locked() const noexcept
		{ return has_state(State::locked); }
/// @}

/** @name Operations */ /// @{
	/**
		Open the datastore.

		@throws Error{ErrorCode::datastore_open_already}
		If the datastore is already open.

		@throws Error{ErrorCode::datastore_open_failed}
		If the datastore failed to open.
	*/
	void open();
	/**
		Close the datastore.

		@throws Error{ErrorCode::datastore_locked}
		If the datastore is locked.
	*/
	void close();

	/**
		Acquire raw stream for prop.

		@throws Error{ErrorCode::datastore_closed}
		If the datastore is closed.

		@throws Error{ErrorCode::datastore_locked}
		If the datastore is locked.

		@throws Error{ErrorCode::datastore_object_not_found}
		If @c prop_info.object_id does not exist in the datastore.

		@throws Error{ErrorCode::datastore_prop_unsupplied}
		If the object for @c prop_info.object_id does not supply the
		requested prop.

		@throws Error{..}
		Implementation-defined exceptions.

		@returns Raw prop stream.
		@param prop_info Prop info.

		@sa PropInfo,
			InputPropStream,
			OutputPropStream
		@{
	*/
	std::istream& acquire_input_stream(PropInfo const& prop_info);
	std::ostream& acquire_output_stream(PropInfo const& prop_info);
	/** @} */

	/**
		Release raw stream for prop.

		@throws Error{ErrorCode::datastore_closed}
		If the datastore is closed.

		@throws Error{ErrorCode::datastore_prop_not_locked}
		If either @a prop_info does not match the currently locked
		prop stream or there is no currently locked prop stream.

		@throws Error{ErrorCode::datastore_object_not_found}
		If @c prop_info.object_id does not exist in the datastore.

		@throws Error{ErrorCode::datastore_prop_unsupplied}
		If the object for @c prop_info.object_id does not supply
		the requested prop.

		@throws Error{..}
		Implementation-defined exceptions.

		@param prop_info Prop info.

		@sa PropInfo,
			InputPropStream,
			OutputPropStream
		@{
	*/
	void release_input_stream(PropInfo const& prop_info);
	void release_output_stream(PropInfo const& prop_info);
	/** @} */
/// @}
};

template struct traits::require_t<
	Datastore,
	std::has_virtual_destructor
>;

/** @} */ // end of doc-group datastore
/** @} */ // end of doc-group serialization

} // namespace Hord

#endif // HORD_DATASTORE_HPP_
<commit_msg>Datastore: (poorly) fixed doc grouping¹.<commit_after>/**
@file Datastore.hpp
@brief Datastore class.

@author Tim Howard
@copyright 2013 Tim Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef HORD_DATASTORE_HPP_
#define HORD_DATASTORE_HPP_

#include "./config.hpp"
#include "./traits.hpp"
#include "./common_types.hpp"
#include "./common_enums.hpp"
#include "./Prop.hpp"
#include "./Hive.hpp"

#include <functional>
#include <iosfwd>

namespace Hord {

// Forward declarations
class Datastore;

/**
	@addtogroup serialization
	@{
*/
/**
	@addtogroup datastore
	@{
*/

/**
	Base datastore.

	This is the data interface for hives.

	@note %Datastores must be locked when a single prop stream is
	active.

	@remarks The datastore's hive should not be used by
	an implementation to lookup objects within the datastore. The
	hive is not guaranteed to contain the entire idset at runtime.

	@remarks The hive will always have a valid ID, but
	note that the hive's ID is not within the ID
	space of its children -- see @ref hive.
*/
class Datastore {
public:
	/**
		Type info.
	*/
	struct type_info final {
		/**
			Construct a datastore of this type.

			@returns
			- The constructed datastore;
			- or @c nullptr if construction failed.
			@param root_path Root path.
			@param id %Hive ID.
		*/
		Datastore* (&construct)(String root_path, HiveID const id) noexcept;
	};

	/**
		Ensure traits for deriving classes.

		@tparam D Deriving class.
	*/
	template<typename D>
	struct ensure_traits :
		traits::require_t<
			D,
			tw::capture_post<std::is_base_of, Datastore>::type,
			// FIXME: libstdc++ 4.7.3 doesn't have the nothrow version
			std::is_destructible,
			tw::is_fully_moveable
		>,
		traits::disallow_t<
			D,
			std::is_default_constructible,
			tw::is_fully_copyable
		>
	{};

private:
	unsigned m_states;
	String m_root_path;
	Hive m_hive;

	Datastore()=delete;
	Datastore(Datastore const&)=delete;
	Datastore& operator=(Datastore const&)=delete;

protected:
/** @name Implementation */ /// @{
	/**
		open() implementation.
	*/
	virtual void open_impl()=0;
	/**
		close() implementation.

		@remarks This is not called if @c is_open()==false.
	*/
	virtual void close_impl()=0;

	/**
		acquire_input_stream() implementation.
	*/
	virtual std::istream& acquire_input_stream_impl(
		PropInfo const& prop_info
	)=0;
	/**
		acquire_output_stream() implementation.
	*/
	virtual std::ostream& acquire_output_stream_impl(
		PropInfo const& prop_info
	)=0;

	/**
		release_input_stream() implementation.
	*/
	virtual void release_input_stream_impl(
		PropInfo const& prop_info
	)=0;
	/**
		release_output_stream() implementation.
	*/
	virtual void release_output_stream_impl(
		PropInfo const& prop_info
	)=0;
/// @}

/** @name Internal state */ /// @{
	/**
		States.

		Implementations are permitted to define states @c 1<<8
		to @c 1<<31.
	*/
	enum class State : uint32_t {
		/** %Datastore is open. */
		opened	=1<<0,
		/** %Datastore is locked. */
		locked	=1<<1,

		/** First reserved state. */
		RESERVED_FIRST=1<<2,
		/** Last reserved state. */
		RESERVED_LAST=1<<7
	};

	/**
		Enable state.
		@param state %State to enable.
	*/
	void enable_state(State const state) noexcept;
	/**
		Disable state.
		@param state %State to disable.
	*/
	void disable_state(State const state) noexcept;
	/**
		Check if a state is enabled.

		@returns
		- @c true if the state is enabled;
		- @c false if the state is disabled.
		@param state %State to test.
	*/
	bool has_state(State const state) const noexcept;
/// @}

/** @name Constructors and destructor */ /// @{
	/**
		Constructor with root path and hive ID.

		@param root_path Root path.
		@param id %Hive ID.
	*/
	Datastore(String root_path, HiveID const id) noexcept;
	/** Move constructor. */
	Datastore(Datastore&&);
public:
	/** Destructor. */
	virtual ~Datastore()=0;
/// @}

protected:
/** @name Operators */ /// @{
	/** Move assignment operator. */
	Datastore& operator=(Datastore&&);
/// @}

public:
/** @name Properties */ /// @{
	/**
		Set root path.

		@throws Error{ErrorCode::datastore_property_immutable}
		If the datastore is open.

		@param root_path New root path.
	*/
	void set_root_path(String root_path);
	/**
		Get root path.
		@returns Root path.
	*/
	String const& get_root_path() const noexcept
		{ return m_root_path; }

	/**
		Get hive.
		@returns %Hive.
	*/
	Hive const& get_hive() const noexcept
		{ return m_hive; }

	/**
		Get mutable hive.
		@returns Mutable hive.
	*/
	Hive& get_hive() noexcept
		{ return m_hive; }

	/**
		Check if the datastore is open.
		@returns
		- @c true if the datastore is open;
		- @c false if it is closed.
	*/
	bool is_open() const noexcept
		{ return has_state(State::opened); }

	/**
		Check if the datastore is locked.
		@returns
		- @c true if the datastore is locked;
		- @c false if it is not locked.
	*/
	bool is_locked() const noexcept
		{ return has_state(State::locked); }
/// @}

/** @name Operations */ /// @{
	/**
		Open the datastore.

		@throws Error{ErrorCode::datastore_open_already}
		If the datastore is already open.

		@throws Error{ErrorCode::datastore_open_failed}
		If the datastore failed to open.
	*/
	void open();
	/**
		Close the datastore.

		@throws Error{ErrorCode::datastore_locked}
		If the datastore is locked.
	*/
	void close();

	/**
		Acquire raw stream for prop.

		@throws Error{ErrorCode::datastore_closed}
		If the datastore is closed.

		@throws Error{ErrorCode::datastore_locked}
		If the datastore is locked.

		@throws Error{ErrorCode::datastore_object_not_found}
		If @c prop_info.object_id does not exist in the datastore.

		@throws Error{ErrorCode::datastore_prop_unsupplied}
		If the object for @c prop_info.object_id does not supply the
		requested prop.

		@throws Error{..}
		Implementation-defined exceptions.

		@returns Raw prop stream.
		@param prop_info Prop info.

		@sa PropInfo,
			InputPropStream,
			OutputPropStream
	*/
	std::istream& acquire_input_stream(PropInfo const& prop_info);
	/** @copydoc acquire_input_stream(PropInfo const&) */
	std::ostream& acquire_output_stream(PropInfo const& prop_info);

	/**
		Release raw stream for prop.

		@throws Error{ErrorCode::datastore_closed}
		If the datastore is closed.

		@throws Error{ErrorCode::datastore_prop_not_locked}
		If either @a prop_info does not match the currently locked
		prop stream or there is no currently locked prop stream.

		@throws Error{ErrorCode::datastore_object_not_found}
		If @c prop_info.object_id does not exist in the datastore.

		@throws Error{ErrorCode::datastore_prop_unsupplied}
		If the object for @c prop_info.object_id does not supply
		the requested prop.

		@throws Error{..}
		Implementation-defined exceptions.

		@param prop_info Prop info.

		@sa PropInfo,
			InputPropStream,
			OutputPropStream
	*/
	void release_input_stream(PropInfo const& prop_info);
	/** @copydoc release_input_stream(PropInfo const&) */
	void release_output_stream(PropInfo const& prop_info);
/// @}
};

template struct traits::require_t<
	Datastore,
	std::has_virtual_destructor
>;

/** @} */ // end of doc-group datastore
/** @} */ // end of doc-group serialization

} // namespace Hord

#endif // HORD_DATASTORE_HPP_
<|endoftext|>


<commit_before>///
/// @file  aligned_vector.hpp
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef ALIGNED_VECTOR_HPP
#define ALIGNED_VECTOR_HPP

#include <cstddef>
#include <vector>

namespace primecount {

/// The aligned_vector class aligns each of its elements on a
/// new cache line in order to avoid false sharing (cache trashing)
/// when multiple threads write to adjacent elements.
///
template <typename T>
class aligned_vector
{
public:
  aligned_vector(std::size_t size)
    : vect_(size) { }
  std::size_t size() const { return vect_.size(); }
  T& operator[](std::size_t pos) { return vect_[pos].val; }
private:
  struct aligned_t
  {
    // maximum cache line size of current CPUs
    alignas(128) T val;
  };
  std::vector<aligned_t> vect_;
};

} // namespace

#endif
<commit_msg>define CACHE_LINE_SIZE<commit_after>///
/// @file  aligned_vector.hpp
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef ALIGNED_VECTOR_HPP
#define ALIGNED_VECTOR_HPP

#include <cstddef>
#include <vector>

// Maximum cache line size of current CPUs
#ifndef CACHE_LINE_SIZE
  #define CACHE_LINE_SIZE 128
#endif

namespace primecount {

/// The aligned_vector class aligns each of its elements on a
/// new cache line in order to avoid false sharing (cache trashing)
/// when multiple threads write to adjacent elements.
///
template <typename T>
class aligned_vector
{
public:
  aligned_vector(std::size_t size)
    : vect_(size) { }
  std::size_t size() const { return vect_.size(); }
  T& operator[](std::size_t pos) { return vect_[pos].val; }
private:
  struct alignas(CACHE_LINE_SIZE) align_t
  {
    T val;
  };
  std::vector<align_t> vect_;
};

} // namespace

#endif
<|endoftext|>


<commit_before>///
/// @file  aligned_vector.hpp
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef ALIGNED_VECTOR_HPP
#define ALIGNED_VECTOR_HPP

#include <cstddef>
#include <vector>

/// Maximum cache line size of current CPUs.
/// Note that in 2019 all x86 CPU have a cache line size of 64 bytes.
/// However there are CPUs out there that have much larger cache line
/// sizes e.g. IBM z13 CPUs from 2015 have a cache line size of 256
/// bytes. Hence in order to be future-proof we set the maximum cache
/// line size to 1 kilobyte.
///
#ifndef CACHE_LINE_SIZE
  #define CACHE_LINE_SIZE 1024
#endif

namespace primecount {

/// The aligned_vector class aligns each of its
/// elements on a new cache line in order to avoid
/// false sharing (cache trashing) when multiple
/// threads write to adjacent elements
///
template <typename T>
class aligned_vector
{
public:
  aligned_vector(std::size_t size)
    : vect_(size) { }
  std::size_t size() const { return vect_.size(); }
  T& operator[](std::size_t pos) { return vect_[pos].val[0]; }
private:
  struct align_t
  {
    T val[CACHE_LINE_SIZE / sizeof(T)];
  };
  std::vector<align_t> vect_;
};

} // namespace

#endif
<commit_msg>Reduce maximum cache line size<commit_after>///
/// @file  aligned_vector.hpp
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef ALIGNED_VECTOR_HPP
#define ALIGNED_VECTOR_HPP

#include <cstddef>
#include <vector>

/// Maximum cache line size of current CPUs.
/// Note that in 2019 all x86 CPU have a cache line size of 64 bytes.
/// However there are CPUs out there that have much larger cache line
/// sizes e.g. IBM z13 CPUs from 2015 have a cache line size of 256
/// bytes. Hence in order to be future-proof we set the maximum cache
/// line size to 512 bytes.
///
#ifndef CACHE_LINE_SIZE
  #define CACHE_LINE_SIZE 512
#endif

namespace primecount {

/// The aligned_vector class aligns each of its
/// elements on a new cache line in order to avoid
/// false sharing (cache trashing) when multiple
/// threads write to adjacent elements
///
template <typename T>
class aligned_vector
{
public:
  aligned_vector(std::size_t size)
    : vect_(size) { }
  std::size_t size() const { return vect_.size(); }
  T& operator[](std::size_t pos) { return vect_[pos].val[0]; }
private:
  struct align_t
  {
    T val[CACHE_LINE_SIZE / sizeof(T)];
  };
  std::vector<align_t> vect_;
};

} // namespace

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015-2016 Fotonic
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *       http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *       http://www.opensource.org/licenses/apache2.0.php
 *
 * You can redistribute this code under either of these licenses.
 * For more information; see http://www.arrowhead.eu/licensing
 */

/**
 * @file
 * @brief       JSON processing API declarations
 *
 * @author      Joakim Nohlgård <joakim@nohlgard.se>
 */

#ifndef ARROWHEAD_JSON_HPP_
#define ARROWHEAD_JSON_HPP_

#include "arrowhead/config.h"

#include <cstddef> // for size_t

namespace Arrowhead {

} /* namespace Arrowhead */

/* Template definitions are found in detail/json.hpp */
#include "arrowhead/detail/json.hpp"

#endif /* ARROWHEAD_JSON_HPP_ */

<commit_msg>delete old empty include json.hpp<commit_after><|endoftext|>


<commit_before>#pragma once
/**
	@file
	@brief int type definition and macros
	Copyright (C) 2008 Cybozu Labs, Inc., all rights reserved.
*/

#if defined(_MSC_VER) && (MSC_VER <= 1500)
	typedef __int64 int64_t;
	typedef unsigned __int64 uint64_t;
	typedef unsigned int uint32_t;
	typedef int int32_t;
	typedef unsigned short uint16_t;
	typedef short int16_t;
	typedef unsigned char uint8_t;
	typedef signed char int8_t;
#else
	#include <stdint.h>
#endif

#ifdef _MSC_VER
	#ifndef CYBOZU_DEFINED_SSIZE_T
		#define CYBOZU_DEFINED_SSIZE_T
		#ifdef _WIN64
			typedef int64_t ssize_t;
		#else
			typedef int32_t ssize_t;
		#endif
	#endif
#else
	#include <unistd.h> // for ssize_t
#endif

#ifndef CYBOZU_ALIGN
	#ifdef _MSC_VER
		#define CYBOZU_ALIGN(x) __declspec(align(x))
	#else
		#define CYBOZU_ALIGN(x) __attribute__((aligned(x)))
	#endif
#endif
#ifndef CYBOZU_ALLOCA
	#ifdef _MSC_VER
		#include <malloc.h>
		#define CYBOZU_ALLOCA(x) _malloca(x)
	#else
		#define CYBOZU_ALLOCA_(x) __builtin_alloca(x)
	#endif
#endif
#ifndef CYBOZU_FOREACH
	// std::vector<int> v; CYBOZU_FOREACH(auto x, v) {...}
	#if defined(_MSC_VER) && (_MSC_VER >= 1400)
		#define CYBOZU_FOREACH(type_x, xs) for each (type_x in xs)
	#elif defined(__GNUC__)
		#define CYBOZU_FOREACH(type_x, xs) for (type_x : xs)
	#endif
#endif
#ifndef CYBOZU_NUM_OF_ARRAY
	#define CYBOZU_NUM_OF_ARRAY(x) (sizeof(x) / sizeof(*x))
#endif
#ifndef CYBOZU_SNPRINTF
	#ifdef _MSC_VER
		#define CYBOZU_SNPRINTF(x, len, ...) _snprintf_s(x, len - 1, __VA_ARGS__)
	#else
		#define CYBOZU_SNPRINTF(x, len, ...) snprintf(x, len, __VA_ARGS__)
	#endif
#endif

#define CYBOZU_CPP_VERSION_CPP03 0
#define CYBOZU_CPP_VERSION_TR1 1
#define CYBOZU_CPP_VERSION_CPP11 2

#if (__cplusplus >= 201103) || (_MSC_VER >= 1500) || defined(__GXX_EXPERIMENTAL_CXX0X__)
	#if defined(_MSC_VER) && (_MSC_VER <= 1600)
		#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1
	#else
		#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP11
	#endif
#elif (__GNUC__ >= 4 && __GNUC_MINOR__ >= 5) || (__clang_major__ >= 3)
	#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1
#else
	#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP03
#endif

#if (CYBOZU_CPP_VERSION == CYBOZU_CPP_VERSION_TR1)
	#define CYBOZU_NAMESPACE_STD std::tr1
	#define CYBOZU_NAMESPACE_TR1_BEGIN namespace tr1 {
	#define CYBOZU_NAMESPACE_TR1_END }
#else
	#define CYBOZU_NAMESPACE_STD std
	#define CYBOZU_NAMESPACE_TR1_BEGIN
	#define CYBOZU_NAMESPACE_TR1_END
#endif

#ifndef CYBOZU_OS_BIT
	#if defined(_WIN64) || defined(__x86_64__)
		#define CYBOZU_OS_BIT 64
	#else
		#define CYBOZU_OS_BIT 32
	#endif
#endif

#ifndef CYBOZU_ENDIAN
	#define CYBOZU_ENDIAN_UNKNOWN 0
	#define CYBOZU_ENDIAN_LITTLE 1
	#define CYBOZU_ENDIAN_BIG 2
	#if defined(_M_IX86) || defined(_M_AMD64) || defined(__x86_64__) || defined(__i386__)
		#define CYBOZU_ENDIAN CYBOZU_ENDIAN_LITTLE
	#else
		#define CYBOZU_ENDIAN CYBOZU_ENDIAN_UNKNOWN
	#endif
#endif

namespace cybozu {
template<class T>
void disable_warning_unused_variable(const T&) { }
template<class T, class S>
T cast(const S* ptr) { return static_cast<T>(static_cast<const void*>(ptr)); }
template<class T, class S>
T cast(S* ptr) { return static_cast<T>(static_cast<void*>(ptr)); }
} // cybozu
<commit_msg>avoid to use return value<commit_after>#pragma once
/**
	@file
	@brief int type definition and macros
	Copyright (C) 2008 Cybozu Labs, Inc., all rights reserved.
*/

#if defined(_MSC_VER) && (MSC_VER <= 1500)
	typedef __int64 int64_t;
	typedef unsigned __int64 uint64_t;
	typedef unsigned int uint32_t;
	typedef int int32_t;
	typedef unsigned short uint16_t;
	typedef short int16_t;
	typedef unsigned char uint8_t;
	typedef signed char int8_t;
#else
	#include <stdint.h>
#endif

#ifdef _MSC_VER
	#ifndef CYBOZU_DEFINED_SSIZE_T
		#define CYBOZU_DEFINED_SSIZE_T
		#ifdef _WIN64
			typedef int64_t ssize_t;
		#else
			typedef int32_t ssize_t;
		#endif
	#endif
#else
	#include <unistd.h> // for ssize_t
#endif

#ifndef CYBOZU_ALIGN
	#ifdef _MSC_VER
		#define CYBOZU_ALIGN(x) __declspec(align(x))
	#else
		#define CYBOZU_ALIGN(x) __attribute__((aligned(x)))
	#endif
#endif
#ifndef CYBOZU_ALLOCA
	#ifdef _MSC_VER
		#include <malloc.h>
		#define CYBOZU_ALLOCA(x) _malloca(x)
	#else
		#define CYBOZU_ALLOCA_(x) __builtin_alloca(x)
	#endif
#endif
#ifndef CYBOZU_FOREACH
	// std::vector<int> v; CYBOZU_FOREACH(auto x, v) {...}
	#if defined(_MSC_VER) && (_MSC_VER >= 1400)
		#define CYBOZU_FOREACH(type_x, xs) for each (type_x in xs)
	#elif defined(__GNUC__)
		#define CYBOZU_FOREACH(type_x, xs) for (type_x : xs)
	#endif
#endif
#ifndef CYBOZU_NUM_OF_ARRAY
	#define CYBOZU_NUM_OF_ARRAY(x) (sizeof(x) / sizeof(*x))
#endif
#ifndef CYBOZU_SNPRINTF
	#ifdef _MSC_VER
		#define CYBOZU_SNPRINTF(x, len, ...) (void)_snprintf_s(x, len, len - 1, __VA_ARGS__)
	#else
		#define CYBOZU_SNPRINTF(x, len, ...) (void)snprintf(x, len, __VA_ARGS__)
	#endif
#endif

#define CYBOZU_CPP_VERSION_CPP03 0
#define CYBOZU_CPP_VERSION_TR1 1
#define CYBOZU_CPP_VERSION_CPP11 2

#if (__cplusplus >= 201103) || (_MSC_VER >= 1500) || defined(__GXX_EXPERIMENTAL_CXX0X__)
	#if defined(_MSC_VER) && (_MSC_VER <= 1600)
		#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1
	#else
		#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP11
	#endif
#elif (__GNUC__ >= 4 && __GNUC_MINOR__ >= 5) || (__clang_major__ >= 3)
	#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1
#else
	#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP03
#endif

#if (CYBOZU_CPP_VERSION == CYBOZU_CPP_VERSION_TR1)
	#define CYBOZU_NAMESPACE_STD std::tr1
	#define CYBOZU_NAMESPACE_TR1_BEGIN namespace tr1 {
	#define CYBOZU_NAMESPACE_TR1_END }
#else
	#define CYBOZU_NAMESPACE_STD std
	#define CYBOZU_NAMESPACE_TR1_BEGIN
	#define CYBOZU_NAMESPACE_TR1_END
#endif

#ifndef CYBOZU_OS_BIT
	#if defined(_WIN64) || defined(__x86_64__)
		#define CYBOZU_OS_BIT 64
	#else
		#define CYBOZU_OS_BIT 32
	#endif
#endif

#ifndef CYBOZU_ENDIAN
	#define CYBOZU_ENDIAN_UNKNOWN 0
	#define CYBOZU_ENDIAN_LITTLE 1
	#define CYBOZU_ENDIAN_BIG 2
	#if defined(_M_IX86) || defined(_M_AMD64) || defined(__x86_64__) || defined(__i386__)
		#define CYBOZU_ENDIAN CYBOZU_ENDIAN_LITTLE
	#else
		#define CYBOZU_ENDIAN CYBOZU_ENDIAN_UNKNOWN
	#endif
#endif

namespace cybozu {
template<class T>
void disable_warning_unused_variable(const T&) { }
template<class T, class S>
T cast(const S* ptr) { return static_cast<T>(static_cast<const void*>(ptr)); }
template<class T, class S>
T cast(S* ptr) { return static_cast<T>(static_cast<void*>(ptr)); }
} // cybozu
<|endoftext|>


<commit_before>// Own header
#include "sized_glyph.h"

// Project headers
#include "master_glyph.h"
#include "text_renderer_aa_internals.h"

// Standard headers
#include <algorithm>
#include <stdlib.h>

// ***************************************************************************
// SizedGlyph
// ***************************************************************************

void SizedGlyph::PutPix(unsigned x, unsigned y, unsigned char val)
{
    unsigned char *line = m_pixelData + y * m_width;
    line[x] = val;
}

SizedGlyph::SizedGlyph(MasterGlyph *mg, unsigned output_size)
{
    // The downsampling code below if mostly cut-n-paste from code written by
    // Ryan Geiss (http://www.geisswerks.com/ryan/FAQS/resize.html)

    double scale_factor = (double)output_size / (double)MASTER_GLYPH_RESOLUTION;
    m_height = round(mg->m_height * scale_factor);
    m_width = round(mg->m_width * scale_factor);
    m_pixelData = new unsigned char[m_width * m_height];

    int w1 = mg->m_width;
    int h1 = mg->m_height;

    int w2 = m_width;
    int h2 = m_height;

    float fh = 256*h1 / (float)h2;
    float fw = 256*w1 / (float)w2;

    // FOR EVERY OUTPUT PIXEL
    for (int y2 = 0; y2 < h2; y2++)
    {
        // find the y-range of input pixels that will contribute:
        int y1a = (int)((y2  )*fh);
        int y1b = (int)((y2+1)*fh);
        y1b = std::min(y1b, 256*h1 - 1);
        int y1c = y1a >> 8;
        int y1d = y1b >> 8;
        y1a &= 0xff;
        y1b &= 0xff;

        for (int x2 = 0; x2 < w2; x2++)
        {
            // find the x-range of input pixels that will contribute:
            int x1a = (int)((x2)*fw);
            int x1b = (int)((x2 + 1)*fw);
            x1b = std::min(x1b, 256 * w1 - 1);
            int x1c = x1a >> 8;
            int x1d = x1b >> 8;
            x1a &= 0xff;
            x1b &= 0xff;

            // ADD UP ALL INPUT PIXELS CONTRIBUTING TO THIS OUTPUT PIXEL:
            unsigned int accumulatedBrightness = 0;
            for (int y = y1c; y <= y1d; y++)
            {
                unsigned int weight_y = 256;
                if (y1c != y1d)
                {
                    if (y == y1c)
                        weight_y = 256 - y1a;
                    else if (y == y1d)
                        weight_y = y1b;
                }

                unsigned char *line = mg->m_pixelData + y * mg->m_widthBytes;
                unsigned accumulatedBrightnessX = 0;

                unsigned int weight_x = 256 - x1a;
                unsigned char pixelByte = line[x1c >> 3];
                unsigned char shiftedPixelByte = pixelByte >> (x1c & 0x7);
                accumulatedBrightnessX += (shiftedPixelByte & 1) * weight_x;

                for (int x = x1c + 1; x < x1d; x++)
                {
                    pixelByte = line[x >> 3];
                    shiftedPixelByte = pixelByte >> (x & 0x7);
                    accumulatedBrightnessX += (shiftedPixelByte & 1) << 8;
                }

                pixelByte = line[x1d >> 3];
                shiftedPixelByte = pixelByte >> (x1d & 0x7);
                accumulatedBrightnessX += (shiftedPixelByte & 1) * x1b;

                accumulatedBrightness += accumulatedBrightnessX * weight_y;
            }

            int xSize = (256 - x1a) + (256 * (x1d - x1c - 1)) + (x1b);
            int ySize = (256 - y1a) + (256 * (y1d - y1c - 1)) + (y1b);
            int accumulatedArea = xSize * ySize;

            // write results
            unsigned int c = accumulatedBrightness / (accumulatedArea >> 8);
            c = std::min(c, (unsigned)255);
            PutPix(x2, y2, c);
        }
    }

    for (unsigned i = 0; i < 255; i++)
        m_kerning[i] = round(mg->m_kerning[i] * scale_factor) + 1;
}

// ***************************************************************************
// SizedGlyphSet
// ***************************************************************************

SizedGlyphSet::SizedGlyphSet(MasterGlyph *masterGlyphSet[256], int size)
{
    for (int i = 0; i < 256; i++)
    {
        MasterGlyph *mg = masterGlyphSet[i];
        if (mg)
        {
            m_sizedGlyphs[i] = new SizedGlyph(mg, size);
        }
        else
        {
            m_sizedGlyphs[i] = NULL;
        }
    }
}
<commit_msg>Cosmetic change only<commit_after>// Own header
#include "sized_glyph.h"

// Project headers
#include "master_glyph.h"
#include "text_renderer_aa_internals.h"

// Standard headers
#include <algorithm>
#include <stdlib.h>

// ***************************************************************************
// SizedGlyph
// ***************************************************************************

void SizedGlyph::PutPix(unsigned x, unsigned y, unsigned char val)
{
    unsigned char *line = m_pixelData + y * m_width;
    line[x] = val;
}

SizedGlyph::SizedGlyph(MasterGlyph *mg, unsigned output_size)
{
    // The downsampling code below is based on code written by Ryan Geiss.
    // (http://www.geisswerks.com/ryan/FAQS/resize.html)

    double scale_factor = (double)output_size / (double)MASTER_GLYPH_RESOLUTION;
    m_height = round(mg->m_height * scale_factor);
    m_width = round(mg->m_width * scale_factor);
    m_pixelData = new unsigned char[m_width * m_height];

    int w1 = mg->m_width;
    int h1 = mg->m_height;

    int w2 = m_width;
    int h2 = m_height;

    float fh = 256*h1 / (float)h2;
    float fw = 256*w1 / (float)w2;

    // FOR EVERY OUTPUT PIXEL
    for (int y2 = 0; y2 < h2; y2++)
    {
        // find the y-range of input pixels that will contribute:
        int y1a = (int)((y2  )*fh);
        int y1b = (int)((y2+1)*fh);
        y1b = std::min(y1b, 256*h1 - 1);
        int y1c = y1a >> 8;
        int y1d = y1b >> 8;
        y1a &= 0xff;
        y1b &= 0xff;

        for (int x2 = 0; x2 < w2; x2++)
        {
            // find the x-range of input pixels that will contribute:
            int x1a = (int)((x2)*fw);
            int x1b = (int)((x2 + 1)*fw);
            x1b = std::min(x1b, 256 * w1 - 1);
            int x1c = x1a >> 8;
            int x1d = x1b >> 8;
            x1a &= 0xff;
            x1b &= 0xff;

            // ADD UP ALL INPUT PIXELS CONTRIBUTING TO THIS OUTPUT PIXEL:
            unsigned int accumulatedBrightness = 0;
            for (int y = y1c; y <= y1d; y++)
            {
                unsigned int weight_y = 256;
                if (y1c != y1d)
                {
                    if (y == y1c)
                        weight_y = 256 - y1a;
                    else if (y == y1d)
                        weight_y = y1b;
                }

                unsigned char *line = mg->m_pixelData + y * mg->m_widthBytes;
                unsigned accumulatedBrightnessX = 0;

                unsigned int weight_x = 256 - x1a;
                unsigned char pixelByte = line[x1c >> 3];
                unsigned char shiftedPixelByte = pixelByte >> (x1c & 0x7);
                accumulatedBrightnessX += (shiftedPixelByte & 1) * weight_x;

                for (int x = x1c + 1; x < x1d; x++)
                {
                    pixelByte = line[x >> 3];
                    shiftedPixelByte = pixelByte >> (x & 0x7);
                    accumulatedBrightnessX += (shiftedPixelByte & 1) << 8;
                }

                pixelByte = line[x1d >> 3];
                shiftedPixelByte = pixelByte >> (x1d & 0x7);
                accumulatedBrightnessX += (shiftedPixelByte & 1) * x1b;

                accumulatedBrightness += accumulatedBrightnessX * weight_y;
            }

            int xSize = (256 - x1a) + (256 * (x1d - x1c - 1)) + (x1b);
            int ySize = (256 - y1a) + (256 * (y1d - y1c - 1)) + (y1b);
            int accumulatedArea = xSize * ySize;

            // write results
            unsigned int c = accumulatedBrightness / (accumulatedArea >> 8);
            c = std::min(c, (unsigned)255);
            PutPix(x2, y2, c);
        }
    }

    for (unsigned i = 0; i < 255; i++)
        m_kerning[i] = round(mg->m_kerning[i] * scale_factor) + 1;
}

// ***************************************************************************
// SizedGlyphSet
// ***************************************************************************

SizedGlyphSet::SizedGlyphSet(MasterGlyph *masterGlyphSet[256], int size)
{
    for (int i = 0; i < 256; i++)
    {
        MasterGlyph *mg = masterGlyphSet[i];
        if (mg)
        {
            m_sizedGlyphs[i] = new SizedGlyph(mg, size);
        }
        else
        {
            m_sizedGlyphs[i] = NULL;
        }
    }
}
<|endoftext|>


<commit_before>#include <QtWidgets>
#include <QObject>
#include <QStandardPaths>

#include "mainwindow.h"
#include "timer.h"

#include "../../Numeric/CRC.hpp"

/*
	General Qt implementation notes:

	*	it seems like Qt doesn't offer a way to constrain the aspect ratio of a view by constraining
		the size of the window (i.e. you can use a custom layout to constrain a view, but that won't
		affect the window, so isn't useful for this project). Therefore the emulation window
*/

MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent)
	, ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	createActions();
	qApp->installEventFilter(this);

	// Set up the emulation timer. Bluffer's guide: the QTimer will post an
	// event to an event loop. QThread is a thread with an event loop.
	// My class, Timer, will be wired up to receive the QTimer's events.
	qTimer = std::make_unique<QTimer>(this);
	qTimer->setInterval(1);

	timerThread = std::make_unique<QThread>(this);

	timer = std::make_unique<Timer>();
	timer->moveToThread(timerThread.get());

	connect(qTimer.get(), SIGNAL(timeout()), timer.get(), SLOT(tick()));

	// Hide the missing ROMs box unless or until it's needed; grab the text it
	// began with as a prefix for future mutation.
	ui->missingROMsBox->setVisible(false);
	romRequestBaseText = ui->missingROMsBox->toPlainText();
}

void MainWindow::createActions() {
	// Create a file menu.
	QMenu *fileMenu = menuBar()->addMenu(tr("&File"));

//	QAction *newAct = new QAction(tr("&New"), this);
//	newAct->setShortcuts(QKeySequence::New);
//	newAct->setStatusTip(tr("Create a new file"));
//	connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
//	fileMenu->addAction(newAct);

	// Add file option: 'Open..."
	QAction *openAct = new QAction(tr("&Open..."), this);
	openAct->setShortcuts(QKeySequence::Open);
	openAct->setStatusTip(tr("Open an existing file"));
	connect(openAct, &QAction::triggered, this, &MainWindow::open);
	fileMenu->addAction(openAct);

}

void MainWindow::open() {
	QString fileName = QFileDialog::getOpenFileName(this);
	if(!fileName.isEmpty()) {
		targets = Analyser::Static::GetTargets(fileName.toStdString());
		if(targets.empty()) {
			qDebug() << "Not media:" << fileName;
		} else {
			qDebug() << "Got media:" << fileName;
			launchMachine();
		}
	}
}

MainWindow::~MainWindow() {
	// Stop the timer by asking its QThread to exit and
	// waiting for it to do so.
	timerThread->exit();
	while(timerThread->isRunning());
}

// MARK: Machine launch.

namespace {

std::unique_ptr<std::vector<uint8_t>> fileContentsAndClose(FILE *file) {
	auto data = std::make_unique<std::vector<uint8_t>>();

	fseek(file, 0, SEEK_END);
	data->resize(std::ftell(file));
	fseek(file, 0, SEEK_SET);
	size_t read = fread(data->data(), 1, data->size(), file);
	fclose(file);

	if(read == data->size()) {
		return data;
	}

	return nullptr;
}

}

void MainWindow::launchMachine() {
	const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
	missingRoms.clear();

	ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this]
		(const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
		std::vector<std::unique_ptr<std::vector<uint8_t>>> results;

		for(const auto &rom: roms) {
			FILE *file = nullptr;
			for(const auto &path: appDataLocations) {
				const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name;
				const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString();

				qDebug() << "Taking a run at " << nativeSource.c_str();

				file = fopen(nativeSource.c_str(), "rb");
				if(file) break;
			}

			if(file) {
				auto data = fileContentsAndClose(file);
				if(data) {
					results.push_back(std::move(data));
					continue;
				}
			}

			results.push_back(nullptr);
			missingRoms.push_back(rom);
		}
		return results;
	};
	Machine::Error error;
	machine.reset(Machine::MachineForTargets(targets, rom_fetcher, error));

	switch(error) {
		default: {
			// TODO: correct assumptions herein that this is the first machine to be
			// assigned to this window.
			ui->missingROMsBox->setVisible(false);
			uiPhase = UIPhase::RunningMachine;

			// Install user-friendly options. TODO: plus user overrides.
//			const auto configurable = machine->configurable_device();
//			if(configurable) {
//				configurable->set_options(configurable->get_options());
//			}

			// Supply the scan target.
			// TODO: in the future, hypothetically, deal with non-scan producers.
			const auto scan_producer = machine->scan_producer();
			if(scan_producer) {
				scan_producer->set_scan_target(ui->openGLWidget->getScanTarget());
			}

			// Install audio output if required.
			const auto audio_producer = machine->audio_producer();
			if(audio_producer) {
				const auto speaker = audio_producer->get_speaker();
				if(speaker) {
					const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
					QAudioFormat idealFormat = defaultDeviceInfo.preferredFormat();

					// Use the ideal format's sample rate, provide stereo as long as at least two channels
					// are available, and — at least for now — assume 512 samples/buffer is a good size.
					audioIsStereo = (idealFormat.channelCount() > 1) && speaker->get_is_stereo();
					audioIs8bit = idealFormat.sampleSize() < 16;
					const int samplesPerBuffer = 512;
					speaker->set_output_rate(idealFormat.sampleRate(), samplesPerBuffer, audioIsStereo);

					// Adjust format appropriately, and create an audio output.
					idealFormat.setChannelCount(1 + int(audioIsStereo));
					idealFormat.setSampleSize(audioIs8bit ? 8 : 16);
					audioOutput = std::make_unique<QAudioOutput>(idealFormat, this);
					audioOutput->setBufferSize(samplesPerBuffer * (audioIsStereo ? 2 : 1) * (audioIs8bit ? 1 : 2));

					qDebug() << idealFormat;

					// Start the output.
					speaker->set_delegate(this);
					audioIODevice = audioOutput->start();
				}
			}

			// If this is a timed machine, start up the timer.
			const auto timedMachine = machine->timed_machine();
			if(timedMachine) {
				timer->setMachine(timedMachine, &machineMutex);
				timerThread->start();
				qTimer->start();
			}

		} break;

		case Machine::Error::MissingROM: {
			ui->missingROMsBox->setVisible(true);
			uiPhase = UIPhase::RequestingROMs;

			// Populate request text.
			QString requestText = romRequestBaseText;
			size_t index = 0;
			for(const auto rom: missingRoms) {
				requestText += "• ";
				requestText += rom.descriptive_name.c_str();

				++index;
				if(index == missingRoms.size()) {
					requestText += ".\n";
					continue;
				}
				if(index == missingRoms.size() - 1) {
					requestText += "; and\n";
					continue;
				}
				requestText += ";\n";
			}
			ui->missingROMsBox->setPlainText(requestText);
		} break;
	}
}

void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector<int16_t> &buffer) {
	const auto bytesWritten = audioIODevice->write(reinterpret_cast<const char *>(buffer.data()), qint64(buffer.size()));
//	qDebug() << bytesWritten << "; " << audioOutput->state();
	(void)bytesWritten;
}

void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
	// Always accept dragged files.
	if(event->mimeData()->hasUrls())
		event->accept();
}

void MainWindow::dropEvent(QDropEvent* event) {
	if(!event->mimeData()->hasUrls()) {
		return;
	}
	event->accept();

	switch(uiPhase) {
		case UIPhase::NoFileSelected:
			// Treat exactly as a File -> Open... .
		break;

		case UIPhase::RequestingROMs: {
			// Attempt to match up the dragged files to the requested ROM list;
			// if and when they match, copy to a writeable QStandardPaths::AppDataLocation
			// and try launchMachine() again.

			bool foundROM = false;
			const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();

			for(const auto &url: event->mimeData()->urls()) {
				const char *const name = url.toLocalFile().toUtf8();
				FILE *const file = fopen(name, "rb");
				const auto contents = fileContentsAndClose(file);
				if(!contents) continue;

				CRC::CRC32 generator;
				const uint32_t crc = generator.compute_crc(*contents);

				for(const auto &rom: missingRoms) {
					if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
						foundROM = true;

						// Ensure the destination folder exists.
						const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name;
						QDir dir(QString::fromStdString(path));
						if (!dir.exists())
							dir.mkpath(".");

						// Write into place.
						const std::string destination =  QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString();
						FILE *const target = fopen(destination.c_str(), "wb");
						fwrite(contents->data(), 1, contents->size(), target);
						fclose(target);
					}
				}
			}

			if(foundROM) launchMachine();
		} break;

		case UIPhase::RunningMachine:
			// Attempt to insert into the running machine.
			qDebug() << "Should start machine";
		break;
	}
}

// MARK: Input capture.

bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
	switch(event->type()) {
		case QEvent::KeyPress:
		case QEvent::KeyRelease: {
			const auto keyEvent = static_cast<QKeyEvent *>(event);
			if(!processEvent(keyEvent)) {
				return false;
			}
		} break;

		default:
		break;
	}

	return QObject::eventFilter(obj, event);
}

bool MainWindow::processEvent(QKeyEvent *event) {
	if(!machine) return true;

	// First version: support keyboard input only.
	const auto keyboardMachine = machine->keyboard_machine();
	if(!keyboardMachine) return true;

#define BIND2(qtKey, clkKey) case Qt::qtKey: key = Inputs::Keyboard::Key::clkKey; break;
#define BIND(key) BIND2(Key_##key, key)

	Inputs::Keyboard::Key key;
	switch(event->key()) {
		default: return true;

		BIND(Escape);
		BIND(F1);	BIND(F2);	BIND(F3);	BIND(F4);	BIND(F5);	BIND(F6);
		BIND(F7);	BIND(F8);	BIND(F9);	BIND(F10);	BIND(F11);	BIND(F12);
		BIND2(Key_Print, PrintScreen);
		BIND(ScrollLock);	BIND(Pause);

		BIND2(Key_AsciiTilde, BackTick);
		BIND2(Key_1, k1);	BIND2(Key_2, k2);	BIND2(Key_3, k3);	BIND2(Key_4, k4);	BIND2(Key_5, k5);
		BIND2(Key_6, k6);	BIND2(Key_7, k7);	BIND2(Key_8, k8);	BIND2(Key_9, k9);	BIND2(Key_0, k0);
		BIND2(Key_Minus, Hyphen);
		BIND2(Key_Plus, Equals);
		BIND(Backspace);

		BIND(Tab);	BIND(Q);	BIND(W);	BIND(E);	BIND(R);	BIND(T);	BIND(Y);
		BIND(U);	BIND(I);	BIND(O);	BIND(P);
		BIND2(Key_BraceLeft, OpenSquareBracket);
		BIND2(Key_BraceRight, CloseSquareBracket);
		BIND(Backslash);

		BIND(CapsLock);	BIND(A);	BIND(S);	BIND(D);	BIND(F);	BIND(G);
		BIND(H);		BIND(J);	BIND(K);	BIND(L);
		BIND(Semicolon);
		BIND2(Key_QuoteDbl, Quote);
		// TODO: something to hash?
		BIND2(Key_Return, Enter);

		BIND2(Key_Shift, LeftShift);
		BIND(Z);	BIND(X);	BIND(C);	BIND(V);
		BIND(B);	BIND(N);	BIND(M);
		BIND(Comma);
		BIND2(Key_Period, FullStop);
		BIND2(Key_Slash, ForwardSlash);
		// Omitted: right shift.

		BIND2(Key_Control, LeftControl);
		BIND2(Key_Alt, LeftOption);
		BIND2(Key_Meta, LeftMeta);
		BIND(Space);
		BIND2(Key_AltGr, RightOption);

		BIND(Left);	BIND(Right);	BIND(Up);	BIND(Down);

		BIND(Insert); BIND(Home);	BIND(PageUp);	BIND(Delete);	BIND(End);	BIND(PageDown);

		BIND(NumLock);
	}

	std::unique_lock lock(machineMutex);
	keyboardMachine->get_keyboard().set_key_pressed(key, event->text()[0].toLatin1(), event->type() == QEvent::KeyPress);

	return true;
}
<commit_msg>QIODevice isn't guaranteed thread safe, so use of it is now thread confined.<commit_after>#include <QtWidgets>
#include <QObject>
#include <QStandardPaths>

#include "mainwindow.h"
#include "timer.h"

#include "../../Numeric/CRC.hpp"

namespace {

struct AudioEvent: public QEvent {
	AudioEvent() : QEvent(QEvent::Type::User) {}
	std::vector<int16_t> audio;
};

}

/*
	General Qt implementation notes:

	*	it seems like Qt doesn't offer a way to constrain the aspect ratio of a view by constraining
		the size of the window (i.e. you can use a custom layout to constrain a view, but that won't
		affect the window, so isn't useful for this project). Therefore the emulation window
*/

MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent)
	, ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	createActions();
	qApp->installEventFilter(this);

	// Set up the emulation timer. Bluffer's guide: the QTimer will post an
	// event to an event loop. QThread is a thread with an event loop.
	// My class, Timer, will be wired up to receive the QTimer's events.
	qTimer = std::make_unique<QTimer>(this);
	qTimer->setInterval(1);

	timerThread = std::make_unique<QThread>(this);

	timer = std::make_unique<Timer>();
	timer->moveToThread(timerThread.get());

	connect(qTimer.get(), SIGNAL(timeout()), timer.get(), SLOT(tick()));

	// Hide the missing ROMs box unless or until it's needed; grab the text it
	// began with as a prefix for future mutation.
	ui->missingROMsBox->setVisible(false);
	romRequestBaseText = ui->missingROMsBox->toPlainText();
}

void MainWindow::createActions() {
	// Create a file menu.
	QMenu *fileMenu = menuBar()->addMenu(tr("&File"));

//	QAction *newAct = new QAction(tr("&New"), this);
//	newAct->setShortcuts(QKeySequence::New);
//	newAct->setStatusTip(tr("Create a new file"));
//	connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
//	fileMenu->addAction(newAct);

	// Add file option: 'Open..."
	QAction *openAct = new QAction(tr("&Open..."), this);
	openAct->setShortcuts(QKeySequence::Open);
	openAct->setStatusTip(tr("Open an existing file"));
	connect(openAct, &QAction::triggered, this, &MainWindow::open);
	fileMenu->addAction(openAct);

}

void MainWindow::open() {
	QString fileName = QFileDialog::getOpenFileName(this);
	if(!fileName.isEmpty()) {
		targets = Analyser::Static::GetTargets(fileName.toStdString());
		if(targets.empty()) {
			qDebug() << "Not media:" << fileName;
		} else {
			qDebug() << "Got media:" << fileName;
			launchMachine();
		}
	}
}

MainWindow::~MainWindow() {
	// Stop the timer by asking its QThread to exit and
	// waiting for it to do so.
	timerThread->exit();
	while(timerThread->isRunning());
}

// MARK: Machine launch.

namespace {

std::unique_ptr<std::vector<uint8_t>> fileContentsAndClose(FILE *file) {
	auto data = std::make_unique<std::vector<uint8_t>>();

	fseek(file, 0, SEEK_END);
	data->resize(std::ftell(file));
	fseek(file, 0, SEEK_SET);
	size_t read = fread(data->data(), 1, data->size(), file);
	fclose(file);

	if(read == data->size()) {
		return data;
	}

	return nullptr;
}

}

void MainWindow::launchMachine() {
	const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
	missingRoms.clear();

	ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this]
		(const std::vector<ROMMachine::ROM> &roms) -> std::vector<std::unique_ptr<std::vector<uint8_t>>> {
		std::vector<std::unique_ptr<std::vector<uint8_t>>> results;

		for(const auto &rom: roms) {
			FILE *file = nullptr;
			for(const auto &path: appDataLocations) {
				const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name;
				const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString();

				qDebug() << "Taking a run at " << nativeSource.c_str();

				file = fopen(nativeSource.c_str(), "rb");
				if(file) break;
			}

			if(file) {
				auto data = fileContentsAndClose(file);
				if(data) {
					results.push_back(std::move(data));
					continue;
				}
			}

			results.push_back(nullptr);
			missingRoms.push_back(rom);
		}
		return results;
	};
	Machine::Error error;
	machine.reset(Machine::MachineForTargets(targets, rom_fetcher, error));

	switch(error) {
		default: {
			// TODO: correct assumptions herein that this is the first machine to be
			// assigned to this window.
			ui->missingROMsBox->setVisible(false);
			uiPhase = UIPhase::RunningMachine;

			// Install user-friendly options. TODO: plus user overrides.
//			const auto configurable = machine->configurable_device();
//			if(configurable) {
//				configurable->set_options(configurable->get_options());
//			}

			// Supply the scan target.
			// TODO: in the future, hypothetically, deal with non-scan producers.
			const auto scan_producer = machine->scan_producer();
			if(scan_producer) {
				scan_producer->set_scan_target(ui->openGLWidget->getScanTarget());
			}

			// Install audio output if required.
			const auto audio_producer = machine->audio_producer();
			if(audio_producer) {
				const auto speaker = audio_producer->get_speaker();
				if(speaker) {
					const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
					QAudioFormat idealFormat = defaultDeviceInfo.preferredFormat();

					// Use the ideal format's sample rate, provide stereo as long as at least two channels
					// are available, and — at least for now — assume 512 samples/buffer is a good size.
					audioIsStereo = (idealFormat.channelCount() > 1) && speaker->get_is_stereo();
					audioIs8bit = idealFormat.sampleSize() < 16;
					const int samplesPerBuffer = 65536;
					speaker->set_output_rate(idealFormat.sampleRate(), samplesPerBuffer, audioIsStereo);

					// Adjust format appropriately, and create an audio output.
					idealFormat.setChannelCount(1 + int(audioIsStereo));
					idealFormat.setSampleSize(audioIs8bit ? 8 : 16);
					audioOutput = std::make_unique<QAudioOutput>(idealFormat, this);
					audioOutput->setBufferSize(samplesPerBuffer * (audioIsStereo ? 2 : 1) * (audioIs8bit ? 1 : 2));

					qDebug() << idealFormat;

					// Start the output.
					speaker->set_delegate(this);
					audioIODevice = audioOutput->start();
				}
			}

			// If this is a timed machine, start up the timer.
			const auto timedMachine = machine->timed_machine();
			if(timedMachine) {
				timer->setMachine(timedMachine, &machineMutex);
				timerThread->start();
				qTimer->start();
			}

		} break;

		case Machine::Error::MissingROM: {
			ui->missingROMsBox->setVisible(true);
			uiPhase = UIPhase::RequestingROMs;

			// Populate request text.
			QString requestText = romRequestBaseText;
			size_t index = 0;
			for(const auto rom: missingRoms) {
				requestText += "• ";
				requestText += rom.descriptive_name.c_str();

				++index;
				if(index == missingRoms.size()) {
					requestText += ".\n";
					continue;
				}
				if(index == missingRoms.size() - 1) {
					requestText += "; and\n";
					continue;
				}
				requestText += ";\n";
			}
			ui->missingROMsBox->setPlainText(requestText);
		} break;
	}
}

void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector<int16_t> &buffer) {
	// Forward this buffrer to the QThread that QAudioOutput lives on.
	AudioEvent *event = new AudioEvent;
	event->audio = buffer;
	QApplication::instance()->postEvent(this, event);
//	const auto bytesWritten = audioIODevice->write(reinterpret_cast<const char *>(buffer.data()), qint64(buffer.size()));
//	qDebug() << bytesWritten << "; " << audioOutput->state();
//	(void)bytesWritten;
}

void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
	// Always accept dragged files.
	if(event->mimeData()->hasUrls())
		event->accept();
}

void MainWindow::dropEvent(QDropEvent* event) {
	if(!event->mimeData()->hasUrls()) {
		return;
	}
	event->accept();

	switch(uiPhase) {
		case UIPhase::NoFileSelected:
			// Treat exactly as a File -> Open... .
		break;

		case UIPhase::RequestingROMs: {
			// Attempt to match up the dragged files to the requested ROM list;
			// if and when they match, copy to a writeable QStandardPaths::AppDataLocation
			// and try launchMachine() again.

			bool foundROM = false;
			const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();

			for(const auto &url: event->mimeData()->urls()) {
				const char *const name = url.toLocalFile().toUtf8();
				FILE *const file = fopen(name, "rb");
				const auto contents = fileContentsAndClose(file);
				if(!contents) continue;

				CRC::CRC32 generator;
				const uint32_t crc = generator.compute_crc(*contents);

				for(const auto &rom: missingRoms) {
					if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
						foundROM = true;

						// Ensure the destination folder exists.
						const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name;
						QDir dir(QString::fromStdString(path));
						if (!dir.exists())
							dir.mkpath(".");

						// Write into place.
						const std::string destination =  QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString();
						FILE *const target = fopen(destination.c_str(), "wb");
						fwrite(contents->data(), 1, contents->size(), target);
						fclose(target);
					}
				}
			}

			if(foundROM) launchMachine();
		} break;

		case UIPhase::RunningMachine:
			// Attempt to insert into the running machine.
			qDebug() << "Should start machine";
		break;
	}
}

// MARK: Input capture.

bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
	switch(event->type()) {
		case QEvent::KeyPress:
		case QEvent::KeyRelease: {
			const auto keyEvent = static_cast<QKeyEvent *>(event);
			if(!processEvent(keyEvent)) {
				return false;
			}
		} break;

		case QEvent::User: {
			const auto audioEvent = dynamic_cast<AudioEvent *>(event);
			if(audioEvent) {
				audioIODevice->write(reinterpret_cast<const char *>(audioEvent->audio.data()), qint64(audioEvent->audio.size()));
			}
		} break;

		default:
		break;
	}

	return QObject::eventFilter(obj, event);
}

bool MainWindow::processEvent(QKeyEvent *event) {
	if(!machine) return true;

	// First version: support keyboard input only.
	const auto keyboardMachine = machine->keyboard_machine();
	if(!keyboardMachine) return true;

#define BIND2(qtKey, clkKey) case Qt::qtKey: key = Inputs::Keyboard::Key::clkKey; break;
#define BIND(key) BIND2(Key_##key, key)

	Inputs::Keyboard::Key key;
	switch(event->key()) {
		default: return true;

		BIND(Escape);
		BIND(F1);	BIND(F2);	BIND(F3);	BIND(F4);	BIND(F5);	BIND(F6);
		BIND(F7);	BIND(F8);	BIND(F9);	BIND(F10);	BIND(F11);	BIND(F12);
		BIND2(Key_Print, PrintScreen);
		BIND(ScrollLock);	BIND(Pause);

		BIND2(Key_AsciiTilde, BackTick);
		BIND2(Key_1, k1);	BIND2(Key_2, k2);	BIND2(Key_3, k3);	BIND2(Key_4, k4);	BIND2(Key_5, k5);
		BIND2(Key_6, k6);	BIND2(Key_7, k7);	BIND2(Key_8, k8);	BIND2(Key_9, k9);	BIND2(Key_0, k0);
		BIND2(Key_Minus, Hyphen);
		BIND2(Key_Plus, Equals);
		BIND(Backspace);

		BIND(Tab);	BIND(Q);	BIND(W);	BIND(E);	BIND(R);	BIND(T);	BIND(Y);
		BIND(U);	BIND(I);	BIND(O);	BIND(P);
		BIND2(Key_BraceLeft, OpenSquareBracket);
		BIND2(Key_BraceRight, CloseSquareBracket);
		BIND(Backslash);

		BIND(CapsLock);	BIND(A);	BIND(S);	BIND(D);	BIND(F);	BIND(G);
		BIND(H);		BIND(J);	BIND(K);	BIND(L);
		BIND(Semicolon);
		BIND2(Key_QuoteDbl, Quote);
		// TODO: something to hash?
		BIND2(Key_Return, Enter);

		BIND2(Key_Shift, LeftShift);
		BIND(Z);	BIND(X);	BIND(C);	BIND(V);
		BIND(B);	BIND(N);	BIND(M);
		BIND(Comma);
		BIND2(Key_Period, FullStop);
		BIND2(Key_Slash, ForwardSlash);
		// Omitted: right shift.

		BIND2(Key_Control, LeftControl);
		BIND2(Key_Alt, LeftOption);
		BIND2(Key_Meta, LeftMeta);
		BIND(Space);
		BIND2(Key_AltGr, RightOption);

		BIND(Left);	BIND(Right);	BIND(Up);	BIND(Down);

		BIND(Insert); BIND(Home);	BIND(PageUp);	BIND(Delete);	BIND(End);	BIND(PageDown);

		BIND(NumLock);
	}

	std::unique_lock lock(machineMutex);
	keyboardMachine->get_keyboard().set_key_pressed(key, event->text()[0].toLatin1(), event->type() == QEvent::KeyPress);

	return true;
}
<|endoftext|>


<commit_before>#include <netdb.h>
#include <stdio.h>
#include <string.h>

#include <arpa/inet.h>

#include <iostream>
#include <string>

#include <boost/unordered_map.hpp>

#include "config.hpp"
#include "pid.hpp"
#include "process.hpp"

using std::istream;
using std::ostream;
using std::size_t;
using std::string;

namespace process {

UPID::UPID(const char* s)
{
  std::istringstream in(s);
  in >> *this;
}

UPID::UPID(const std::string& s)
{
  std::istringstream in(s);
  in >> *this;
}

// TODO(benh): Make this inline-able (cyclic dependency issues).
UPID::UPID(const ProcessBase& process)
{
  id = process.self().id;
  ip = process.self().ip;
  port = process.self().port;
}

UPID::operator std::string() const
{
  std::ostringstream out;
  out << *this;
  return out.str();
}

ostream& operator << (ostream& stream, const UPID& pid)
{
  // Call inet_ntop since inet_ntoa is not thread-safe!
  char ip[INET_ADDRSTRLEN];
  if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
    memset(ip, 0, INET_ADDRSTRLEN);

  stream << pid.id << "@" << ip << ":" << pid.port;
  return stream;
}

istream& operator >> (istream& stream, UPID& pid)
{
  pid.id = "";
  pid.ip = 0;
  pid.port = 0;

  string str;
  if (!(stream >> str)) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  if (str.size() > 512) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  string id;
  string host;
  unsigned short port;

  size_t index = str.find('@');

  if (index != string::npos) {
    id = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1, str.size() - index);

  index = str.find(':');

  if (index != string::npos) {
    host = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  hostent *he = gethostbyname2(host.c_str(), AF_INET);
  if (!he) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1, str.size() - index);

  if (sscanf(str.c_str(), "%hu", &port) != 1) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  pid.id = id;
  pid.ip = *((uint32_t *) he->h_addr);
  pid.port = port;

  return stream;
}

size_t hash_value(const UPID& pid)
{
  size_t seed = 0;
  boost::hash_combine(seed, pid.id);
  boost::hash_combine(seed, pid.ip);
  boost::hash_combine(seed, pid.port);
  return seed;
}

} // namespace process {
<commit_msg>Updated parsing of pids to account for bad DNS resolutions (e.g., sometimes DNS fails and will return nothing).<commit_after>#include <netdb.h>
#include <stdio.h>
#include <string.h>

#include <arpa/inet.h>

#include <iostream>
#include <string>

#include <boost/unordered_map.hpp>

#include "config.hpp"
#include "pid.hpp"
#include "process.hpp"

using std::istream;
using std::ostream;
using std::size_t;
using std::string;

namespace process {

UPID::UPID(const char* s)
{
  std::istringstream in(s);
  in >> *this;
}

UPID::UPID(const std::string& s)
{
  std::istringstream in(s);
  in >> *this;
}

// TODO(benh): Make this inline-able (cyclic dependency issues).
UPID::UPID(const ProcessBase& process)
{
  id = process.self().id;
  ip = process.self().ip;
  port = process.self().port;
}

UPID::operator std::string() const
{
  std::ostringstream out;
  out << *this;
  return out.str();
}

ostream& operator << (ostream& stream, const UPID& pid)
{
  // Call inet_ntop since inet_ntoa is not thread-safe!
  char ip[INET_ADDRSTRLEN];
  if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
    memset(ip, 0, INET_ADDRSTRLEN);

  stream << pid.id << "@" << ip << ":" << pid.port;
  return stream;
}

istream& operator >> (istream& stream, UPID& pid)
{
  pid.id = "";
  pid.ip = 0;
  pid.port = 0;

  string str;
  if (!(stream >> str)) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  if (str.size() == 0) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  string id;
  string host;
  unsigned short port;

  size_t index = str.find('@');

  if (index != string::npos) {
    id = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1, str.size() - index);

  index = str.find(':');

  if (index != string::npos) {
    host = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  hostent* he = gethostbyname2(host.c_str(), AF_INET);
  if (he == NULL) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  if (he->h_addr_list[0] == NULL) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1, str.size() - index);

  if (sscanf(str.c_str(), "%hu", &port) != 1) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  pid.id = id;
  pid.ip = *((uint32_t*) he->h_addr_list[0]);
  pid.port = port;

  return stream;
}

size_t hash_value(const UPID& pid)
{
  size_t seed = 0;
  boost::hash_combine(seed, pid.id);
  boost::hash_combine(seed, pid.ip);
  boost::hash_combine(seed, pid.port);
  return seed;
}

} // namespace process {
<|endoftext|>