<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDir>
#include <QTextStream>
#include <QSettings>
#include <QStandardPaths>
#include <QProcessEnvironment>
#include <fstream>
#include <algorithm>
using namespace std;

// Macro to manage mkdir call (Windows vs Unix)
#ifdef _WIN32
#include <direct.h>
#define MKDIR(PATH) mkdir(PATH)
#else
#include <sys/stat.h>
#define MKDIR(PATH) mkdir(PATH, 0775)
#endif

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    readSettings();
    detectPaths();
    detectSteamUsers();
    selectGame(0);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::readSettings()
{
  QSettings settings("Games.ini", QSettings::IniFormat);
  QStringList groups = settings.childGroups();
  int nbgroups = groups.size();

  for(int i=0; i<nbgroups; ++i)
  {
    settings.beginGroup(groups[i]);

    // Name of the game
    GameFWL newgame;
    newgame.setName(groups[i]);

    // AppID, savegames extension and offset to strip header
    newgame.setAppid(settings.value("app","0").toString());
    newgame.setSavename(settings.value("savename").toString());
    newgame.setExtension(settings.value("extension").toString());
    newgame.setOffset(settings.value("offset").toString());

    // Get possible paths to the savegames
    QStringList steamcloud = settings.value("steamcloud").toStringList();
    QStringList steamapps = settings.value("steamapps").toStringList();
    QStringList documents = settings.value("documents").toStringList();
    QStringList local = settings.value("local").toStringList();
    QStringList appdata = settings.value("appdata").toStringList();
    QStringList publicfolder = settings.value("public").toStringList();
    QStringList publicdata = settings.value("publicdata").toStringList();

    // Check if paths are empty before storing them
    if(steamcloud.size() > 1)
      if(!steamcloud[0].isEmpty())
        newgame.setPathInSteamCloud(steamcloud);

    if(steamapps.size() > 1)
      if(!steamapps[0].isEmpty())
        newgame.setPathInSteamApps(steamapps);

    if(documents.size() > 1)
      if(!documents[0].isEmpty())
        newgame.setPathInDocs(documents);

    if(local.size() > 1)
      if(!local[0].isEmpty())
        newgame.setPathInLocal(local);

    if(appdata.size() > 1)
      if(!appdata[0].isEmpty())
        newgame.setPathInAppData(appdata);

    if(publicfolder.size() > 1)
      if(!publicfolder[0].isEmpty())
        newgame.setPathInPublic(publicfolder);

    if(publicdata.size() > 1)
      if(!publicdata[0].isEmpty())
        newgame.setPathInPublicData(publicdata);

    // Done reading game info
    settings.endGroup();

    // New game added to the list of games
    games.push_back(newgame);
  }
  sort(games.begin(),games.end());

  foreach(GameFWL game, games)
    ui->game_selection->addItem(game.getName());
}

void MainWindow::detectPaths()
{
  QSettings settings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", QSettings::NativeFormat);
  steampath = QDir::toNativeSeparators(settings.value("SteamPath", "").toString());

  QStringList doclist = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
  if(!doclist.empty())
    documentspath = QDir::toNativeSeparators(doclist[0]);

  QStringList genericdata = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
  if(!genericdata.empty())
    localpath = genericdata[0] + QDir::separator();

  if(genericdata.size() > 1)
    publicdatapath = genericdata[1] + QDir::separator();

  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
  appdatapath = env.value("APPDATA") + QDir::separator();
  publicpath = env.value("PUBLIC") + QDir::separator();

  // Add trailing separators if paths not empty
  if(!steampath.isEmpty())
    steampath += QDir::separator();
  if(!documentspath.isEmpty())
    documentspath += QDir::separator();
  if(!localpath.isEmpty())
    localpath += QDir::separator();
  if(!appdatapath.isEmpty())
    appdatapath += QDir::separator();
  if(!publicdatapath.isEmpty())
    publicdatapath += QDir::separator();
  if(!publicpath.isEmpty())
    publicpath += QDir::separator();
}

void MainWindow::detectSavePath()
{
  int found = 0;
  GameFWL game = games[gameidx];

  QStringList possiblepaths;
  QString detectedpath;

  // We add the possible paths in documents
  if(!documentspath.isEmpty())
    foreach(QString path, game.getPathInDocs())
      possiblepaths.push_back(QDir::toNativeSeparators(documentspath + path));

  // We add the possible paths in Steam Cloud
  QString steamcloudpath;
  if(!steampath.isEmpty() && !game.getPathInSteamCloud().isEmpty())
  {
    steamcloudpath = steampath + QString("userdata") + QDir::separator();

    // We look in each Steam user folder
    QDir userdata(steamcloudpath);
    QFileInfoList steamusers = userdata.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
    for(int i=0; i<steamusers.size() && !found; ++i)
    {
      QString user = QDir::toNativeSeparators(steamusers[i].canonicalFilePath() + QDir::separator());
      foreach(QString appid, game.getPathInSteamCloud())
        possiblepaths.push_back(QDir::toNativeSeparators(user + appid + QString("/remote")));
    }
  }

  // We add the possible paths in SteamApps
  QString steamappspath;
  if(!steampath.isEmpty())
  {
    steamappspath = steampath + QString("steamapps") + QDir::separator();
    foreach(QString path, game.getPathInSteamApps())
      possiblepaths.push_back(QDir::toNativeSeparators(steamappspath + path));
  }

  // We add the possible paths in "appdata"
  if(!appdatapath.isEmpty())
    foreach(QString path, game.getPathInAppData())
      possiblepaths.push_back(QDir::toNativeSeparators(appdatapath + path));

  // We add the possible paths in "local"
  if(!localpath.isEmpty())
    foreach(QString path, game.getPathInLocal())
      possiblepaths.push_back(QDir::toNativeSeparators(localpath + path));

  // We add the possible paths in "public"
  if(!publicpath.isEmpty())
    foreach(QString path, game.getPathInPublic())
      possiblepaths.push_back(QDir::toNativeSeparators(publicpath + path));

  // We add the possible paths in "publicdata"
  if(!publicdatapath.isEmpty())
    foreach(QString path, game.getPathInPublicData())
      possiblepaths.push_back(QDir::toNativeSeparators(publicdatapath + path));

  for(int i=0; i<possiblepaths.size() && found<1; ++i)
  {
    QDir folder(possiblepaths[i]);
    QFileInfoList files = folder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Time);
    foreach(QFileInfo file, files)
    {
      if(file.completeSuffix() == game.getExtension())
        ++found;
      if(found > 0)
        detectedpath = folder.canonicalPath();
    }

    if(found == 0)
    {
      QFileInfoList subfolders = folder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
      foreach(QFileInfo subfolder, subfolders)
      {
        QDir subdir(subfolder.canonicalFilePath());
        QFileInfoList subfiles = subdir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Time);
        foreach(QFileInfo subfile, subfiles)
        {
          if(subfile.completeSuffix() == game.getExtension())
            ++found;
          if(found > 0)
            detectedpath = subdir.canonicalPath();
        }
      }
    }
  }

  ui->filename_lineEdit->setText(QDir::toNativeSeparators(detectedpath));
  ui->statusBar->showMessage(QString("Detected files: ") + QString::number(found));
  selectFolder(detectedpath);
}

void MainWindow::detectSteamUsers()
{
  if(!steampath.isEmpty())
  {
    // List to display
    QStringList display;

    // Path to Steam Cloud
    QString steamcloudpath = steampath + QString("userdata") + QDir::separator();

    // We look in each Steam user folder
    QDir userdata(steamcloudpath);
    QFileInfoList steamuids = userdata.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
    for(int i=0; i<steamuids.size(); ++i)
    {
      QString uid = QDir::toNativeSeparators(steamuids[i].baseName());
      QString user = getPersona(uid);
      display.push_back(uid + QString(" (") + user + QString(")"));
      uids.push_back(uid);
    }
    ui->dest_selection->addItems(display);
  }
  else
  {
    uids.clear();
    ui->dest_selection->setDisabled(true);
    ui->dest_label->setText(QString("Steam not found."));
  }
}

QString MainWindow::getPersona(QString uid)
{
  QString res;
  if(!steampath.isEmpty())
  {
    QString steamcloudpath = steampath + QString("userdata") + QDir::separator();
    QString localconfig = steamcloudpath + uid + QDir::separator() + QString("config") + QDir::separator() + "localconfig.vdf";

    ifstream file(localconfig.toStdString().c_str());
    string line;
    if(file.is_open())
    {
      while(getline(file,line) && res.isEmpty())
      {
        QString qline = QString::fromStdString(line);
        if(qline.contains("PersonaName"))
          res = qline.replace("\"PersonaName\"","").replace("\t","").replace('"',"");
      }
      file.close();
    }
  }
  return res;
}

int MainWindow::copyToSteam()
{
  int nbcopied = 0;

  // If we've found SteamIDs
  if(!uids.isEmpty())
  {
    GameFWL game = games[gameidx];
    QString appid = game.getAppid();
    QString steamuid = uids[ui->dest_selection->currentIndex()];
    QChar sep = QDir::separator();

    QString steamcloudappid = steampath + QString("userdata") + sep + steamuid + sep + appid + sep;
    QString steamcloudbackup = documentspath + appid + ".bak";

    // Check if directory already exists
    bool backup = false;
    QDir dir(steamcloudappid);
    if(dir.exists())
    {
      // Old saves backup
      if(dir.rename(steamcloudappid,steamcloudbackup))
        backup = true;
    }
    else
      backup = true;

    // Copy patched files to Steam if previous folder has been backed up
    if(backup)
    {
      int mkd1 = MKDIR(steamcloudappid.toStdString().c_str());
      if(mkd1 == 0 || errno == EEXIST)
      {
        QString remotefolder = steamcloudappid + QString("remote") + sep;
        int mkd2 = MKDIR(remotefolder.toStdString().c_str());

        if(mkd2 == 0 || errno == EEXIST)
        {
          QDir outputfolder = QDir(appid + QDir::separator() + "remote" + QDir::separator());
          QFileInfoList files = outputfolder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
          foreach(QFileInfo file, files)
          {
            QFile tmp(file.canonicalFilePath());
            QString newfile = remotefolder + file.fileName();
            if(tmp.copy(newfile))
              ++nbcopied;
          }
        }
      }
    }
  }
  return nbcopied;
}

void MainWindow::selectFolder(const QString & path)
{
  if(path.isEmpty())
  {
    QString currentpath = ui->filename_lineEdit->text();
    savepath = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Choose savegame directory"),currentpath));
    if(savepath != 0)
    {
      ui->filename_lineEdit->setText(savepath);
      ui->start_button->setEnabled(true);
      ui->statusBar->clearMessage();
    }
    else
    {
      ui->start_button->setEnabled(false);
    }
  }
  else
  {
    savepath = path;
    ui->start_button->setEnabled(true);
  }
}

void MainWindow::processFolder()
{
  QString message;
  QDir savefolder(savepath);
  GameFWL game = games[gameidx];
  int processed = 0;

  QString appid = game.getAppid();
  QString outputfolder = appid + QDir::separator() + "remote" + QDir::separator();

  if(savefolder.exists())
  {
    QFileInfoList files = savefolder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
    for(int i=0; i<files.size(); ++i)
    {
      if(files[i].completeSuffix() == game.getExtension())
      {
        int mkd1 = MKDIR(appid.toStdString().c_str());
        if(mkd1 == 0 || errno == EEXIST)
        {
          int mkd2 = MKDIR(outputfolder.toStdString().c_str());
          if(mkd2 == 0 || errno == EEXIST)
          {
            QString newname = files[i].fileName();
            if(!game.getSavename().isEmpty())
            {
              if(~newname.startsWith(game.getSavename(),Qt::CaseInsensitive))
              {
                int ind = newname.indexOf(game.getSavename(),0,Qt::CaseInsensitive);
                if(ind != -1)
                  newname = newname.remove(0,ind);
              }
            }

            QString savefile = files[i].canonicalFilePath();
            QString newsavefile = outputfolder + newname;

            ifstream input_file(savefile.toStdString().c_str(),ios::binary);
            ofstream output_file(newsavefile.toStdString().c_str(),ios::binary);

            if (input_file.is_open() && output_file.is_open())
            {
              input_file.seekg(game.getOffset());
              output_file << input_file.rdbuf();
              output_file.close();
              input_file.close();
            }
            ++processed;
          }
        }
      }
    }
  }
  message = "Processed " + QString::number(processed) + " file(s) into " + appid + ". ";
  int nbcopied = copyToSteam();
  if(nbcopied > 0)
    message = message + QString::number(nbcopied) + " files copied to Steam. ";
  ui->statusBar->showMessage(message);
}
<commit_msg>Bug fixes: - Do not ask a path automatically if none was found. - Name backup folder appropriately if there are others.<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDir>
#include <QTextStream>
#include <QSettings>
#include <QStandardPaths>
#include <QProcessEnvironment>
#include <fstream>
#include <algorithm>
using namespace std;

// Macro to manage mkdir call (Windows vs Unix)
#ifdef _WIN32
#include <direct.h>
#define MKDIR(PATH) mkdir(PATH)
#else
#include <sys/stat.h>
#define MKDIR(PATH) mkdir(PATH, 0775)
#endif

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    readSettings();
    detectPaths();
    detectSteamUsers();
    selectGame(0);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::readSettings()
{
  QSettings settings("Games.ini", QSettings::IniFormat);
  QStringList groups = settings.childGroups();
  int nbgroups = groups.size();

  for(int i=0; i<nbgroups; ++i)
  {
    settings.beginGroup(groups[i]);

    // Name of the game
    GameFWL newgame;
    newgame.setName(groups[i]);

    // AppID, savegames extension and offset to strip header
    newgame.setAppid(settings.value("app","0").toString());
    newgame.setSavename(settings.value("savename").toString());
    newgame.setExtension(settings.value("extension").toString());
    newgame.setOffset(settings.value("offset").toString());

    // Get possible paths to the savegames
    QStringList steamcloud = settings.value("steamcloud").toStringList();
    QStringList steamapps = settings.value("steamapps").toStringList();
    QStringList documents = settings.value("documents").toStringList();
    QStringList local = settings.value("local").toStringList();
    QStringList appdata = settings.value("appdata").toStringList();
    QStringList publicfolder = settings.value("public").toStringList();
    QStringList publicdata = settings.value("publicdata").toStringList();

    // Check if paths are empty before storing them
    if(steamcloud.size() > 1)
      if(!steamcloud[0].isEmpty())
        newgame.setPathInSteamCloud(steamcloud);

    if(steamapps.size() > 1)
      if(!steamapps[0].isEmpty())
        newgame.setPathInSteamApps(steamapps);

    if(documents.size() > 1)
      if(!documents[0].isEmpty())
        newgame.setPathInDocs(documents);

    if(local.size() > 1)
      if(!local[0].isEmpty())
        newgame.setPathInLocal(local);

    if(appdata.size() > 1)
      if(!appdata[0].isEmpty())
        newgame.setPathInAppData(appdata);

    if(publicfolder.size() > 1)
      if(!publicfolder[0].isEmpty())
        newgame.setPathInPublic(publicfolder);

    if(publicdata.size() > 1)
      if(!publicdata[0].isEmpty())
        newgame.setPathInPublicData(publicdata);

    // Done reading game info
    settings.endGroup();

    // New game added to the list of games
    games.push_back(newgame);
  }
  sort(games.begin(),games.end());

  foreach(GameFWL game, games)
    ui->game_selection->addItem(game.getName());
}

void MainWindow::detectPaths()
{
  QSettings settings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", QSettings::NativeFormat);
  steampath = QDir::toNativeSeparators(settings.value("SteamPath", "").toString());

  QStringList doclist = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
  if(!doclist.empty())
    documentspath = QDir::toNativeSeparators(doclist[0]);

  QStringList genericdata = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
  if(!genericdata.empty())
    localpath = genericdata[0] + QDir::separator();

  if(genericdata.size() > 1)
    publicdatapath = genericdata[1] + QDir::separator();

  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
  appdatapath = env.value("APPDATA") + QDir::separator();
  publicpath = env.value("PUBLIC") + QDir::separator();

  // Add trailing separators if paths not empty
  if(!steampath.isEmpty())
    steampath += QDir::separator();
  if(!documentspath.isEmpty())
    documentspath += QDir::separator();
  if(!localpath.isEmpty())
    localpath += QDir::separator();
  if(!appdatapath.isEmpty())
    appdatapath += QDir::separator();
  if(!publicdatapath.isEmpty())
    publicdatapath += QDir::separator();
  if(!publicpath.isEmpty())
    publicpath += QDir::separator();
}

void MainWindow::detectSavePath()
{
  int found = 0;
  GameFWL game = games[gameidx];

  QStringList possiblepaths;
  QString detectedpath;

  // We add the possible paths in documents
  if(!documentspath.isEmpty())
    foreach(QString path, game.getPathInDocs())
      possiblepaths.push_back(QDir::toNativeSeparators(documentspath + path));

  // We add the possible paths in Steam Cloud
  QString steamcloudpath;
  if(!steampath.isEmpty() && !game.getPathInSteamCloud().isEmpty())
  {
    steamcloudpath = steampath + QString("userdata") + QDir::separator();

    // We look in each Steam user folder
    QDir userdata(steamcloudpath);
    QFileInfoList steamusers = userdata.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
    for(int i=0; i<steamusers.size() && !found; ++i)
    {
      QString user = QDir::toNativeSeparators(steamusers[i].canonicalFilePath() + QDir::separator());
      foreach(QString appid, game.getPathInSteamCloud())
        possiblepaths.push_back(QDir::toNativeSeparators(user + appid + QString("/remote")));
    }
  }

  // We add the possible paths in SteamApps
  QString steamappspath;
  if(!steampath.isEmpty())
  {
    steamappspath = steampath + QString("steamapps") + QDir::separator();
    foreach(QString path, game.getPathInSteamApps())
      possiblepaths.push_back(QDir::toNativeSeparators(steamappspath + path));
  }

  // We add the possible paths in "appdata"
  if(!appdatapath.isEmpty())
    foreach(QString path, game.getPathInAppData())
      possiblepaths.push_back(QDir::toNativeSeparators(appdatapath + path));

  // We add the possible paths in "local"
  if(!localpath.isEmpty())
    foreach(QString path, game.getPathInLocal())
      possiblepaths.push_back(QDir::toNativeSeparators(localpath + path));

  // We add the possible paths in "public"
  if(!publicpath.isEmpty())
    foreach(QString path, game.getPathInPublic())
      possiblepaths.push_back(QDir::toNativeSeparators(publicpath + path));

  // We add the possible paths in "publicdata"
  if(!publicdatapath.isEmpty())
    foreach(QString path, game.getPathInPublicData())
      possiblepaths.push_back(QDir::toNativeSeparators(publicdatapath + path));

  for(int i=0; i<possiblepaths.size() && found<1; ++i)
  {
    QDir folder(possiblepaths[i]);
    QFileInfoList files = folder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Time);
    foreach(QFileInfo file, files)
    {
      if(file.completeSuffix() == game.getExtension())
        ++found;
      if(found > 0)
        detectedpath = folder.canonicalPath();
    }

    if(found == 0)
    {
      QFileInfoList subfolders = folder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
      foreach(QFileInfo subfolder, subfolders)
      {
        QDir subdir(subfolder.canonicalFilePath());
        QFileInfoList subfiles = subdir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Time);
        foreach(QFileInfo subfile, subfiles)
        {
          if(subfile.completeSuffix() == game.getExtension())
            ++found;
          if(found > 0)
            detectedpath = subdir.canonicalPath();
        }
      }
    }
  }

  ui->filename_lineEdit->setText(QDir::toNativeSeparators(detectedpath));
  ui->statusBar->showMessage(QString("Detected files: ") + QString::number(found));

  // We set detectedpath to "Not found." to avoid asking automatically the user the path when the tool starts.
  if(detectedpath.isEmpty())
    detectedpath = "Not found.";

  selectFolder(detectedpath);
}

void MainWindow::detectSteamUsers()
{
  if(!steampath.isEmpty())
  {
    // List to display
    QStringList display;

    // Path to Steam Cloud
    QString steamcloudpath = steampath + QString("userdata") + QDir::separator();

    // We look in each Steam user folder
    QDir userdata(steamcloudpath);
    QFileInfoList steamuids = userdata.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time);
    for(int i=0; i<steamuids.size(); ++i)
    {
      QString uid = QDir::toNativeSeparators(steamuids[i].baseName());
      QString user = getPersona(uid);
      display.push_back(uid + QString(" (") + user + QString(")"));
      uids.push_back(uid);
    }
    ui->dest_selection->addItems(display);
  }
  else
  {
    uids.clear();
    ui->dest_selection->setDisabled(true);
    ui->dest_label->setText(QString("Steam not found."));
  }
}

QString MainWindow::getPersona(QString uid)
{
  QString res;
  if(!steampath.isEmpty())
  {
    QString steamcloudpath = steampath + QString("userdata") + QDir::separator();
    QString localconfig = steamcloudpath + uid + QDir::separator() + QString("config") + QDir::separator() + "localconfig.vdf";

    ifstream file(localconfig.toStdString().c_str());
    string line;
    if(file.is_open())
    {
      while(getline(file,line) && res.isEmpty())
      {
        QString qline = QString::fromStdString(line);
        if(qline.contains("PersonaName"))
          res = qline.replace("\"PersonaName\"","").replace("\t","").replace('"',"");
      }
      file.close();
    }
  }
  return res;
}

int MainWindow::copyToSteam()
{
  int nbcopied = 0;

  // If we've found SteamIDs
  if(!uids.isEmpty())
  {
    GameFWL game = games[gameidx];
    QString appid = game.getAppid();
    QString steamuid = uids[ui->dest_selection->currentIndex()];
    QChar sep = QDir::separator();

    QString steamcloudappid = steampath + QString("userdata") + sep + steamuid + sep + appid + sep;
    QString steamcloudbackup = documentspath + "backup." + appid;

    // We check how many backups exist and name the next one accordingly.
    QDir dir(steamcloudbackup);
    int nbbak = 0;
    while(dir.exists())
    {
      ++nbbak;
      dir.setPath(steamcloudbackup + QString(".") + QString::number(nbbak));
    }
    steamcloudbackup = dir.absolutePath();

    // Check if directory already exists
    bool backup = false;
    dir.setPath(steamcloudappid);
    if(dir.exists())
    {
      // Old saves backup
      backup = dir.rename(steamcloudappid,steamcloudbackup);
    }
    else
      backup = true;

    // Copy patched files to Steam if previous folder has been backed up
    if(backup)
    {
      int mkd1 = MKDIR(steamcloudappid.toStdString().c_str());
      if(mkd1 == 0 || errno == EEXIST)
      {
        QString remotefolder = steamcloudappid + QString("remote") + sep;
        int mkd2 = MKDIR(remotefolder.toStdString().c_str());

        if(mkd2 == 0 || errno == EEXIST)
        {
          QDir outputfolder = QDir(appid + QDir::separator() + "remote" + QDir::separator());
          QFileInfoList files = outputfolder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
          foreach(QFileInfo file, files)
          {
            QFile tmp(file.canonicalFilePath());
            QString newfile = remotefolder + file.fileName();
            if(tmp.copy(newfile))
              ++nbcopied;
          }
        }
      }
    }
  }
  return nbcopied;
}

void MainWindow::selectFolder(const QString & path)
{
  if(path.isEmpty())
  {
    QString currentpath = ui->filename_lineEdit->text();
    savepath = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Choose savegame directory"),currentpath));
    if(savepath != 0)
    {
      ui->filename_lineEdit->setText(savepath);
      ui->start_button->setEnabled(true);
      ui->statusBar->clearMessage();
    }
    else
    {
      ui->start_button->setEnabled(false);
    }
  }
  else
  {
    // We do nothing if the path was not found automatically.
    if(path.compare(QString("Not found."),Qt::CaseInsensitive) != 0)
    {
      savepath = path;
      ui->start_button->setEnabled(true);
    }
    else
    {
      ui->start_button->setEnabled(false);
    }
  }
}

void MainWindow::processFolder()
{
  QString message;
  QDir savefolder(savepath);
  GameFWL game = games[gameidx];
  int processed = 0;

  QString appid = game.getAppid();
  QString outputfolder = appid + QDir::separator() + "remote" + QDir::separator();

  if(savefolder.exists())
  {
    QFileInfoList files = savefolder.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
    for(int i=0; i<files.size(); ++i)
    {
      if(files[i].completeSuffix() == game.getExtension())
      {
        int mkd1 = MKDIR(appid.toStdString().c_str());
        if(mkd1 == 0 || errno == EEXIST)
        {
          int mkd2 = MKDIR(outputfolder.toStdString().c_str());
          if(mkd2 == 0 || errno == EEXIST)
          {
            QString newname = files[i].fileName();
            if(!game.getSavename().isEmpty())
            {
              if(~newname.startsWith(game.getSavename(),Qt::CaseInsensitive))
              {
                int ind = newname.indexOf(game.getSavename(),0,Qt::CaseInsensitive);
                if(ind != -1)
                  newname = newname.remove(0,ind);
              }
            }

            QString savefile = files[i].canonicalFilePath();
            QString newsavefile = outputfolder + newname;

            ifstream input_file(savefile.toStdString().c_str(),ios::binary);
            ofstream output_file(newsavefile.toStdString().c_str(),ios::binary);

            if (input_file.is_open() && output_file.is_open())
            {
              input_file.seekg(game.getOffset());
              output_file << input_file.rdbuf();
              output_file.close();
              input_file.close();
            }
            ++processed;
          }
        }
      }
    }
  }
  message = "Processed " + QString::number(processed) + " file(s) into " + appid + ". ";
  int nbcopied = copyToSteam();
  if(nbcopied > 0)
    message = message + QString::number(nbcopied) + " files copied to Steam. ";
  ui->statusBar->showMessage(message);
}
<|endoftext|>


<commit_before>#include <boost/filesystem.hpp>

#include <QFileDialog>
#include <QMessageBox>

#include "fish_detector/common/species_dialog.h"
#include "fish_detector/image_annotator/image_annotation.h"
#include "fish_detector/image_annotator/mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_detector { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , scene_(new QGraphicsScene)
  , ui_(new Ui::MainWidget)
  , species_controls_(new SpeciesControls(this))
  , image_files_() {
  ui_->setupUi(this);
  setStyleSheet("QPushButton { background-color: rgb(230, 230, 230);"
	  "border-style: outset; border-radius: 5px; border-width: 2px; "
    "border-color: grey; padding: 6px;}"
	  "QPushButton:pressed{background-color: rgb(190, 190, 190); "
    "border-style: outset; border-radius: 5px;"
	  "border-width: 2px; border-color: grey; padding: 6px;}");
  ui_->next->setEnabled(false);
  ui_->prev->setEnabled(false);
  ui_->saveAnnotations->setEnabled(false);
  ui_->imageSlider->setEnabled(false);
  ui_->sideBarLayout->addWidget(species_controls_.get());
  QObject::connect(species_controls_.get(),
      SIGNAL(individualAdded(std::string, std::string)),
      this, SLOT(addIndividual(std::string, std::string)));
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_clicked() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_clicked() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  QString filename(image_files_[ui_->imageSlider->value()].c_str());
  QImage current(filename);
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    ui_->imageWindow->setScene(scene_.get());
    ui_->imageWindow->fitInView(scene_->sceneRect(), Qt::KeepAspectRatio);
    ui_->imageWindow->show();
    ui_->fileNameValue->setText(filename);
    auto annotations =
      annotations_->getImageAnnotations(filename.toStdString());
    for(auto annotation : annotations) {
      auto region = new AnnotatedRegion<ImageAnnotation>(
            annotation->id_, annotation, current.rect());
      scene_->addItem(region);
    }
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename.toStdString()
      + std::string(".")).c_str());
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    std::string current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image, species, subspecies, id, Rect(0, 0, 0, 0));
    annotations_->insert(annotation);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        image_files_.push_back(dir_it->path().generic_string());
      }
    }
  }
  if(image_files_.size() > 0) {
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    on_imageSlider_valueChanged();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

#include "../../include/fish_detector/image_annotator/moc_mainwindow.cpp"

}} // namespace fish_detector::image_annotator
<commit_msg>Update count each time current image is updated<commit_after>#include <boost/filesystem.hpp>

#include <QFileDialog>
#include <QMessageBox>

#include "fish_detector/common/species_dialog.h"
#include "fish_detector/image_annotator/image_annotation.h"
#include "fish_detector/image_annotator/mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_detector { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , scene_(new QGraphicsScene)
  , ui_(new Ui::MainWidget)
  , species_controls_(new SpeciesControls(this))
  , image_files_() {
  ui_->setupUi(this);
  setStyleSheet("QPushButton { background-color: rgb(230, 230, 230);"
	  "border-style: outset; border-radius: 5px; border-width: 2px; "
    "border-color: grey; padding: 6px;}"
	  "QPushButton:pressed{background-color: rgb(190, 190, 190); "
    "border-style: outset; border-radius: 5px;"
	  "border-width: 2px; border-color: grey; padding: 6px;}");
  ui_->next->setEnabled(false);
  ui_->prev->setEnabled(false);
  ui_->saveAnnotations->setEnabled(false);
  ui_->imageSlider->setEnabled(false);
  ui_->sideBarLayout->addWidget(species_controls_.get());
  QObject::connect(species_controls_.get(),
      SIGNAL(individualAdded(std::string, std::string)),
      this, SLOT(addIndividual(std::string, std::string)));
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_clicked() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_clicked() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  QString filename(image_files_[ui_->imageSlider->value()].c_str());
  QImage current(filename);
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    ui_->imageWindow->setScene(scene_.get());
    ui_->imageWindow->fitInView(scene_->sceneRect(), Qt::KeepAspectRatio);
    ui_->imageWindow->show();
    ui_->fileNameValue->setText(filename);
    auto annotations =
      annotations_->getImageAnnotations(filename.toStdString());
    for(auto annotation : annotations) {
      auto region = new AnnotatedRegion<ImageAnnotation>(
            annotation->id_, annotation, current.rect());
      scene_->addItem(region);
    }
    auto counts = annotations_->getCounts(filename.toStdString());
    for(auto it = counts.begin(); it != counts.end(); it++) {
      species_controls_->setCount(it->second, it->first);
    }
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename.toStdString()
      + std::string(".")).c_str());
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    std::string current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image, species, subspecies, id, Rect(0, 0, 0, 0));
    annotations_->insert(annotation);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        image_files_.push_back(dir_it->path().generic_string());
      }
    }
  }
  if(image_files_.size() > 0) {
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    on_imageSlider_valueChanged();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

#include "../../include/fish_detector/image_annotator/moc_mainwindow.cpp"

}} // namespace fish_detector::image_annotator
<|endoftext|>


<commit_before>// This file is part of the dune-Pymor project:
//   https://github.com/wwu-numerik/dune-Pymor/
// Copyright holders: Rene Milk, Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH
#define DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH

#include <dune/stuff/common/fvector.hh>
#include <dune/stuff/playground/functions/indicator.hh>
#include <dune/stuff/functions/spe10model2.hh>

#include "default.hh"

namespace Dune {
namespace Pymor {
namespace Functions {
namespace Spe10 {

// default, to allow for specialization
template< class E, class D, size_t d, class R, size_t r, size_t rC = 1 >
class Model2
  : public AffinelyDecomposableFunctionInterface< E, D, d, R, r, rC >
{
  Model2() { static_assert(AlwaysFalse< E >::value, "Not available for these dimensions!"); }
};

template< class E, class D, class R >
class Model2< E, D, 3, R, 3, 3 >
  : public AffinelyDecomposableDefault< E, D, 3, R, 3, 3 >
{
  static const size_t d = 3;
  static const size_t r = 3;
  static const size_t rC = 3;
  typedef AffinelyDecomposableDefault< E, D, d, R, r, rC > BaseType;
  typedef Model2< E, D, d, R, r, rC >                      ThisType;
  typedef Stuff::Functions::Spe10::Model2< E, D, d, R, r, rC > Spe10FunctionType;
public:
  using typename BaseType::DomainType;

  static const bool available = true;

  static std::string static_id()
  {
    return BaseType::BaseType::static_id() + ".spe10.model2";
  }

  static Stuff::Common::Configuration default_config(const std::string sub_name = "")
  {
    auto config = Spe10FunctionType::default_config();
    config["type"] = static_id();
    config["channel_width"] = "[0 0]";
    if (sub_name.empty())
      return config;
    else {
      Stuff::Common::Configuration tmp;
      tmp.add(config, sub_name);
      return tmp;
    }
  } // ... default_config(...)

  static std::unique_ptr< ThisType > create(const Stuff::Common::Configuration config = BaseType::default_config(),
                                            const std::string sub_name = BaseType::static_id())
  {
    // get correct config
    const Stuff::Common::Configuration cfg = config.has_sub(sub_name) ? config.sub(sub_name) : config;
    const Stuff::Common::Configuration default_cfg = default_config();
    // create
    return Stuff::Common::make_unique< ThisType >(
          cfg.get("filename",      default_cfg.get< std::string >("filename")),
          cfg.get("name",          default_cfg.get< std::string >("name")),
          cfg.get("lower_left",    default_cfg.get< DomainType >("lower_left")),
          cfg.get("upper_right",   default_cfg.get< DomainType >("upper_right")),
          cfg.get("channel_width", default_cfg.get< Stuff::Common::FieldVector< D, 2 > >("channel_width")),
          cfg.get("min",           default_cfg.get< R >("min")),
          cfg.get("max",           default_cfg.get< R >("max")));
  } // ... create(...)

  Model2(const std::string& filename = default_config().get< std::string >("filename"),
         const std::string nm = default_config().get< std::string >("name"),
         const Stuff::Common::FieldVector< D, d >& lower_left
            = default_config().get< DomainType >("lower_left"),
         const Stuff::Common::FieldVector< D, d >& upper_right
            = default_config().get< DomainType >("upper_right"),
         const Stuff::Common::FieldVector< D, 2 >& channel_width
            = default_config.get< Stuff::Common::FieldVector< D, 2 > >("channel_width"),
         const R min = default_config().get< R >("min"),
         const R max = default_config().get< R >("max"))
    : BaseType(nm)
  {
    typedef Stuff::Functions::Constant<E, D, 3, R, 1>     ScalarConstantFunctionType;
    typedef Stuff::Functions::Indicator<E, D, 3, R, 1>    ScalarIndicatorFunctionType;
    typedef Stuff::Functions::Indicator<E, D, 3, R, 3, 3> TensorIndicatorFunctionType;

    auto spe10 = std::make_shared<Spe10FunctionType>(filename, "spe10", lower_left, upper_right, min, max);
    this->register_affine_part(spe10);

    auto one = std::make_shared<ScalarConstantFunctionType>(1, "one");
    if (channel_width[0] > 0) {
      auto channel_x = std::shared_ptr<ScalarIndicatorFunctionType>(new ScalarIndicatorFunctionType(
          {{{{lower_left[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. - channel_width[0]/2.,
              lower_left[2]},
             {upper_right[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. + channel_width[0]/2.,
                  lower_left[2] + (upper_right[2] - lower_left[2])/2.}},
            1.}}));
      typename TensorIndicatorFunctionType::RangeType channel_x_scaled_value(0.);
      channel_x_scaled_value[0][0] = channel_x_scaled_value[1][1] = channel_x_scaled_value[2][2] = max;
      auto channel_x_scaled = std::shared_ptr<TensorIndicatorFunctionType>(new TensorIndicatorFunctionType(
          {{{{lower_left[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. - channel_width[0]/2.,
                  lower_left[2]},
             {upper_right[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. + channel_width[0]/2.,
                  lower_left[2] + (upper_right[2] - lower_left[2])/2.}},
            channel_x_scaled_value}}));
      auto channel_x_remover = Stuff::Functions::make_difference(one, channel_x, "channel_x_remover");
      auto spe10_wo_channel_x = Stuff::Functions::make_product(channel_x_remover, spe10, "spe10_wo_channel_x");
      auto spe10_w_scaled_channel_x = Stuff::Functions::make_sum(spe10_wo_channel_x,
                                                                 channel_x_scaled,
                                                                 "spe10_w_scaled_channel_x");
      this->register_component(Stuff::Functions::make_difference(spe10_w_scaled_channel_x, spe10, "component_x"),
                               new Pymor::ParameterFunctional("channel",
                                                              channel_width[1] > 0 ? 2 : 1,
                               "channel[0]"));
    }

    if (channel_width[1] > 0) {
      auto channel_y = std::shared_ptr<ScalarIndicatorFunctionType>(new ScalarIndicatorFunctionType(
          {{{{lower_left[0] + (upper_right[0] - lower_left[0])/2. - channel_width[1]/2.,
                lower_left[1],
                  lower_left[2] + (upper_right[2] - lower_left[2])/2.},
             {lower_left[0] + (upper_right[0] - lower_left[0])/2. + channel_width[1]/2.,
                upper_right[1],
                  upper_right[2]}},
            1.}}));
      typename TensorIndicatorFunctionType::RangeType channel_y_scaled_value(0.);
      channel_y_scaled_value[0][0] = channel_y_scaled_value[1][1] = channel_y_scaled_value[2][2] = max;
      auto channel_y_scaled = std::shared_ptr<TensorIndicatorFunctionType>(new TensorIndicatorFunctionType(
          {{{{lower_left[0] + (upper_right[0] - lower_left[0])/2. - channel_width[1]/2.,
                lower_left[1],
                  lower_left[2] + (upper_right[2] - lower_left[2])/2.},
             {lower_left[0] + (upper_right[0] - lower_left[0])/2. + channel_width[1]/2.,
                upper_right[1],
                  upper_right[2]}},
            channel_y_scaled_value}}));
      auto channel_y_remover = Stuff::Functions::make_difference(one, channel_y, "channel_y_remover");
      auto spe10_wo_channel_y = Stuff::Functions::make_product(channel_y_remover, spe10, "spe10_wo_channel_y");
      auto spe10_w_scaled_channel_y = Stuff::Functions::make_sum(spe10_wo_channel_y,
                                                                 channel_y_scaled,
                                                                 "spe10_w_scaled_channel_y");
      this->register_component(Stuff::Functions::make_difference(spe10_w_scaled_channel_y, spe10, "component_y"),
                               new Pymor::ParameterFunctional("channel",
                                                              channel_width[0] > 0 ? 2 : 1,
                               "channel[" + std::string(channel_width[0] > 0 ? "1" : "0") + "]"));
    }
  } // Model2(...)

  virtual std::string type() const override final
  {
    return BaseType::BaseType::static_id() + ".spe10.model2";
  }
}; // class Model2

} // namespace Spe10
} // namespace Functions
} // namespace Pymor
} // namespace Dune

#endif // DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH
<commit_msg>[functions.spe10.model2] turn channel into a blockade<commit_after>// This file is part of the dune-Pymor project:
//   https://github.com/wwu-numerik/dune-Pymor/
// Copyright holders: Rene Milk, Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH
#define DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH

#include <dune/stuff/common/fvector.hh>
#include <dune/stuff/playground/functions/indicator.hh>
#include <dune/stuff/functions/spe10model2.hh>

#include "default.hh"

namespace Dune {
namespace Pymor {
namespace Functions {
namespace Spe10 {

// default, to allow for specialization
template< class E, class D, size_t d, class R, size_t r, size_t rC = 1 >
class Model2
  : public AffinelyDecomposableFunctionInterface< E, D, d, R, r, rC >
{
  Model2() { static_assert(AlwaysFalse< E >::value, "Not available for these dimensions!"); }
};

template< class E, class D, class R >
class Model2< E, D, 3, R, 3, 3 >
  : public AffinelyDecomposableDefault< E, D, 3, R, 3, 3 >
{
  static const size_t d = 3;
  static const size_t r = 3;
  static const size_t rC = 3;
  typedef AffinelyDecomposableDefault< E, D, d, R, r, rC > BaseType;
  typedef Model2< E, D, d, R, r, rC >                      ThisType;
  typedef Stuff::Functions::Spe10::Model2< E, D, d, R, r, rC > Spe10FunctionType;
public:
  using typename BaseType::DomainType;

  static const bool available = true;

  static std::string static_id()
  {
    return BaseType::BaseType::static_id() + ".spe10.model2";
  }

  static Stuff::Common::Configuration default_config(const std::string sub_name = "")
  {
    auto config = Spe10FunctionType::default_config();
    config["type"] = static_id();
    config["blockade_width"] = "0";
    config["blockade_value"] = "0";
    if (sub_name.empty())
      return config;
    else {
      Stuff::Common::Configuration tmp;
      tmp.add(config, sub_name);
      return tmp;
    }
  } // ... default_config(...)

  static std::unique_ptr< ThisType > create(const Stuff::Common::Configuration config = BaseType::default_config(),
                                            const std::string sub_name = BaseType::static_id())
  {
    // get correct config
    const Stuff::Common::Configuration cfg = config.has_sub(sub_name) ? config.sub(sub_name) : config;
    const Stuff::Common::Configuration default_cfg = default_config();
    // create
    return Stuff::Common::make_unique< ThisType >(
          cfg.get("filename",       default_cfg.get< std::string >("filename")),
          cfg.get("name",           default_cfg.get< std::string >("name")),
          cfg.get("lower_left",     default_cfg.get< DomainType >("lower_left")),
          cfg.get("upper_right",    default_cfg.get< DomainType >("upper_right")),
          cfg.get("blockade_width", default_cfg.get< D >("blockade_width")),
          cfg.get("blockade_value", default_cfg.get< R >("blockade_value")),
          cfg.get("anisotropic",    default_cfg.get< bool >("anisotropic")),
          cfg.get("min",            default_cfg.get< R >("min")),
          cfg.get("max",            default_cfg.get< R >("max")));
  } // ... create(...)

  Model2(const std::string& filename = default_config().get< std::string >("filename"),
         const std::string nm = default_config().get< std::string >("name"),
         const Stuff::Common::FieldVector< D, d >& lower_left
            = default_config().get< DomainType >("lower_left"),
         const Stuff::Common::FieldVector< D, d >& upper_right
            = default_config().get< DomainType >("upper_right"),
         const D& blockade_width = default_config.get< D >("blockade_width"),
         const R& blockade_value = default_config().get< R >("blockade_value"),
         const bool anisotropic = default_config().get< bool >("anisotropic"),
         const R& min = default_config().get< R >("min"),
         const R& max = default_config().get< R >("max"))
    : BaseType(nm)
  {
    typedef Stuff::Functions::Constant<E, D, 3, R, 1>     ScalarConstantFunctionType;
    typedef Stuff::Functions::Indicator<E, D, 3, R, 1>    ScalarIndicatorFunctionType;
    typedef Stuff::Functions::Indicator<E, D, 3, R, 3, 3> TensorIndicatorFunctionType;

    auto spe10 = std::make_shared<Spe10FunctionType>(filename, "spe10", lower_left, upper_right, anisotropic, min, max);

    if (blockade_width > 0) {
      auto one = std::make_shared<ScalarConstantFunctionType>(1, "one");
      auto blockade = std::shared_ptr<ScalarIndicatorFunctionType>(new ScalarIndicatorFunctionType(
          {{{{lower_left[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. - blockade_width/2.,
                  lower_left[2]},
             {upper_right[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. + blockade_width/2.,
                  upper_right[2]}},
            1.}}));
      typename TensorIndicatorFunctionType::RangeType blockade_scaled_value(0.);
      blockade_scaled_value[0][0] = blockade_scaled_value[1][1] = blockade_scaled_value[2][2] = blockade_value;
      auto blockade_scaled = std::shared_ptr<TensorIndicatorFunctionType>(new TensorIndicatorFunctionType(
          {{{{lower_left[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. - blockade_width/2.,
                  lower_left[2]},
             {upper_right[0],
                lower_left[1] + (upper_right[1] - lower_left[1])/2. + blockade_width/2.,
                  upper_right[2]}},
            blockade_scaled_value}}));
      auto blockade_remover = Stuff::Functions::make_difference(one, blockade, "blockade_remover");
      auto spe10_wo_blockade = Stuff::Functions::make_product(blockade_remover, spe10, "spe10_wo_blockade");
      auto spe10_w_scaled_blockade = Stuff::Functions::make_sum(spe10_wo_blockade,
                                                                 blockade_scaled,
                                                                 "spe10_w_scaled_blockade");
      this->register_affine_part(spe10_w_scaled_blockade);
      this->register_component(Stuff::Functions::make_difference(spe10, spe10_w_scaled_blockade, "component"),
                               new Pymor::ParameterFunctional("blockade", 1, "blockade[0]"));
    } else {
      this->register_affine_part(spe10);
    }
  } // Model2(...)

  virtual std::string type() const override final
  {
    return BaseType::BaseType::static_id() + ".spe10.model2";
  }
}; // class Model2

} // namespace Spe10
} // namespace Functions
} // namespace Pymor
} // namespace Dune

#endif // DUNE_PYMOR_FUNCTIONS_SPE10MODEL2_HH
<|endoftext|>


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

#ifndef _XLIB_H_
#include <prex.h>
#include <X11/Xlib.h>
#include <postx.h>
#endif
#ifndef _REF_HXX
#include <tools/ref.hxx>
#endif
#ifndef _RTL_TENCINFO_H
#include <rtl/tencinfo.h>
#endif
#ifndef _VCL_VCLENUM_HXX
#include <vclenum.hxx>
#endif
#ifndef _SV_SALLAYOUT_HXX
#include <sallayout.hxx>
#endif // _SV_SALLAYOUT_HXX

typedef unsigned short sal_MultiByte;

class ImplFontMetricData;
class ExtendedXlfd;

struct VerticalTextItem
{
    BOOL                mbFixed;
    XFontStruct*        mpXFontStruct;
    const sal_Unicode*  mpString;
    int                 mnLength;
    int                 mnTransX;
    int                 mnTransY;
    int                 mnFixedAdvance;
    int*                mpAdvanceAry;

    VerticalTextItem::VerticalTextItem( XFontStruct* pXFontStruct,
                        const sal_Unicode* pString,
                        int nLength,
                        int nTransX,
                        int nTransY,
                        int nFixedAdvance )
      : mbFixed( TRUE ),
        mpXFontStruct( pXFontStruct ),
        mpString( pString ),
        mnLength( nLength ),
        mnTransX( nTransX ),
        mnTransY( nTransY ),
        mnFixedAdvance( nFixedAdvance )
    {}

    VerticalTextItem::VerticalTextItem( XFontStruct* pXFontStruct,
                        const sal_Unicode* pString,
                        int nLength,
                        int nTransX,
                        int nTransY,
                        int* pAdvanceAry )
      : mbFixed( FALSE ),
        mpXFontStruct( pXFontStruct ),
        mpString( pString ),
        mnLength( nLength ),
        mnTransX( nTransX ),
        mnTransY( nTransY ),
        mpAdvanceAry( pAdvanceAry )
    {}

    VerticalTextItem::~VerticalTextItem()
    {
        if (!mbFixed)
        {
            delete( mpAdvanceAry );
        }
    }
};

class ExtendedFontStruct : public SvRefBase
{
    private:
        Display*            mpDisplay;
        unsigned short      mnPixelSize;
        sal_Size            mnDefaultWidth;
        sal_Bool            mbVertical;
        rtl_TextEncoding    mnCachedEncoding;
        rtl_TextEncoding    mnAsciiEncoding;

        ExtendedXlfd*       mpXlfd;
        XFontStruct**       mpXFontStruct;

        int                 LoadEncoding( rtl_TextEncoding nEncoding );
        FontPitch           GetSpacing( rtl_TextEncoding nEncoding );
        Bool                GetFontBoundingBox( XCharStruct *pCharStruct,
                                    int *pAscent, int *pDescent ) ;

        sal_Size            GetDefaultWidth();
        sal_Size            GetCharWidth8( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray,
                                    rtl_TextEncoding nEncoding );
        sal_Size            GetCharWidthUTF16( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray );
        sal_Size            GetCharWidth16( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray, ExtendedFontStruct *pFallback );
    public:
                            ExtendedFontStruct( Display* pDisplay,
                                    unsigned short nPixelSize, sal_Bool bVertical,
                                    ExtendedXlfd* pXlfd );
                            ~ExtendedFontStruct();
        Bool                Match( const ExtendedXlfd *pXlfd,
                                    int nPixelSize, sal_Bool bVertical ) const;
        XFontStruct*        GetFontStruct( rtl_TextEncoding nEncoding );
        XFontStruct*        GetFontStruct( sal_Unicode nChar,
                                    rtl_TextEncoding *pEncoding );
        Bool                ToImplFontMetricData( ImplFontMetricData *pMetric );
        rtl_TextEncoding    GetAsciiEncoding( int *pAsciiRange = NULL ) const;
        sal_Size            GetCharWidth( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray, ExtendedFontStruct *pFallback );
        ULONG               GetFontCodeRanges( sal_uInt32* pCodePairs ) const;
};

// Declaration and Implementation for ExtendedFontStructRef: Add RefCounting
// to ExtendedFontStruct (it's not possible to separate decl and impl into
// a separate source file: all ref member functions are inline
SV_DECL_IMPL_REF( ExtendedFontStruct );

class X11FontLayout : public GenericSalLayout
{
public:
                    X11FontLayout( ExtendedFontStruct& );
    virtual bool    LayoutText( ImplLayoutArgs& );
    virtual void    DrawText( SalGraphics& ) const;

    virtual bool    IsNotdefGlyph( long nGlyphIndex ) const;

private:
    ExtendedFontStruct& mrFont;
};

#endif /* EXTENDED_FONTSTRUCT_HXX */
<commit_msg>INTEGRATION: CWS vcl07 (1.10.4.1.26); FILE MERGED 2003/03/31 15:27:31 hdu 1.10.4.1.26.2: #107559# cache FontCodeRanges for LayoutText 2003/03/14 11:31:08 hdu 1.10.4.1.26.1: #98919# xfont needs own AdjustLayout method to account for mnOrientation==0<commit_after>/*************************************************************************
 *
 *  $RCSfile: xfont.hxx,v $
 *
 *  $Revision: 1.12 $
 *
 *  last change: $Author: vg $ $Date: 2003-04-11 17:32:08 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef EXTENDED_FONTSTRUCT_HXX
#define EXTENDED_FONTSTRUCT_HXX

#ifndef _XLIB_H_
#include <prex.h>
#include <X11/Xlib.h>
#include <postx.h>
#endif
#ifndef _REF_HXX
#include <tools/ref.hxx>
#endif
#ifndef _RTL_TENCINFO_H
#include <rtl/tencinfo.h>
#endif
#ifndef _VCL_VCLENUM_HXX
#include <vclenum.hxx>
#endif
#ifndef _SV_SALLAYOUT_HXX
#include <sallayout.hxx>
#endif // _SV_SALLAYOUT_HXX

typedef unsigned short sal_MultiByte;

class ImplFontMetricData;
class ExtendedXlfd;

struct VerticalTextItem
{
    BOOL                mbFixed;
    XFontStruct*        mpXFontStruct;
    const sal_Unicode*  mpString;
    int                 mnLength;
    int                 mnTransX;
    int                 mnTransY;
    int                 mnFixedAdvance;
    int*                mpAdvanceAry;

    VerticalTextItem::VerticalTextItem( XFontStruct* pXFontStruct,
                        const sal_Unicode* pString,
                        int nLength,
                        int nTransX,
                        int nTransY,
                        int nFixedAdvance )
      : mbFixed( TRUE ),
        mpXFontStruct( pXFontStruct ),
        mpString( pString ),
        mnLength( nLength ),
        mnTransX( nTransX ),
        mnTransY( nTransY ),
        mnFixedAdvance( nFixedAdvance )
    {}

    VerticalTextItem::VerticalTextItem( XFontStruct* pXFontStruct,
                        const sal_Unicode* pString,
                        int nLength,
                        int nTransX,
                        int nTransY,
                        int* pAdvanceAry )
      : mbFixed( FALSE ),
        mpXFontStruct( pXFontStruct ),
        mpString( pString ),
        mnLength( nLength ),
        mnTransX( nTransX ),
        mnTransY( nTransY ),
        mpAdvanceAry( pAdvanceAry )
    {}

    VerticalTextItem::~VerticalTextItem()
    {
        if (!mbFixed)
        {
            delete( mpAdvanceAry );
        }
    }
};

class ExtendedFontStruct : public SvRefBase
{
    private:
        Display*            mpDisplay;
        unsigned short      mnPixelSize;
        sal_Size            mnDefaultWidth;
        sal_Bool            mbVertical;
        rtl_TextEncoding    mnCachedEncoding;
        rtl_TextEncoding    mnAsciiEncoding;

        ExtendedXlfd*       mpXlfd;
        XFontStruct**       mpXFontStruct;

        // unicode range cache
        mutable sal_uInt32* mpRangeCodes;
        mutable int         mnRangeCount;

        int                 LoadEncoding( rtl_TextEncoding nEncoding );
        FontPitch           GetSpacing( rtl_TextEncoding nEncoding );
        Bool                GetFontBoundingBox( XCharStruct *pCharStruct,
                                    int *pAscent, int *pDescent ) ;

        sal_Size            GetDefaultWidth();
        sal_Size            GetCharWidth8( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray,
                                    rtl_TextEncoding nEncoding );
        sal_Size            GetCharWidthUTF16( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray );
        sal_Size            GetCharWidth16( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray, ExtendedFontStruct *pFallback );
    public:
                            ExtendedFontStruct( Display* pDisplay,
                                    unsigned short nPixelSize, sal_Bool bVertical,
                                    ExtendedXlfd* pXlfd );
                            ~ExtendedFontStruct();
        Bool                Match( const ExtendedXlfd *pXlfd,
                                    int nPixelSize, sal_Bool bVertical ) const;
        XFontStruct*        GetFontStruct( rtl_TextEncoding nEncoding );
        XFontStruct*        GetFontStruct( sal_Unicode nChar,
                                    rtl_TextEncoding *pEncoding );
        Bool                ToImplFontMetricData( ImplFontMetricData *pMetric );
        rtl_TextEncoding    GetAsciiEncoding( int *pAsciiRange = NULL ) const;
        sal_Size            GetCharWidth( sal_Unicode nFrom, sal_Unicode nTo,
                                    long *pWidthArray, ExtendedFontStruct *pFallback );
        int                 GetFontCodeRanges( sal_uInt32* pCodePairs ) const;
        bool                HasUnicodeChar( sal_Unicode ) const;
};

// Declaration and Implementation for ExtendedFontStructRef: Add RefCounting
// to ExtendedFontStruct (it's not possible to separate decl and impl into
// a separate source file: all ref member functions are inline
SV_DECL_IMPL_REF( ExtendedFontStruct );

class X11FontLayout : public GenericSalLayout
{
public:
                    X11FontLayout( ExtendedFontStruct& );
    virtual bool    LayoutText( ImplLayoutArgs& );
    virtual void    AdjustLayout( ImplLayoutArgs& );
    virtual void    DrawText( SalGraphics& ) const;

private:
    ExtendedFontStruct& mrFont;
};

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


<commit_before>#include <iostream>
#include "xson/json.hpp"
#include "db/metadata.hpp"
#include "db/engine.hpp"

using namespace std::string_literals;

db::engine::engine(const std::string file) : m_index{}, m_storage{}
{
    m_storage.open(file, std::ios_base::out | std::ios_base::in | std::ios_base::binary);

    if (!m_storage.is_open())
        throw std::invalid_argument("Cannot open file "s + file);

    rebuild_indexes();
}

void db::engine::rebuild_indexes(std::initializer_list<std::string> keys)
{
    for(auto& key : keys)
        m_index.add(key);

    m_storage.clear();
    m_storage.seekg(0, m_storage.beg);

    while(m_storage)
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage >> metadata >> document;
        if(m_storage && metadata.valid())
            m_index.insert(document, metadata.position);
    }
}

void db::engine::create(db::object& document)
{
    m_storage.clear();
    m_storage.seekp(0, m_storage.end);
    m_index.insert(document, m_storage.tellp());
    auto metadata = db::metadata{};
    m_storage << metadata << document << std::flush;
}

void db::engine::read(const db::object& selector, std::vector<db::object>& results)
{
    m_storage.clear();

    for(const auto position : m_index.range(selector))
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;
        if(document.match(selector))
            results.emplace_back(document);
    }
}

void db::engine::update(const db::object& selector, const db::object& changes)
{
    m_storage.clear();

    for(const auto position : m_index.range(selector))
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;

        if(document.match(selector))
        {
            m_storage.seekp(0, m_storage.end);

            auto new_document = changes;
            new_document = new_document + document;

            m_index.insert(new_document, m_storage.tellp());
            m_storage << metadata << new_document << std::flush;

            m_storage.seekp(position, m_storage.beg);
            m_storage << updated;
        }
    }
}

void db::engine::destroy(const db::object& selector)
{
    if(!m_index.primary_key(selector))
        throw std::invalid_argument("Expected primary key got "s + xson::json::stringify(selector, 0));

    m_storage.clear();

    auto position = m_index.position(selector);
    auto metadata = db::metadata{};
    auto document = db::object{};
    m_storage.seekg(position, m_storage.beg);
    m_storage >> metadata >> document;
    m_index.erase(document);
    m_storage.seekp(position, m_storage.beg);
    m_storage << deleted;
}

void db::engine::history(const db::object& selector, std::vector<db::object>& results)
{
    if(!m_index.primary_key(selector))
        throw std::invalid_argument("Expected primary key got "s + xson::json::stringify(selector, 0));

    m_storage.clear();

    auto position = m_index.position(selector);
    while(position >= 0)
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;
        results.emplace_back(document);
        position = metadata.previous;
        std::clog << position << std::endl;
    }
}
<commit_msg>Fixing<commit_after>#include <iostream>
#include "xson/json.hpp"
#include "db/metadata.hpp"
#include "db/engine.hpp"

using namespace std::string_literals;

db::engine::engine(const std::string file) : m_index{}, m_storage{}
{
    m_storage.open(file, std::ios_base::out | std::ios_base::in | std::ios_base::binary);

    if (!m_storage.is_open())
        throw std::invalid_argument("Cannot open file "s + file);

    rebuild_indexes();
}

void db::engine::rebuild_indexes(std::initializer_list<std::string> keys)
{
    for(auto& key : keys)
        m_index.add(key);

    m_storage.clear();
    m_storage.seekg(0, m_storage.beg);

    while(m_storage)
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage >> metadata >> document;
        if(m_storage && metadata.valid())
            m_index.insert(document, metadata.position);
    }
}

void db::engine::create(db::object& document)
{
    m_storage.clear();
    m_storage.seekp(0, m_storage.end);
    m_index.insert(document, m_storage.tellp());
    auto metadata = db::metadata{};
    m_storage << metadata << document << std::flush;
}

void db::engine::read(const db::object& selector, std::vector<db::object>& results)
{
    m_storage.clear();

    for(const auto position : m_index.range(selector))
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;
        if(document.match(selector))
            results.emplace_back(document);
    }
}

void db::engine::update(const db::object& selector, const db::object& changes)
{
    m_storage.clear();

    for(const auto position : m_index.range(selector))
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;

        if(document.match(selector))
        {
            m_storage.seekp(0, m_storage.end);

            auto new_document = changes;
            new_document = new_document + document;

            m_index.insert(new_document, m_storage.tellp());
            m_storage << metadata << new_document;

            m_storage.seekp(position, m_storage.beg);
            m_storage << updated << std::flush;
        }
    }
}

void db::engine::destroy(const db::object& selector)
{
    if(!m_index.primary_key(selector))
        throw std::invalid_argument("Expected primary key got "s + xson::json::stringify(selector, 0));

    m_storage.clear();

    auto position = m_index.position(selector);
    auto metadata = db::metadata{};
    auto document = db::object{};
    m_storage.seekg(position, m_storage.beg);
    m_storage >> metadata >> document;
    m_index.erase(document);
    m_storage.seekp(position, m_storage.beg);
    m_storage << deleted << std::flush;
}

void db::engine::history(const db::object& selector, std::vector<db::object>& results)
{
    if(!m_index.primary_key(selector))
        throw std::invalid_argument("Expected primary key got "s + xson::json::stringify(selector, 0));

    m_storage.clear();

    auto position = m_index.position(selector);
    while(position >= 0)
    {
        auto metadata = db::metadata{};
        auto document = db::object{};
        m_storage.seekg(position, m_storage.beg);
        m_storage >> metadata >> document;
        results.emplace_back(document);
        position = metadata.previous;
    }
}
<|endoftext|>