<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2008 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

//!    File   : Scripting.cpp
//!    Author : Jens Krueger
//!             SCI Institute
//!             University of Utah
//!    Date   : January 2009
//
//!    Copyright (C) 2008 SCI Institute

#include "Scripting.h"
#include <Controller/MasterController.h>
#include <Basics/SysTools.h>

#include <sstream>
#include <limits>

using namespace std;

ScriptableListElement::ScriptableListElement(Scriptable* source, const std::string& strCommand, const std::string& strParameters, const std::string& strDescription) :
      m_source(source),
      m_strCommand(strCommand),
      m_strDescription(strDescription)
{
  m_vParameters = SysTools::Tokenize(strParameters, false);

  m_iMaxParam = UINT32(m_vParameters.size());
  m_iMinParam = 0;

  bool bFoundOptional = false;
  for (UINT32 i = 0;i<m_iMaxParam;i++) {
    if (m_vParameters[i] == "...") {
      m_iMaxParam = numeric_limits<UINT32>::max();
    } else {
      if (m_vParameters[i][0] == '[' && m_vParameters[i][m_vParameters[i].size()-1] == ']') {
        bFoundOptional = true;
        m_vParameters[i] = string(m_vParameters[i].begin()+1, m_vParameters[i].end()-1);
      } else {
        if (!bFoundOptional)
          m_iMinParam++;
        // else // this else would be an syntax error case but lets just assume all parameters after the first optional parameter are also optional
      }
    }
  }
}

Scripting::Scripting(MasterController* pMasterController) :
  m_pMasterController(pMasterController),
  m_bEcho(false)
{
  RegisterCalls(this);
}

Scripting::~Scripting() {
  for (size_t i = 0;i<m_ScriptableList.size();i++) delete m_ScriptableList[i];
}

bool Scripting::RegisterCommand(Scriptable* source, const std::string& strCommand, const std::string& strParameters, const std::string& strDescription) {
  // commands may not contain whitespaces
  vector<string> strTest = SysTools::Tokenize(strCommand, false);
  if (strTest.size() != 1) return false;

  // commands must be unique
  for (size_t i = 0;i<m_ScriptableList.size();i++) {
    if (m_ScriptableList[i]->m_strCommand == strCommand) return false;
  }

  // ok, all seems fine: add the command to the list
  ScriptableListElement* elem = new ScriptableListElement(source, strCommand, strParameters, strDescription);
  m_ScriptableList.push_back(elem);
  return true;
}

bool Scripting::ParseLine(const string& strLine) {
  // tokenize string
  vector<string> vParameters = SysTools::Tokenize(strLine);
  if(vParameters.empty()) { return true; }

  string strMessage = "";
  bool bResult = ParseCommand(vParameters, strMessage);

  if (!bResult) {
    if (strMessage == "")
      m_pMasterController->DebugOut()->printf("Input \"%s\" not understood, try \"help\"!", strLine.c_str());
    else
      m_pMasterController->DebugOut()->printf(strMessage.c_str());
  } else
    if (m_bEcho) m_pMasterController->DebugOut()->printf("OK (%s)", strLine.c_str());

  return bResult;
}

bool Scripting::ParseCommand(const vector<string>& strTokenized, string& strMessage) {

  if (strTokenized.empty()) return false;
  string strCommand = strTokenized[0];
  vector<string> strParams(strTokenized.begin()+1, strTokenized.end());

  strMessage = "";
  for (size_t i = 0;i<m_ScriptableList.size();i++) {
    if (m_ScriptableList[i]->m_strCommand == strCommand) {
      if (strParams.size() >= m_ScriptableList[i]->m_iMinParam &&
          strParams.size() <= m_ScriptableList[i]->m_iMaxParam) {
        return m_ScriptableList[i]->m_source->Execute(strCommand, strParams, strMessage);
      } else {
         strMessage = "Parameter mismatch for command \""+strCommand+"\"";
         return false;
      }
    }
  }

  return false;
}

bool Scripting::ParseFile(const std::string& strFilename) {
  string line;
  ifstream fileData(strFilename.c_str());

  UINT32 iLine=0;
  if (fileData.is_open())
  {
    while (! fileData.eof() )
    {
      getline (fileData,line);
      iLine++;
      SysTools::RemoveLeadingWhitespace(line);
      if (line.size() == 0) continue;     // skip empty lines
      if (line[0] == '#') continue;       // skip comments

      if (!ParseLine(line)) {
        m_pMasterController->DebugOut()->Error("Scripting::ParseFile:","Error reading line %i in file %s (%s)", iLine, strFilename.c_str(), line.c_str());
        fileData.close();
        return false;
      }
    }
  } else {
    m_pMasterController->DebugOut()->Error("Scripting::ParseFile:","Error opening script file %s", strFilename.c_str());
    return false;
  }

  fileData.close();
  return true;
}

void Scripting::RegisterCalls(Scripting* pScriptEngine) {
  pScriptEngine->RegisterCommand(this, "help", "", "show all commands");
  pScriptEngine->RegisterCommand(this, "execute", "filename", "run the script saved as 'filename'");
  pScriptEngine->RegisterCommand(this, "echo", "on/off", "turn feedback on succesfull command execution on or off");
}

bool Scripting::Execute(const std::string& strCommand, const std::vector< std::string >& strParams, std::string& strMessage) {
  strMessage = "";
  if (strCommand == "echo") {
    m_bEcho = SysTools::ToLowerCase(strParams[0]) == "on";
    return true;
  } else
  if (strCommand == "execute") {
    return ParseFile(strParams[0]);
  } else
  if (strCommand == "help") {
    m_pMasterController->DebugOut()->printf("Command Listing:");
    for (size_t i = 0;i<m_ScriptableList.size();i++) {
      string strParams = "";
      for (size_t j = 0;j<m_ScriptableList[i]->m_iMinParam;j++) {
        strParams = strParams + m_ScriptableList[i]->m_vParameters[j];
        if (j != m_ScriptableList[i]->m_vParameters.size()-1) strParams = strParams + " ";
      }
      for (size_t j = m_ScriptableList[i]->m_iMinParam;j<m_ScriptableList[i]->m_iMaxParam;j++) {
        strParams = strParams + "["+m_ScriptableList[i]->m_vParameters[j]+"]";
        if (j != m_ScriptableList[i]->m_vParameters.size()-1) strParams = strParams + " ";
      }

      m_pMasterController->DebugOut()->printf("\"%s\" %s: %s", m_ScriptableList[i]->m_strCommand.c_str(), strParams.c_str(), m_ScriptableList[i]->m_strDescription.c_str());
    }
    return true;
  }

  return false;

}
<commit_msg>added date, time, and write calls to the scripting language<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2008 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

//!    File   : Scripting.cpp
//!    Author : Jens Krueger
//!             SCI Institute
//!             University of Utah
//!    Date   : January 2009
//
//!    Copyright (C) 2008 SCI Institute

#include "Scripting.h"
#include <Controller/MasterController.h>
#include <Basics/SysTools.h>
#include <QtCore/QTime>
#include <QtCore/QDate>

#include <sstream>
#include <limits>

using namespace std;

ScriptableListElement::ScriptableListElement(Scriptable* source, const std::string& strCommand, const std::string& strParameters, const std::string& strDescription) :
      m_source(source),
      m_strCommand(strCommand),
      m_strDescription(strDescription)
{
  m_vParameters = SysTools::Tokenize(strParameters, false);

  m_iMaxParam = UINT32(m_vParameters.size());
  m_iMinParam = 0;

  bool bFoundOptional = false;
  for (UINT32 i = 0;i<m_iMaxParam;i++) {
    if (m_vParameters[i] == "...") {
      m_iMaxParam = numeric_limits<UINT32>::max();
    } else {
      if (m_vParameters[i][0] == '[' && m_vParameters[i][m_vParameters[i].size()-1] == ']') {
        bFoundOptional = true;
        m_vParameters[i] = string(m_vParameters[i].begin()+1, m_vParameters[i].end()-1);
      } else {
        if (!bFoundOptional)
          m_iMinParam++;
        // else // this else would be an syntax error case but lets just assume all parameters after the first optional parameter are also optional
      }
    }
  }
}

Scripting::Scripting(MasterController* pMasterController) :
  m_pMasterController(pMasterController),
  m_bEcho(false)
{
  RegisterCalls(this);
}

Scripting::~Scripting() {
  for (size_t i = 0;i<m_ScriptableList.size();i++) delete m_ScriptableList[i];
}

bool Scripting::RegisterCommand(Scriptable* source, const std::string& strCommand, const std::string& strParameters, const std::string& strDescription) {
  // commands may not contain whitespaces
  vector<string> strTest = SysTools::Tokenize(strCommand, false);
  if (strTest.size() != 1) return false;

  // commands must be unique
  for (size_t i = 0;i<m_ScriptableList.size();i++) {
    if (m_ScriptableList[i]->m_strCommand == strCommand) return false;
  }

  // ok, all seems fine: add the command to the list
  ScriptableListElement* elem = new ScriptableListElement(source, strCommand, strParameters, strDescription);
  m_ScriptableList.push_back(elem);
  return true;
}

bool Scripting::ParseLine(const string& strLine) {
  // tokenize string
  vector<string> vParameters = SysTools::Tokenize(strLine);
  if(vParameters.empty()) { return true; }

  string strMessage = "";
  bool bResult = ParseCommand(vParameters, strMessage);

  if (!bResult) {
    if (strMessage == "")
      m_pMasterController->DebugOut()->printf("Input \"%s\" not understood, try \"help\"!", strLine.c_str());
    else
      m_pMasterController->DebugOut()->printf(strMessage.c_str());
  } else
    if (m_bEcho) m_pMasterController->DebugOut()->printf("OK (%s)", strLine.c_str());

  return bResult;
}

bool Scripting::ParseCommand(const vector<string>& strTokenized, string& strMessage) {

  if (strTokenized.empty()) return false;
  string strCommand = strTokenized[0];
  vector<string> strParams(strTokenized.begin()+1, strTokenized.end());

  strMessage = "";
  for (size_t i = 0;i<m_ScriptableList.size();i++) {
    if (m_ScriptableList[i]->m_strCommand == strCommand) {
      if (strParams.size() >= m_ScriptableList[i]->m_iMinParam &&
          strParams.size() <= m_ScriptableList[i]->m_iMaxParam) {
        return m_ScriptableList[i]->m_source->Execute(strCommand, strParams, strMessage);
      } else {
         strMessage = "Parameter mismatch for command \""+strCommand+"\"";
         return false;
      }
    }
  }

  return false;
}

bool Scripting::ParseFile(const std::string& strFilename) {
  string line;
  ifstream fileData(strFilename.c_str());

  UINT32 iLine=0;
  if (fileData.is_open())
  {
    while (! fileData.eof() )
    {
      getline (fileData,line);
      iLine++;
      SysTools::RemoveLeadingWhitespace(line);
      if (line.size() == 0) continue;     // skip empty lines
      if (line[0] == '#') continue;       // skip comments

      if (!ParseLine(line)) {
        m_pMasterController->DebugOut()->Error("Scripting::ParseFile:","Error reading line %i in file %s (%s)", iLine, strFilename.c_str(), line.c_str());
        fileData.close();
        return false;
      }
    }
  } else {
    m_pMasterController->DebugOut()->Error("Scripting::ParseFile:","Error opening script file %s", strFilename.c_str());
    return false;
  }

  fileData.close();
  return true;
}

void Scripting::RegisterCalls(Scripting* pScriptEngine) {
  pScriptEngine->RegisterCommand(this, "help", "", "show all commands");
  pScriptEngine->RegisterCommand(this, "execute", "filename", "run the script saved as 'filename'");
  pScriptEngine->RegisterCommand(this, "echo", "on/off", "turn feedback on succesfull command execution on or off");
  pScriptEngine->RegisterCommand(this, "time", "","print out the current time");
  pScriptEngine->RegisterCommand(this, "date", "","print out the current date");
  pScriptEngine->RegisterCommand(this, "write", "test","print out 'text'");
}

bool Scripting::Execute(const std::string& strCommand, const std::vector< std::string >& strParams, std::string& strMessage) {
  strMessage = "";
  if (strCommand == "echo") {
    m_bEcho = SysTools::ToLowerCase(strParams[0]) == "on";
    return true;
  } else
  if (strCommand == "execute") {
    return ParseFile(strParams[0]);
  } else
  if (strCommand == "help") {
    m_pMasterController->DebugOut()->printf("Command Listing:");
    for (size_t i = 0;i<m_ScriptableList.size();i++) {
      string strParams = "";
      for (size_t j = 0;j<m_ScriptableList[i]->m_iMinParam;j++) {
        strParams = strParams + m_ScriptableList[i]->m_vParameters[j];
        if (j != m_ScriptableList[i]->m_vParameters.size()-1) strParams = strParams + " ";
      }
      for (size_t j = m_ScriptableList[i]->m_iMinParam;j<m_ScriptableList[i]->m_iMaxParam;j++) {
        strParams = strParams + "["+m_ScriptableList[i]->m_vParameters[j]+"]";
        if (j != m_ScriptableList[i]->m_vParameters.size()-1) strParams = strParams + " ";
      }

      m_pMasterController->DebugOut()->printf("\"%s\" %s: %s", m_ScriptableList[i]->m_strCommand.c_str(), strParams.c_str(), m_ScriptableList[i]->m_strDescription.c_str());
    }
    return true;
  } else
  if (strCommand == "time") {
    string strTime(QTime::currentTime().toString().toAscii());
    m_pMasterController->DebugOut()->printf(strTime.c_str());
    return true;
  } else
  if (strCommand == "date") {
    string strDate(QDate::currentDate().toString().toAscii());
    m_pMasterController->DebugOut()->printf(strDate.c_str());
    return true;
  } else
  if (strCommand == "write") {
    m_pMasterController->DebugOut()->printf(strParams[0].c_str());
    return true;
  }

  return false;

}
<|endoftext|>


<commit_before>#include "types.h"
#include "kernel.hh"
#include "spinlock.h"
#include "condvar.h"
#include "fs.h"
#include "file.hh"
#include "stat.h"
#include "net.hh"

struct devsw __mpalign__ devsw[NDEV];

file*
file::alloc(void)
{
  return new file();
}

file::file(void)
  : rcu_freed("file"),
    type(file::FD_NONE), readable(0), writable(0),
    socket(0), pipe(nullptr), ip(nullptr), off(0)
{
}

void
file::onzero(void) const
{
  if(type == file::FD_PIPE)
    pipeclose(pipe, writable);
  else if(type == file::FD_INODE)
    iput(ip);
  else if(type == file::FD_SOCKET)
    netclose(socket);
  else if(type != file::FD_NONE)
    panic("file::close bad type");
  gc_delayed((file*)this);
}

void
file::do_gc(void)
{
  delete this;
}

file*
file::dup(void)
{
  inc();
  return this;
}

int
file::stat(struct stat *st)
{
  if(type == file::FD_INODE){
    ilock(ip, 0);
    if(ip->type == 0)
      panic("filestat");
    stati(ip, st);
    iunlock(ip);
    return 0;
  }
  return -1;
}

int
file::read(char *addr, int n)
{
  int r;

  if(readable == 0)
    return -1;
  if(type == file::FD_PIPE)
    return piperead(pipe, addr, n);
  if(type == file::FD_INODE){
    ilock(ip, 0);
    if(ip->type == 0)
      panic("fileread");
    if((r = readi(ip, addr, off, n)) > 0)
      off += r;
    iunlock(ip);
    return r;
  }
  if(type == file::FD_SOCKET)
    return netread(socket, addr, n);
  panic("fileread");
}

ssize_t
file::pread(char *addr, size_t n, off_t off)
{
  if(type == file::FD_INODE){
    int r;
    ilock(ip, 0);
    if(ip->type == 0)
      panic("file::pread");
    r = readi(ip, addr, off, n);
    iunlock(ip);
    return r;
  }
  return -1;
}

int
file::write(const char *addr, int n)
{
  int r;

  if(writable == 0)
    return -1;
  if(type == file::FD_PIPE)
    return pipewrite(pipe, addr, n);
  if(type == file::FD_INODE){
    ilock(ip, 1);
    if(ip->type == 0 || ip->type == T_DIR)
      panic("filewrite but 0 or T_DIR");
    if((r = writei(ip, addr, off, n)) > 0)
      off += r;
    iunlock(ip);
    return r;
  }
  if(type == file::FD_SOCKET)
    return netwrite(socket, addr, n);
  panic("filewrite");
}
<commit_msg>Remove ilock from file::pread<commit_after>#include "types.h"
#include "kernel.hh"
#include "spinlock.h"
#include "condvar.h"
#include "fs.h"
#include "file.hh"
#include "stat.h"
#include "net.hh"

struct devsw __mpalign__ devsw[NDEV];

file*
file::alloc(void)
{
  return new file();
}

file::file(void)
  : rcu_freed("file"),
    type(file::FD_NONE), readable(0), writable(0),
    socket(0), pipe(nullptr), ip(nullptr), off(0)
{
}

void
file::onzero(void) const
{
  if(type == file::FD_PIPE)
    pipeclose(pipe, writable);
  else if(type == file::FD_INODE)
    iput(ip);
  else if(type == file::FD_SOCKET)
    netclose(socket);
  else if(type != file::FD_NONE)
    panic("file::close bad type");
  gc_delayed((file*)this);
}

void
file::do_gc(void)
{
  delete this;
}

file*
file::dup(void)
{
  inc();
  return this;
}

int
file::stat(struct stat *st)
{
  if(type == file::FD_INODE){
    ilock(ip, 0);
    if(ip->type == 0)
      panic("filestat");
    stati(ip, st);
    iunlock(ip);
    return 0;
  }
  return -1;
}

int
file::read(char *addr, int n)
{
  int r;

  if(readable == 0)
    return -1;
  if(type == file::FD_PIPE)
    return piperead(pipe, addr, n);
  if(type == file::FD_INODE){
    ilock(ip, 0);
    if(ip->type == 0)
      panic("fileread");
    if((r = readi(ip, addr, off, n)) > 0)
      off += r;
    iunlock(ip);
    return r;
  }
  if(type == file::FD_SOCKET)
    return netread(socket, addr, n);
  panic("fileread");
}

ssize_t
file::pread(char *addr, size_t n, off_t off)
{
  if(type == file::FD_INODE){
    int r;
    if(ip->type == 0)
      panic("file::pread");
    r = readi(ip, addr, off, n);
    return r;
  }
  return -1;
}

int
file::write(const char *addr, int n)
{
  int r;

  if(writable == 0)
    return -1;
  if(type == file::FD_PIPE)
    return pipewrite(pipe, addr, n);
  if(type == file::FD_INODE){
    ilock(ip, 1);
    if(ip->type == 0 || ip->type == T_DIR)
      panic("filewrite but 0 or T_DIR");
    if((r = writei(ip, addr, off, n)) > 0)
      off += r;
    iunlock(ip);
    return r;
  }
  if(type == file::FD_SOCKET)
    return netwrite(socket, addr, n);
  panic("filewrite");
}
<|endoftext|>


<commit_before>#include "SettingsUI.h"

#include <iostream>

/* DLGTEMPLATEEX Structure */
#include <pshpack1.h>
typedef struct DLGTEMPLATEEX
{
    WORD dlgVer;
    WORD signature;
    DWORD helpID;
    DWORD exStyle;
    DWORD style;
    WORD cDlgItems;
    short x;
    short y;
    short cx;
    short cy;
} DLGTEMPLATEEX, *LPDLGTEMPLATEEX;
#include <poppack.h>

#include "../3RVX/3RVX.h"
#include "../3RVX/CommCtl.h"
#include "../3RVX/LanguageTranslator.h"
#include "../3RVX/Logger.h"
#include "../3RVX/Settings.h"
#include "Tabs/General.h"
#include "Tabs/Display.h"
#include "Tabs/OSD.h"
#include "Tabs/Hotkeys.h"
#include "Tabs/About.h"
#include "UITranslator.h"
#include "Updater/Updater.h"
#include "Updater/UpdaterWindow.h"

/* Needed to determine whether the Apply button is enabled/disabled */
static const int IDD_APPLYNOW = 0x3021;

const wchar_t *MUTEX_NAME = L"Local\\3RVXSettings";
HANDLE mutex;
HWND tabWnd = NULL;
bool relaunch = false;

int APIENTRY wWinMain(
        _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPTSTR lpCmdLine,
        _In_ int nCmdShow) {

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

    Logger::Start();
    CLOG(L"Starting SettingsUI...");

    bool alreadyRunning = false;
    mutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex) {
            ReleaseMutex(mutex);
            CloseHandle(mutex);
        }
        alreadyRunning = true;
    }

    /* Inspect command line parameters to determine whether this settings
     * instance is being launched as an update checker. */
    std::wstring cmdLine(lpCmdLine);
    if (cmdLine.find(L"-update") != std::wstring::npos) {
        if (alreadyRunning) {
            return EXIT_SUCCESS;
        } else {
            /* If this is the only settings instance running, we release the
             * mutex so that the user can launch the settings app. If this
             * happens, the updater is closed to prevent settings file race
             * conditions. */
            ReleaseMutex(mutex);
            CloseHandle(mutex);
        }

        if (Updater::NewerVersionAvailable()) {
            Settings::Instance()->Load();
            CLOG(L"An update is available. Showing update icon.");
            UpdaterWindow uw;
            PostMessage(
                uw.Handle(),
                _3RVX::WM_3RVX_SETTINGSCTRL,
                _3RVX::MSG_UPDATEICON,
                NULL);
            uw.DoModal();
        } else {
#if defined(ENABLE_3RVX_LOG) && (defined(ENABLE_3RVX_LOGTOFILE) == FALSE)
            CLOG(L"No update available. Press [enter] to terminate");
            std::cin.get();
#endif
        }

        /* Process was used for updates; time to quit. */
        return EXIT_SUCCESS;
    }

    if (alreadyRunning) {
        HWND settingsWnd = _3RVX::MasterSettingsHwnd();
        CLOG(L"A settings instance is already running. Moving window [%d] "
            L"to the foreground.", (int) settingsWnd);
        SetForegroundWindow(settingsWnd);
        SendMessage(
            settingsWnd,
            _3RVX::WM_3RVX_SETTINGSCTRL,
            _3RVX::MSG_ACTIVATE,
            NULL);

#if defined(ENABLE_3RVX_LOG) && (defined(ENABLE_3RVX_LOGTOFILE) == FALSE)
        CLOG(L"Press [enter] to terminate");
        std::cin.get();
#endif

        return EXIT_SUCCESS;
    }

    HWND updater = _3RVX::UpdaterHwnd();
    if (updater != 0) {
        CLOG(L"Telling updater to close");
        SendMessage(updater, WM_CLOSE, 0, 0);
    }

    SettingsUI mainWnd(hInstance);
    INT_PTR result;
    do {
        result = mainWnd.LaunchPropertySheet();
        CLOG(L"Relaunch: %s", relaunch ? L"TRUE" : L"FALSE");
    } while (relaunch == true);

    return result;
}

SettingsUI::SettingsUI(HINSTANCE hInstance) :
Window(
        Window::Builder(_3RVX::CLASS_3RVX_SETTINGS)
        .Title(_3RVX::CLASS_3RVX_SETTINGS)
        .InstanceHandle(hInstance)
        .Icon(LoadIcon(NULL, MAKEINTRESOURCE(IDI_SETTINGS)))
        .Build()) {

    Settings::Instance()->Load();

    _tabs.push_back((_general = new General));
    _tabs.push_back((_display = new Display));
    _tabs.push_back((_osd = new OSD));
    _tabs.push_back((_hotkeys = new Hotkeys));
    _tabs.push_back((_about = new About));
}

INT_PTR SettingsUI::LaunchPropertySheet() {
    HPROPSHEETPAGE *pages = new HPROPSHEETPAGE[_tabs.size()];
    for (size_t i = 0; i < _tabs.size(); ++i) {
        pages[i] = _tabs[i]->PageHandle();
    }

    PROPSHEETHEADER psh = { 0 };
    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_USEICONID | PSH_USECALLBACK;
    psh.hwndParent = Window::Handle();
    psh.hInstance = Window::InstanceHandle();
    psh.pszIcon = MAKEINTRESOURCE(IDI_SETTINGS);
    psh.pszCaption = L"3RVX Settings";
    psh.nStartPage = 0;
    psh.nPages = _tabs.size();
    psh.phpage = pages;
    psh.pfnCallback = PropSheetProc;

    tabWnd = NULL;

    /* Position the window if this is the first launch */
    if (relaunch == false) {
        POINT pt = { 0 };
        GetCursorPos(&pt);
        MoveWindow(Window::Handle(),
            pt.x - XOFFSET, pt.y - YOFFSET, 0, 0, TRUE);
    }

    relaunch = false;

    CLOG(L"Launching modal property sheet.");
    return PropertySheet(&psh);
}

LRESULT SettingsUI::WndProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

    if (message == WM_DESTROY) {
        PostQuitMessage(0);
    } else if (message == _3RVX::WM_3RVX_SETTINGSCTRL) {
        switch (wParam) {
        case _3RVX::MSG_ACTIVATE:
            CLOG(L"Received request to activate window from external program");
            SetActiveWindow(tabWnd);
            break;

        case _3RVX::MSG_MUSTRESTART:
            relaunch = true;
            break;
        }
    }

    return Window::WndProc(hWnd, message, wParam, lParam);
}

int CALLBACK PropSheetProc(HWND hWnd, UINT msg, LPARAM lParam) {
    switch (msg) {
    case PSCB_PRECREATE:
        /* Disable the help button: */
        if (((LPDLGTEMPLATEEX) lParam)->signature == 0xFFFF) {
            ((LPDLGTEMPLATEEX) lParam)->style &= ~DS_CONTEXTHELP;
        } else {
            ((LPDLGTEMPLATE) lParam)->style &= ~DS_CONTEXTHELP;
        }

        /* Show window in the taskbar: */
        ((LPDLGTEMPLATE) lParam)->dwExtendedStyle |= WS_EX_APPWINDOW;
        break;

    case PSCB_INITIALIZED:
        UITranslator::TranslateWindowText(hWnd);

        if (tabWnd == NULL) {
            tabWnd = hWnd;
        }

        /* These values are hard-coded in case the user has a non-english GUI
           but wants to change the program language */
        UITranslator::TranslateControlText(
            hWnd, IDOK, std::wstring(L"OK"));
        UITranslator::TranslateControlText(
            hWnd, IDCANCEL, std::wstring(L"Cancel"));
        UITranslator::TranslateControlText(
            hWnd, IDD_APPLYNOW, std::wstring(L"Apply"));
        break;

    case PSCB_BUTTONPRESSED:
        if (lParam == PSBTN_OK || lParam == PSBTN_APPLYNOW) {
            HWND hApply = GetDlgItem(hWnd, IDD_APPLYNOW);
            if (IsWindowEnabled(hApply)) {
                /* Save settings*/
                CLOG(L"Saving settings...");
//                for (SettingsTab *tab : _tabs) {
//                    tab->SaveSettings();
//                }
                Settings::Instance()->Save();

                CLOG(L"Notifying 3RVX process of settings change");
                _3RVX::Message(_3RVX::MSG_LOAD, NULL, true);

                if (lParam == PSBTN_APPLYNOW && relaunch == true) {
                    /* Language was changed */
                    SendMessage(tabWnd, WM_CLOSE, NULL, NULL);
                } else {
                    relaunch = false;
                }
            }
        }
        break;
    }

    return TRUE;
}<commit_msg>Fix mutex close/release bug<commit_after>#include "SettingsUI.h"

#include <iostream>

/* DLGTEMPLATEEX Structure */
#include <pshpack1.h>
typedef struct DLGTEMPLATEEX
{
    WORD dlgVer;
    WORD signature;
    DWORD helpID;
    DWORD exStyle;
    DWORD style;
    WORD cDlgItems;
    short x;
    short y;
    short cx;
    short cy;
} DLGTEMPLATEEX, *LPDLGTEMPLATEEX;
#include <poppack.h>

#include "../3RVX/3RVX.h"
#include "../3RVX/CommCtl.h"
#include "../3RVX/LanguageTranslator.h"
#include "../3RVX/Logger.h"
#include "../3RVX/Settings.h"
#include "Tabs/General.h"
#include "Tabs/Display.h"
#include "Tabs/OSD.h"
#include "Tabs/Hotkeys.h"
#include "Tabs/About.h"
#include "UITranslator.h"
#include "Updater/Updater.h"
#include "Updater/UpdaterWindow.h"

/* Needed to determine whether the Apply button is enabled/disabled */
static const int IDD_APPLYNOW = 0x3021;

const wchar_t *MUTEX_NAME = L"Local\\3RVXSettings";
HANDLE mutex;
HWND tabWnd = NULL;
bool relaunch = false;

int APIENTRY wWinMain(
        _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPTSTR lpCmdLine,
        _In_ int nCmdShow) {

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

    Logger::Start();
    CLOG(L"Starting SettingsUI...");

    bool alreadyRunning = false;
    mutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex) {
            ReleaseMutex(mutex);
            CloseHandle(mutex);
        }
        alreadyRunning = true;
    }

    /* Inspect command line parameters to determine whether this settings
     * instance is being launched as an update checker. */
    std::wstring cmdLine(lpCmdLine);
    if (cmdLine.find(L"-update") != std::wstring::npos) {
        if (alreadyRunning) {
            return EXIT_SUCCESS;
        } else {
            /* If this is the only settings instance running, we release the
             * mutex so that the user can launch the settings app. If this
             * happens, the updater is closed to prevent settings file race
             * conditions. */
            if (mutex) {
                ReleaseMutex(mutex);
                CloseHandle(mutex);
            }
        }

        if (Updater::NewerVersionAvailable()) {
            Settings::Instance()->Load();
            CLOG(L"An update is available. Showing update icon.");
            UpdaterWindow uw;
            PostMessage(
                uw.Handle(),
                _3RVX::WM_3RVX_SETTINGSCTRL,
                _3RVX::MSG_UPDATEICON,
                NULL);
            uw.DoModal();
        } else {
#if defined(ENABLE_3RVX_LOG) && (defined(ENABLE_3RVX_LOGTOFILE) == FALSE)
            CLOG(L"No update available. Press [enter] to terminate");
            std::cin.get();
#endif
        }

        /* Process was used for updates; time to quit. */
        return EXIT_SUCCESS;
    }

    if (alreadyRunning) {
        HWND settingsWnd = _3RVX::MasterSettingsHwnd();
        CLOG(L"A settings instance is already running. Moving window [%d] "
            L"to the foreground.", (int) settingsWnd);
        SetForegroundWindow(settingsWnd);
        SendMessage(
            settingsWnd,
            _3RVX::WM_3RVX_SETTINGSCTRL,
            _3RVX::MSG_ACTIVATE,
            NULL);

#if defined(ENABLE_3RVX_LOG) && (defined(ENABLE_3RVX_LOGTOFILE) == FALSE)
        CLOG(L"Press [enter] to terminate");
        std::cin.get();
#endif

        return EXIT_SUCCESS;
    }

    HWND updater = _3RVX::UpdaterHwnd();
    if (updater != 0) {
        CLOG(L"Telling updater to close");
        SendMessage(updater, WM_CLOSE, 0, 0);
    }

    SettingsUI mainWnd(hInstance);
    INT_PTR result;
    do {
        result = mainWnd.LaunchPropertySheet();
        CLOG(L"Relaunch: %s", relaunch ? L"TRUE" : L"FALSE");
    } while (relaunch == true);

    return result;
}

SettingsUI::SettingsUI(HINSTANCE hInstance) :
Window(
        Window::Builder(_3RVX::CLASS_3RVX_SETTINGS)
        .Title(_3RVX::CLASS_3RVX_SETTINGS)
        .InstanceHandle(hInstance)
        .Icon(LoadIcon(NULL, MAKEINTRESOURCE(IDI_SETTINGS)))
        .Build()) {

    Settings::Instance()->Load();

    _tabs.push_back((_general = new General));
    _tabs.push_back((_display = new Display));
    _tabs.push_back((_osd = new OSD));
    _tabs.push_back((_hotkeys = new Hotkeys));
    _tabs.push_back((_about = new About));
}

INT_PTR SettingsUI::LaunchPropertySheet() {
    HPROPSHEETPAGE *pages = new HPROPSHEETPAGE[_tabs.size()];
    for (size_t i = 0; i < _tabs.size(); ++i) {
        pages[i] = _tabs[i]->PageHandle();
    }

    PROPSHEETHEADER psh = { 0 };
    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_USEICONID | PSH_USECALLBACK;
    psh.hwndParent = Window::Handle();
    psh.hInstance = Window::InstanceHandle();
    psh.pszIcon = MAKEINTRESOURCE(IDI_SETTINGS);
    psh.pszCaption = L"3RVX Settings";
    psh.nStartPage = 0;
    psh.nPages = _tabs.size();
    psh.phpage = pages;
    psh.pfnCallback = PropSheetProc;

    tabWnd = NULL;

    /* Position the window if this is the first launch */
    if (relaunch == false) {
        POINT pt = { 0 };
        GetCursorPos(&pt);
        MoveWindow(Window::Handle(),
            pt.x - XOFFSET, pt.y - YOFFSET, 0, 0, TRUE);
    }

    relaunch = false;

    CLOG(L"Launching modal property sheet.");
    return PropertySheet(&psh);
}

LRESULT SettingsUI::WndProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

    if (message == WM_DESTROY) {
        PostQuitMessage(0);
    } else if (message == _3RVX::WM_3RVX_SETTINGSCTRL) {
        switch (wParam) {
        case _3RVX::MSG_ACTIVATE:
            CLOG(L"Received request to activate window from external program");
            SetActiveWindow(tabWnd);
            break;

        case _3RVX::MSG_MUSTRESTART:
            relaunch = true;
            break;
        }
    }

    return Window::WndProc(hWnd, message, wParam, lParam);
}

int CALLBACK PropSheetProc(HWND hWnd, UINT msg, LPARAM lParam) {
    switch (msg) {
    case PSCB_PRECREATE:
        /* Disable the help button: */
        if (((LPDLGTEMPLATEEX) lParam)->signature == 0xFFFF) {
            ((LPDLGTEMPLATEEX) lParam)->style &= ~DS_CONTEXTHELP;
        } else {
            ((LPDLGTEMPLATE) lParam)->style &= ~DS_CONTEXTHELP;
        }

        /* Show window in the taskbar: */
        ((LPDLGTEMPLATE) lParam)->dwExtendedStyle |= WS_EX_APPWINDOW;
        break;

    case PSCB_INITIALIZED:
        UITranslator::TranslateWindowText(hWnd);

        if (tabWnd == NULL) {
            tabWnd = hWnd;
        }

        /* These values are hard-coded in case the user has a non-english GUI
           but wants to change the program language */
        UITranslator::TranslateControlText(
            hWnd, IDOK, std::wstring(L"OK"));
        UITranslator::TranslateControlText(
            hWnd, IDCANCEL, std::wstring(L"Cancel"));
        UITranslator::TranslateControlText(
            hWnd, IDD_APPLYNOW, std::wstring(L"Apply"));
        break;

    case PSCB_BUTTONPRESSED:
        if (lParam == PSBTN_OK || lParam == PSBTN_APPLYNOW) {
            HWND hApply = GetDlgItem(hWnd, IDD_APPLYNOW);
            if (IsWindowEnabled(hApply)) {
                /* Save settings*/
                CLOG(L"Saving settings...");
//                for (SettingsTab *tab : _tabs) {
//                    tab->SaveSettings();
//                }
                Settings::Instance()->Save();

                CLOG(L"Notifying 3RVX process of settings change");
                _3RVX::Message(_3RVX::MSG_LOAD, NULL, true);

                if (lParam == PSBTN_APPLYNOW && relaunch == true) {
                    /* Language was changed */
                    SendMessage(tabWnd, WM_CLOSE, NULL, NULL);
                } else {
                    relaunch = false;
                }
            }
        }
        break;
    }

    return TRUE;
}<|endoftext|>


<commit_before>#include <string.h>
#include <assert.h>
#include <boost/lexical_cast.hpp>
#include "Dmac_Channel.h"
#include "DMAC.h"
#include "RegisterStateFile.h"

#define STATE_PREFIX            ("dmac/channel_")
#define STATE_SUFFIX            (".xml")
#define STATE_REGS_CHCR         ("CHCR")
#define STATE_REGS_MADR         ("MADR")
#define STATE_REGS_QWC          ("QWC")
#define STATE_REGS_TADR         ("TADR")
#define STATE_REGS_SCCTRL       ("SCCTRL")
#define STATE_REGS_ASR0         ("ASR0")
#define STATE_REGS_ASR1         ("ASR1")

using namespace std;
using namespace Dmac;
using namespace boost;

CChannel::CChannel(CDMAC& dmac, unsigned int nNumber, const DmaReceiveHandler& pReceive) :
m_dmac(dmac),
m_nNumber(nNumber),
m_pReceive(pReceive)
{

}

CChannel::~CChannel()
{

}

void CChannel::Reset()
{
	memset(&m_CHCR, 0, sizeof(CHCR));
	m_nMADR		= 0;
	m_nQWC		= 0;
	m_nTADR		= 0;
	m_nSCCTRL	= 0;
}

void CChannel::SaveState(Framework::CZipArchiveWriter& archive)
{
    string path = STATE_PREFIX + lexical_cast<string>(m_nNumber) + STATE_SUFFIX;
    CRegisterStateFile* registerFile = new CRegisterStateFile(path.c_str());
    registerFile->SetRegister32(STATE_REGS_CHCR,    m_CHCR);
    registerFile->SetRegister32(STATE_REGS_MADR,    m_nMADR);
    registerFile->SetRegister32(STATE_REGS_QWC,     m_nQWC);
    registerFile->SetRegister32(STATE_REGS_TADR,    m_nTADR);
    registerFile->SetRegister32(STATE_REGS_SCCTRL,  m_nSCCTRL);
    registerFile->SetRegister32(STATE_REGS_ASR0,    m_nASR[0]);
    registerFile->SetRegister32(STATE_REGS_ASR1,    m_nASR[1]);
    archive.InsertFile(registerFile);
}

void CChannel::LoadState(Framework::CZipArchiveReader& archive)
{
    string path = STATE_PREFIX + lexical_cast<string>(m_nNumber) + STATE_SUFFIX;
    CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
    m_CHCR      <<= registerFile.GetRegister32(STATE_REGS_CHCR);
    m_nMADR       = registerFile.GetRegister32(STATE_REGS_MADR);
    m_nQWC        = registerFile.GetRegister32(STATE_REGS_QWC);
    m_nTADR       = registerFile.GetRegister32(STATE_REGS_TADR);
    m_nSCCTRL     = registerFile.GetRegister32(STATE_REGS_SCCTRL);
    m_nASR[0]     = registerFile.GetRegister32(STATE_REGS_ASR0);
    m_nASR[1]     = registerFile.GetRegister32(STATE_REGS_ASR1);
}

uint32 CChannel::ReadCHCR()
{
	return *(uint32*)&m_CHCR;
}

void CChannel::WriteCHCR(uint32 nValue)
{
	bool nSuspend = false;

	//We need to check if the purpose of this write is to suspend the current transfer
    if(m_dmac.m_D_ENABLE & CDMAC::ENABLE_CPND)
	{
        nSuspend = (m_CHCR.nSTR != 0) && ((nValue & CDMAC::CHCR_STR) == 0);
	}

	if(m_CHCR.nSTR == 1)
	{
		m_CHCR.nSTR = ~m_CHCR.nSTR;
        m_CHCR.nSTR = ((nValue & CDMAC::CHCR_STR) != 0) ? 1 : 0;
	}
	else
	{
		m_CHCR = *(CHCR*)&nValue;
	}

    if(m_CHCR.nSTR != 0)
    {
        m_nSCCTRL |= SCCTRL_INITXFER;
        Execute();
    }
}

void CChannel::Execute()
{
	if(m_CHCR.nSTR != 0)
	{
        if(m_dmac.m_D_ENABLE)
        {
            if(m_nNumber != 4)
            {
                throw runtime_error("Need to check that case.");
            }
            return;
        }
        switch(m_CHCR.nMOD)
		{
		case 0x00:
			ExecuteNormal();
			break;
		case 0x01:
			ExecuteSourceChain();
			break;
		default:
			assert(0);
			break;
		}
	}
}

void CChannel::ExecuteNormal()
{
	uint32 nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

	m_nMADR	+= nRecv * 0x10;
	m_nQWC	-= nRecv;

	if(m_nQWC == 0)
	{
		ClearSTR();
	}
}

void CChannel::ExecuteSourceChain()
{
	uint64 nTag;
	uint32 nRecv;
	uint8 nID;

	//Execute current
	if(m_nQWC != 0)
	{
		nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

		m_nMADR	+= nRecv * 0x10;
		m_nQWC	-= nRecv;

		if(m_nQWC != 0)
		{
			//Transfer isn't finished, suspend for now
			return;
		}
	}

	while(m_CHCR.nSTR == 1)
	{
        //Check if device received DMAtag
        if(m_CHCR.nReserved0)
        {
            assert(m_CHCR.nTTE);
            m_CHCR.nReserved0 = 0;
            if(m_pReceive(m_nTADR, 1, 0, true) != 1)
            {
                //Device didn't receive DmaTag, break for now
                m_CHCR.nReserved0 = 1;
                break;
            }
        }
        else
        {
	        //Check if we've finished our DMA transfer
	        if(m_nQWC == 0)
	        {
		        if(m_nSCCTRL & SCCTRL_INITXFER)
		        {
			        //Clear this bit
			        m_nSCCTRL &= ~SCCTRL_INITXFER;
		        }
		        else
		        {
                    if(CDMAC::IsEndTagId((uint32)m_CHCR.nTAG << 16))
			        {
				        ClearSTR();
				        continue;
			        }
		        }
	        }
	        else
	        {
		        //Suspend transfer
		        break;
	        }

	        if(m_CHCR.nTTE == 1)
	        {
                m_CHCR.nReserved0 = 0;
                if(m_pReceive(m_nTADR, 1, 0, true) != 1)
                {
                    //Device didn't receive DmaTag, break for now
                    m_CHCR.nReserved0 = 1;
                    break;
                }
	        }
        }

        //Half-Life does this...
        if(m_nTADR == 0)
        {
            ClearSTR();
            continue;
        }

		nTag = m_dmac.FetchDMATag(m_nTADR);

		//Save higher 16 bits of tag into CHCR
		m_CHCR.nTAG = nTag >> 16;
		//m_nCHCR &= ~0xFFFF0000;
		//m_nCHCR |= nTag & 0xFFFF0000;

		nID = (uint8)((nTag >> 28) & 0x07);

		switch(nID)
		{
		case 0:
			//REFE - Data to transfer is pointer in memory address, transfer is done
			m_nMADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= m_nTADR + 0x10;
			break;
		case 1:
			//CNT - Data to transfer is after the tag, next tag is after the data
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			m_nTADR		= (m_nQWC * 0x10) + m_nMADR;
			break;
		case 2:
			//NEXT - Transfers data after tag, next tag is at position in ADDR field
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			break;
		case 3:
			//REF - Data to transfer is pointed in memory address, next tag is after this tag
			m_nMADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= m_nTADR + 0x10;
			break;
		case 5:
			//CALL - Transfers QWC after the tag, saves next address in ASR, TADR = ADDR
			assert(m_CHCR.nASP < 2);
			m_nMADR				= m_nTADR + 0x10;
			m_nQWC				= (uint32)(nTag & 0xFFFF);
			m_nASR[m_CHCR.nASP]	= m_nMADR + (m_nQWC * 0x10);
			m_nTADR				= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_CHCR.nASP++;
			break;
		case 6:
			//RET - Transfers QWC after the tag, pops TADR from ASR
			assert(m_CHCR.nASP > 0);
			m_CHCR.nASP--;

			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			m_nTADR		= m_nASR[m_CHCR.nASP];
			break;
		case 7:
			//END - Data to transfer is after the tag, transfer is finished
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			break;
		default:
			m_nQWC = 0;
			assert(0);
			break;
		}

		if(m_nQWC != 0)
		{
			nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

			m_nMADR		+= nRecv * 0x10;
			m_nQWC		-= nRecv;
		}
	}
}

void CChannel::SetReceiveHandler(const DmaReceiveHandler& handler)
{
    m_pReceive = handler;
}

void CChannel::ClearSTR()
{
	m_CHCR.nSTR = ~m_CHCR.nSTR;

	//Set interrupt
	m_dmac.m_D_STAT |= (1 << m_nNumber);

	m_dmac.UpdateCpCond();
}
<commit_msg>Incomplete MFIFO support.<commit_after>#include <string.h>
#include <assert.h>
#include <boost/lexical_cast.hpp>
#include "Dmac_Channel.h"
#include "DMAC.h"
#include "RegisterStateFile.h"

#define STATE_PREFIX            ("dmac/channel_")
#define STATE_SUFFIX            (".xml")
#define STATE_REGS_CHCR         ("CHCR")
#define STATE_REGS_MADR         ("MADR")
#define STATE_REGS_QWC          ("QWC")
#define STATE_REGS_TADR         ("TADR")
#define STATE_REGS_SCCTRL       ("SCCTRL")
#define STATE_REGS_ASR0         ("ASR0")
#define STATE_REGS_ASR1         ("ASR1")

using namespace std;
using namespace Dmac;
using namespace boost;

CChannel::CChannel(CDMAC& dmac, unsigned int nNumber, const DmaReceiveHandler& pReceive) :
m_dmac(dmac),
m_nNumber(nNumber),
m_pReceive(pReceive)
{

}

CChannel::~CChannel()
{

}

void CChannel::Reset()
{
	memset(&m_CHCR, 0, sizeof(CHCR));
	m_nMADR		= 0;
	m_nQWC		= 0;
	m_nTADR		= 0;
	m_nSCCTRL	= 0;
}

void CChannel::SaveState(Framework::CZipArchiveWriter& archive)
{
    string path = STATE_PREFIX + lexical_cast<string>(m_nNumber) + STATE_SUFFIX;
    CRegisterStateFile* registerFile = new CRegisterStateFile(path.c_str());
    registerFile->SetRegister32(STATE_REGS_CHCR,    m_CHCR);
    registerFile->SetRegister32(STATE_REGS_MADR,    m_nMADR);
    registerFile->SetRegister32(STATE_REGS_QWC,     m_nQWC);
    registerFile->SetRegister32(STATE_REGS_TADR,    m_nTADR);
    registerFile->SetRegister32(STATE_REGS_SCCTRL,  m_nSCCTRL);
    registerFile->SetRegister32(STATE_REGS_ASR0,    m_nASR[0]);
    registerFile->SetRegister32(STATE_REGS_ASR1,    m_nASR[1]);
    archive.InsertFile(registerFile);
}

void CChannel::LoadState(Framework::CZipArchiveReader& archive)
{
    string path = STATE_PREFIX + lexical_cast<string>(m_nNumber) + STATE_SUFFIX;
    CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
    m_CHCR      <<= registerFile.GetRegister32(STATE_REGS_CHCR);
    m_nMADR       = registerFile.GetRegister32(STATE_REGS_MADR);
    m_nQWC        = registerFile.GetRegister32(STATE_REGS_QWC);
    m_nTADR       = registerFile.GetRegister32(STATE_REGS_TADR);
    m_nSCCTRL     = registerFile.GetRegister32(STATE_REGS_SCCTRL);
    m_nASR[0]     = registerFile.GetRegister32(STATE_REGS_ASR0);
    m_nASR[1]     = registerFile.GetRegister32(STATE_REGS_ASR1);
}

uint32 CChannel::ReadCHCR()
{
	return *(uint32*)&m_CHCR;
}

void CChannel::WriteCHCR(uint32 nValue)
{
	bool nSuspend = false;

	//We need to check if the purpose of this write is to suspend the current transfer
    if(m_dmac.m_D_ENABLE & CDMAC::ENABLE_CPND)
	{
        nSuspend = (m_CHCR.nSTR != 0) && ((nValue & CDMAC::CHCR_STR) == 0);
	}

	if(m_CHCR.nSTR == 1)
	{
		m_CHCR.nSTR = ~m_CHCR.nSTR;
        m_CHCR.nSTR = ((nValue & CDMAC::CHCR_STR) != 0) ? 1 : 0;
	}
	else
	{
		m_CHCR = *(CHCR*)&nValue;
	}

    if(m_CHCR.nSTR != 0)
    {
        m_nSCCTRL |= SCCTRL_INITXFER;
        Execute();
    }
}

void CChannel::Execute()
{
	if(m_CHCR.nSTR != 0)
	{
        if(m_dmac.m_D_ENABLE)
        {
            if(m_nNumber != 4)
            {
                throw runtime_error("Need to check that case.");
            }
            return;
        }
        switch(m_CHCR.nMOD)
		{
		case 0x00:
			ExecuteNormal();
			break;
		case 0x01:
			ExecuteSourceChain();
			break;
		default:
			assert(0);
			break;
		}
	}
}

void CChannel::ExecuteNormal()
{
	uint32 nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

	m_nMADR	+= nRecv * 0x10;
	m_nQWC	-= nRecv;

	if(m_nQWC == 0)
	{
		ClearSTR();
	}
}

void CChannel::ExecuteSourceChain()
{
	uint64 nTag;
	uint32 nRecv;
	uint8 nID;

	//Execute current
	if(m_nQWC != 0)
	{
		nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

		m_nMADR	+= nRecv * 0x10;
		m_nQWC	-= nRecv;

		if(m_nQWC != 0)
		{
			//Transfer isn't finished, suspend for now
			return;
		}
	}

	while(m_CHCR.nSTR == 1)
	{
        //Check if MFIFO is enabled with this channel
        if(m_dmac.m_D_CTRL.mfd == 0x02 && m_nNumber == 1)
        {
            //Hold transfer if not ready
            if(m_nTADR == m_dmac.m_D8.m_nMADR)
            {
                break;
            }

            uint32 currentPos = (m_nTADR & m_dmac.m_D_RBSR) + m_dmac.m_D_RBOR;
            if(currentPos != m_nTADR)
            {
                int i = 0;
                i++;
            }
        }

        //Check if device received DMAtag
        if(m_CHCR.nReserved0)
        {
            assert(m_CHCR.nTTE);
            m_CHCR.nReserved0 = 0;
            if(m_pReceive(m_nTADR, 1, 0, true) != 1)
            {
                //Device didn't receive DmaTag, break for now
                m_CHCR.nReserved0 = 1;
                break;
            }
        }
        else
        {
	        //Check if we've finished our DMA transfer
	        if(m_nQWC == 0)
	        {
		        if(m_nSCCTRL & SCCTRL_INITXFER)
		        {
			        //Clear this bit
			        m_nSCCTRL &= ~SCCTRL_INITXFER;
		        }
		        else
		        {
                    if(CDMAC::IsEndTagId((uint32)m_CHCR.nTAG << 16))
			        {
				        ClearSTR();
				        continue;
			        }
		        }
	        }
	        else
	        {
		        //Suspend transfer
		        break;
	        }

	        if(m_CHCR.nTTE == 1)
	        {
                m_CHCR.nReserved0 = 0;
                if(m_pReceive(m_nTADR, 1, 0, true) != 1)
                {
                    //Device didn't receive DmaTag, break for now
                    m_CHCR.nReserved0 = 1;
                    break;
                }
	        }
        }

        //Half-Life does this...
        if(m_nTADR == 0)
        {
            ClearSTR();
            continue;
        }

		nTag = m_dmac.FetchDMATag(m_nTADR);

		//Save higher 16 bits of tag into CHCR
		m_CHCR.nTAG = nTag >> 16;
		//m_nCHCR &= ~0xFFFF0000;
		//m_nCHCR |= nTag & 0xFFFF0000;

		nID = (uint8)((nTag >> 28) & 0x07);

		switch(nID)
		{
		case 0:
			//REFE - Data to transfer is pointer in memory address, transfer is done
			m_nMADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= m_nTADR + 0x10;
			break;
		case 1:
			//CNT - Data to transfer is after the tag, next tag is after the data
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			m_nTADR		= (m_nQWC * 0x10) + m_nMADR;
			break;
		case 2:
			//NEXT - Transfers data after tag, next tag is at position in ADDR field
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			break;
		case 3:
			//REF - Data to transfer is pointed in memory address, next tag is after this tag
			m_nMADR		= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_nQWC		= (uint32)((nTag >>   0) & 0x0000FFFF);
			m_nTADR		= m_nTADR + 0x10;
			break;
		case 5:
			//CALL - Transfers QWC after the tag, saves next address in ASR, TADR = ADDR
			assert(m_CHCR.nASP < 2);
			m_nMADR				= m_nTADR + 0x10;
			m_nQWC				= (uint32)(nTag & 0xFFFF);
			m_nASR[m_CHCR.nASP]	= m_nMADR + (m_nQWC * 0x10);
			m_nTADR				= (uint32)((nTag >>  32) & 0xFFFFFFFF);
			m_CHCR.nASP++;
			break;
		case 6:
			//RET - Transfers QWC after the tag, pops TADR from ASR
			assert(m_CHCR.nASP > 0);
			m_CHCR.nASP--;

			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			m_nTADR		= m_nASR[m_CHCR.nASP];
			break;
		case 7:
			//END - Data to transfer is after the tag, transfer is finished
			m_nMADR		= m_nTADR + 0x10;
			m_nQWC		= (uint32)(nTag & 0xFFFF);
			break;
		default:
			m_nQWC = 0;
			assert(0);
			break;
		}

		if(m_nQWC != 0)
		{
			nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);

			m_nMADR		+= nRecv * 0x10;
			m_nQWC		-= nRecv;
		}
	}
}

void CChannel::SetReceiveHandler(const DmaReceiveHandler& handler)
{
    m_pReceive = handler;
}

void CChannel::ClearSTR()
{
	m_CHCR.nSTR = ~m_CHCR.nSTR;

	//Set interrupt
	m_dmac.m_D_STAT |= (1 << m_nNumber);

	m_dmac.UpdateCpCond();
}
<|endoftext|>


<commit_before>#include "momentum.h"
#include <boost/unordered_map.hpp>
#include <iostream>

namespace bts
{
#define MAX_MOMENTUM_NONCE  (1<<26)
#define SEARCH_SPACE_BITS 50
#define BIRTHDAYS_PER_HASH 8

// I'm a terrible person.
#define UPDATE_HASH(varname)                                                               \
    if (varname##_offset >= BIRTHDAYS_PER_HASH) {                                          \
        varname##_offset = varname##_offset % BIRTHDAYS_PER_HASH;                          \
        varname##_nonce = varname##_minhash;                                               \
                                                                                \
        *index = varname##_nonce;                                                          \
        SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash); \
                                                                                \
        varname##_minhash = (result_hash[sz] >> (64 - SEARCH_SPACE_BITS));                 \
        for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {                         \
            if ((result_hash[sz] >> (64 - SEARCH_SPACE_BITS)) < varname##_minhash) {       \
                varname##_minhash = (result_hash[sz] >> (64 - SEARCH_SPACE_BITS));         \
            }                                                                              \
            varname##_hash[sz] = result_hash[sz];                                          \
        }                                                                                \
    }

std::vector< std::pair<uint32_t, uint32_t> > momentum_search(uint256 midHash)
{
    std::vector< std::pair<uint32_t, uint32_t> > results;

    char  hash_tmp[sizeof(midHash) + 4];
    memcpy((char *)&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    uint32_t *index = (uint32_t *)hash_tmp;
    bool found_hit = false;

    // X
    uint32_t turtle_nonce = 0;
    uint32_t turtle_offset = 0;
    uint64_t turtle_minhash = 0;
    uint64_t turtle_hash[8];

    // X'
    uint32_t hare_nonce = 0;
    uint32_t hare_offset = 0;
    uint64_t hare_minhash = 0;
    uint64_t hare_hash[8];

    // Defining X_0
    *index = 0;
    uint64_t result_hash[8];
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    turtle_minhash = result_hash[0];
    hare_minhash = result_hash[0];
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {
        turtle_hash[sz] = result_hash[sz];
        hare_hash[sz] = result_hash[sz];

        if (result_hash[sz] < turtle_minhash) {
            turtle_minhash = result_hash[sz];
            hare_minhash = result_hash[sz];
        }
    }

    // Step 1: dig for a hit
    // Not using 2^(SEARCH_SPACE_BITS/2)
    uint32_t i;
    for(i = 0; i < MAX_MOMENTUM_NONCE; ++i) {

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H( H(X) )
        hare_offset += 2;
        UPDATE_HASH(hare);

        // Found a collision!
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            if (turtle_nonce != hare_nonce && turtle_offset != hare_offset) {
                found_hit = true;
                break;
            }
        }
    }

    // If we stopped due to running out of entries, return the empty list
    if (found_hit == false) {
        std::cerr << "No collision found.\n";
        return results;
    }

    // DEBUG
    std::cerr << "Collision found!\n";
    std::cerr << "   Iteration:     " << i << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Turtle Nonce Valid: " << (turtle_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce Valid:   " << (hare_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "\n";

    results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
    return results;

    ////////////////////////////////////////////////

    // Set X' = X
    hare_nonce = turtle_nonce;
    hare_offset = turtle_offset;
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { hare_hash[sz] = turtle_hash[sz]; }

    // Reset X = X_0
    turtle_nonce = 0;
    turtle_offset = 0;
    *index = 0;
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { turtle_hash[sz] = result_hash[sz]; }

    // Step 2: find where the hit came from
    uint32_t j;
    for (j = 0; j < i; ++j) {
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
            std::cerr << "WOOT WOOT!\n";
            std::cerr << "   Iteration:     " << j << "\n";
            std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
            std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
            std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
            std::cerr << "   Hare Offset:   " << hare_offset << "\n";
            std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            return results;
        }

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H(X)
        ++hare_offset;
        UPDATE_HASH(hare);
    }

    std::cerr << "I have no idea how you got here and it terrifies me.\n";
    std::cerr << "   Iteration:     " << j << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";

    return results;
}

uint64_t getBirthdayHash(const uint256 &midHash, uint32_t a)
{
    uint32_t index = a - (a % 8);
    char  hash_tmp[sizeof(midHash) + 4];

    memcpy(&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    memcpy(&hash_tmp[0], (char *)&index, sizeof(index));

    uint64_t  result_hash[8];

    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)&result_hash);

    uint64_t r = result_hash[a % BIRTHDAYS_PER_HASH] >> (64 - SEARCH_SPACE_BITS);

    return r;
}

bool momentum_verify(uint256 head, uint32_t a, uint32_t b)
{

    if(a == b) {
        return false;
    }

    if(a > MAX_MOMENTUM_NONCE) {
        return false;
    }

    if(b > MAX_MOMENTUM_NONCE) {
        return false;
    }

    bool r = (getBirthdayHash(head, a) == getBirthdayHash(head, b));
    return r;
}

}
<commit_msg>Optimize macro<commit_after>#include "momentum.h"
#include <boost/unordered_map.hpp>
#include <iostream>

namespace bts
{
#define MAX_MOMENTUM_NONCE  (1<<26)
#define SEARCH_SPACE_BITS 50
#define BIRTHDAYS_PER_HASH 8

// I'm a terrible person.
#define UPDATE_HASH(varname)                                                               \
    if (varname##_offset >= BIRTHDAYS_PER_HASH) {                                          \
        varname##_offset = varname##_offset % BIRTHDAYS_PER_HASH;                          \
        varname##_nonce = (varname##_minhash >> (64 - SEARCH_SPACE_BITS));                 \
                                                                                \
        *index = varname##_nonce;                                                          \
        SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash); \
                                                                                \
        varname##_minhash = result_hash[0];                                                \
        for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {                         \
            if (result_hash[sz] < varname##_minhash) {                                     \
                varname##_minhash = result_hash[sz];                                       \
            }                                                                              \
            varname##_hash[sz] = result_hash[sz];                                          \
        }                                                                                \
    }

std::vector< std::pair<uint32_t, uint32_t> > momentum_search(uint256 midHash)
{
    std::vector< std::pair<uint32_t, uint32_t> > results;

    char  hash_tmp[sizeof(midHash) + 4];
    memcpy((char *)&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    uint32_t *index = (uint32_t *)hash_tmp;
    bool found_hit = false;

    // X
    uint32_t turtle_nonce = 0;
    uint32_t turtle_offset = 0;
    uint64_t turtle_minhash = 0;
    uint64_t turtle_hash[8];

    // X'
    uint32_t hare_nonce = 0;
    uint32_t hare_offset = 0;
    uint64_t hare_minhash = 0;
    uint64_t hare_hash[8];

    // Defining X_0
    *index = 0;
    uint64_t result_hash[8];
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    turtle_minhash = result_hash[0];
    hare_minhash = result_hash[0];
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {
        turtle_hash[sz] = result_hash[sz];
        hare_hash[sz] = result_hash[sz];

        if (result_hash[sz] < turtle_minhash) {
            turtle_minhash = result_hash[sz];
            hare_minhash = result_hash[sz];
        }
    }

    // Step 1: dig for a hit
    // Not using 2^(SEARCH_SPACE_BITS/2)
    uint32_t i;
    for(i = 0; i < MAX_MOMENTUM_NONCE; ++i) {

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H( H(X) )
        hare_offset += 2;
        UPDATE_HASH(hare);

        // Found a collision!
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            if (turtle_nonce != hare_nonce && turtle_offset != hare_offset) {
                found_hit = true;
                break;
            }
        }
    }

    // If we stopped due to running out of entries, return the empty list
    if (found_hit == false) {
        std::cerr << "No collision found.\n";
        return results;
    }

    // DEBUG
    std::cerr << "Collision found!\n";
    std::cerr << "   Iteration:     " << i << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Turtle Nonce Valid: " << (turtle_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce Valid:   " << (hare_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "\n";

    results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
    return results;

    ////////////////////////////////////////////////

    // Set X' = X
    hare_nonce = turtle_nonce;
    hare_offset = turtle_offset;
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { hare_hash[sz] = turtle_hash[sz]; }

    // Reset X = X_0
    turtle_nonce = 0;
    turtle_offset = 0;
    *index = 0;
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { turtle_hash[sz] = result_hash[sz]; }

    // Step 2: find where the hit came from
    uint32_t j;
    for (j = 0; j < i; ++j) {
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
            std::cerr << "WOOT WOOT!\n";
            std::cerr << "   Iteration:     " << j << "\n";
            std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
            std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
            std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
            std::cerr << "   Hare Offset:   " << hare_offset << "\n";
            std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            return results;
        }

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H(X)
        ++hare_offset;
        UPDATE_HASH(hare);
    }

    std::cerr << "I have no idea how you got here and it terrifies me.\n";
    std::cerr << "   Iteration:     " << j << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";

    return results;
}

uint64_t getBirthdayHash(const uint256 &midHash, uint32_t a)
{
    uint32_t index = a - (a % 8);
    char  hash_tmp[sizeof(midHash) + 4];

    memcpy(&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    memcpy(&hash_tmp[0], (char *)&index, sizeof(index));

    uint64_t  result_hash[8];

    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)&result_hash);

    uint64_t r = result_hash[a % BIRTHDAYS_PER_HASH] >> (64 - SEARCH_SPACE_BITS);

    return r;
}

bool momentum_verify(uint256 head, uint32_t a, uint32_t b)
{

    if(a == b) {
        return false;
    }

    if(a > MAX_MOMENTUM_NONCE) {
        return false;
    }

    if(b > MAX_MOMENTUM_NONCE) {
        return false;
    }

    bool r = (getBirthdayHash(head, a) == getBirthdayHash(head, b));
    return r;
}

}
<|endoftext|>


<commit_before>#include "momentum.h"
#include <boost/unordered_map.hpp>
#include <iostream>

namespace bts
{
#define MAX_MOMENTUM_NONCE  (1<<26)
#define SEARCH_SPACE_BITS 50
#define BIRTHDAYS_PER_HASH 8

// I'm a terrible person.
#define UPDATE_HASH(varname)                                                                   \
    if (varname##_offset >= BIRTHDAYS_PER_HASH) {                                              \
        varname##_offset = varname##_offset % BIRTHDAYS_PER_HASH;                              \
        varname##_nonce = varname##_minhash;                                                   \
                                                                                \
        *index = varname##_nonce;                                                              \
        SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);     \
                                                                                \
        varname##_minhash = result_hash[0];                                                    \
        for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {                             \
            if (result_hash[sz] < varname##_minhash) { varname##_minhash = result_hash[sz]; }  \
            varname##_hash[sz] = result_hash[sz];                                                   \
        }                                                                                \
    }

std::vector< std::pair<uint32_t, uint32_t> > momentum_search(uint256 midHash)
{
    std::vector< std::pair<uint32_t, uint32_t> > results;

    char  hash_tmp[sizeof(midHash) + 4];
    memcpy((char *)&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    uint32_t *index = (uint32_t *)hash_tmp;
    bool found_hit = false;

    // X
    uint32_t turtle_nonce = 0;
    uint32_t turtle_offset = 0;
    uint64_t turtle_minhash = 0;
    uint64_t turtle_hash[8];

    // X'
    uint32_t hare_nonce = 0;
    uint32_t hare_offset = 0;
    uint64_t hare_minhash = 0;
    uint64_t hare_hash[8];

    // Defining X_0
    *index = 0;
    uint64_t result_hash[8];
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    turtle_minhash = result_hash[0];
    hare_minhash = result_hash[0];
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {
        turtle_hash[sz] = result_hash[sz];
        hare_hash[sz] = result_hash[sz];

        if (result_hash[sz] < turtle_minhash) {
            turtle_minhash = result_hash[sz];
            hare_minhash = result_hash[sz];
        }
    }

    // Step 1: dig for a hit
    // Not using 2^(SEARCH_SPACE_BITS/2)
    uint32_t i;
    for(i = 0; i < MAX_MOMENTUM_NONCE; ++i) {

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H( H(X) )
        hare_offset += 2;
        UPDATE_HASH(hare);

        // Found a collision!
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            if (turtle_nonce != hare_nonce && turtle_offset != hare_offset) {
                found_hit = true;
                break;
            }
        }
    }

    // If we stopped due to running out of entries, return the empty list
    if (found_hit == false) {
        std::cerr << "No collision found.\n";
        return results;
    }

    // DEBUG
    std::cerr << "Collision found!\n";
    std::cerr << "   Iteration:     " << i << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Turtle Nonce Valid: " << (turtle_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce Valid:   " << (hare_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "\n";

    results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
    return results;

    ////////////////////////////////////////////////

    // Set X' = X
    hare_nonce = turtle_nonce;
    hare_offset = turtle_offset;
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { hare_hash[sz] = turtle_hash[sz]; }

    // Reset X = X_0
    turtle_nonce = 0;
    turtle_offset = 0;
    *index = 0;
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { turtle_hash[sz] = result_hash[sz]; }

    // Step 2: find where the hit came from
    uint32_t j;
    for (j = 0; j < i; ++j) {
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
            std::cerr << "WOOT WOOT!\n";
            std::cerr << "   Iteration:     " << j << "\n";
            std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
            std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
            std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
            std::cerr << "   Hare Offset:   " << hare_offset << "\n";
            std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            return results;
        }

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H(X)
        ++hare_offset;
        UPDATE_HASH(hare);
    }

    std::cerr << "I have no idea how you got here and it terrifies me.\n";
    std::cerr << "   Iteration:     " << j << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";

    return results;
}

uint64_t getBirthdayHash(const uint256 &midHash, uint32_t a)
{
    uint32_t index = a - (a % 8);
    char  hash_tmp[sizeof(midHash) + 4];

    memcpy(&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    memcpy(&hash_tmp[0], (char *)&index, sizeof(index));

    uint64_t  result_hash[8];

    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)&result_hash);

    uint64_t r = result_hash[a % BIRTHDAYS_PER_HASH] >> (64 - SEARCH_SPACE_BITS);

    return r;
}

bool momentum_verify(uint256 head, uint32_t a, uint32_t b)
{

    if(a == b) {
        return false;
    }

    if(a > MAX_MOMENTUM_NONCE) {
        return false;
    }

    if(b > MAX_MOMENTUM_NONCE) {
        return false;
    }

    bool r = (getBirthdayHash(head, a) == getBirthdayHash(head, b));
    return r;
}

}
<commit_msg>More macro fixing<commit_after>#include "momentum.h"
#include <boost/unordered_map.hpp>
#include <iostream>

namespace bts
{
#define MAX_MOMENTUM_NONCE  (1<<26)
#define SEARCH_SPACE_BITS 50
#define BIRTHDAYS_PER_HASH 8

// I'm a terrible person.
#define UPDATE_HASH(varname)                                                               \
    if (varname##_offset >= BIRTHDAYS_PER_HASH) {                                          \
        varname##_offset = varname##_offset % BIRTHDAYS_PER_HASH;                          \
        varname##_nonce = varname##_minhash;                                               \
                                                                                \
        *index = varname##_nonce;                                                          \
        SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash); \
                                                                                \
        varname##_minhash = (result_hash[sz] >> (64 - SEARCH_SPACE_BITS));                 \
        for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {                         \
            if ((result_hash[sz] >> (64 - SEARCH_SPACE_BITS)) < varname##_minhash) {       \
                varname##_minhash = (result_hash[sz] >> (64 - SEARCH_SPACE_BITS));         \
            }                                                                              \
            varname##_hash[sz] = result_hash[sz];                                          \
        }                                                                                \
    }

std::vector< std::pair<uint32_t, uint32_t> > momentum_search(uint256 midHash)
{
    std::vector< std::pair<uint32_t, uint32_t> > results;

    char  hash_tmp[sizeof(midHash) + 4];
    memcpy((char *)&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    uint32_t *index = (uint32_t *)hash_tmp;
    bool found_hit = false;

    // X
    uint32_t turtle_nonce = 0;
    uint32_t turtle_offset = 0;
    uint64_t turtle_minhash = 0;
    uint64_t turtle_hash[8];

    // X'
    uint32_t hare_nonce = 0;
    uint32_t hare_offset = 0;
    uint64_t hare_minhash = 0;
    uint64_t hare_hash[8];

    // Defining X_0
    *index = 0;
    uint64_t result_hash[8];
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    turtle_minhash = result_hash[0];
    hare_minhash = result_hash[0];
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) {
        turtle_hash[sz] = result_hash[sz];
        hare_hash[sz] = result_hash[sz];

        if (result_hash[sz] < turtle_minhash) {
            turtle_minhash = result_hash[sz];
            hare_minhash = result_hash[sz];
        }
    }

    // Step 1: dig for a hit
    // Not using 2^(SEARCH_SPACE_BITS/2)
    uint32_t i;
    for(i = 0; i < MAX_MOMENTUM_NONCE; ++i) {

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H( H(X) )
        hare_offset += 2;
        UPDATE_HASH(hare);

        // Found a collision!
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            if (turtle_nonce != hare_nonce && turtle_offset != hare_offset) {
                found_hit = true;
                break;
            }
        }
    }

    // If we stopped due to running out of entries, return the empty list
    if (found_hit == false) {
        std::cerr << "No collision found.\n";
        return results;
    }

    // DEBUG
    std::cerr << "Collision found!\n";
    std::cerr << "   Iteration:     " << i << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Turtle Nonce Valid: " << (turtle_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce Valid:   " << (hare_nonce < MAX_MOMENTUM_NONCE) << "\n";
    std::cerr << "\n";

    results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
    return results;

    ////////////////////////////////////////////////

    // Set X' = X
    hare_nonce = turtle_nonce;
    hare_offset = turtle_offset;
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { hare_hash[sz] = turtle_hash[sz]; }

    // Reset X = X_0
    turtle_nonce = 0;
    turtle_offset = 0;
    *index = 0;
    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)result_hash);
    for (unsigned int sz = 0; sz < BIRTHDAYS_PER_HASH; ++sz) { turtle_hash[sz] = result_hash[sz]; }

    // Step 2: find where the hit came from
    uint32_t j;
    for (j = 0; j < i; ++j) {
        if ((turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) == (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS))) {
            results.push_back( std::make_pair(turtle_nonce + turtle_offset, hare_nonce + hare_offset) );
            std::cerr << "WOOT WOOT!\n";
            std::cerr << "   Iteration:     " << j << "\n";
            std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
            std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
            std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
            std::cerr << "   Hare Offset:   " << hare_offset << "\n";
            std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
            return results;
        }

        // TURTLE
        // X = H(X)
        ++turtle_offset;
        UPDATE_HASH(turtle);

        // HARE
        // X' = H(X)
        ++hare_offset;
        UPDATE_HASH(hare);
    }

    std::cerr << "I have no idea how you got here and it terrifies me.\n";
    std::cerr << "   Iteration:     " << j << "\n";
    std::cerr << "   Turtle Nonce:  " << turtle_nonce  << "\n";
    std::cerr << "   Turtle Offset: " << turtle_offset << "\n";
    std::cerr << "   Turtle Hash:   " << (turtle_hash[turtle_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";
    std::cerr << "   Hare Nonce:    " << hare_nonce  << "\n";
    std::cerr << "   Hare Offset:   " << hare_offset << "\n";
    std::cerr << "   Hare Hash:     " << (hare_hash[hare_offset] >> (64 - SEARCH_SPACE_BITS)) << "\n";

    return results;
}

uint64_t getBirthdayHash(const uint256 &midHash, uint32_t a)
{
    uint32_t index = a - (a % 8);
    char  hash_tmp[sizeof(midHash) + 4];

    memcpy(&hash_tmp[4], (char *)&midHash, sizeof(midHash));
    memcpy(&hash_tmp[0], (char *)&index, sizeof(index));

    uint64_t  result_hash[8];

    SHA512((unsigned char *)hash_tmp, sizeof(hash_tmp), (unsigned char *)&result_hash);

    uint64_t r = result_hash[a % BIRTHDAYS_PER_HASH] >> (64 - SEARCH_SPACE_BITS);

    return r;
}

bool momentum_verify(uint256 head, uint32_t a, uint32_t b)
{

    if(a == b) {
        return false;
    }

    if(a > MAX_MOMENTUM_NONCE) {
        return false;
    }

    if(b > MAX_MOMENTUM_NONCE) {
        return false;
    }

    bool r = (getBirthdayHash(head, a) == getBirthdayHash(head, b));
    return r;
}

}
<|endoftext|>


<commit_before>/******************************************************************************\
* LuaVariable.hpp                                                              *
* A variable living in a Lua interpreter.                                      *
* Leandro Motta Barros                                                         *
\******************************************************************************/

#ifndef _DILUCULUM_LUA_VARIABLE_HPP_
#define _DILUCULUM_LUA_VARIABLE_HPP_

#include <vector>
#include "LuaValue.hpp"

namespace Diluculum
{
   /** A variable living in a Lua state. Notice the crucial difference: unlike a
    *  \c LuaValue, a \c LuaVariable necessarily has a real counterpart in a Lua
    *  state. Thus, when something is assigned to a \c LuaVariable, the value of
    *  the corresponding variable in the Lua state is changed, too.
    *
<tt>LuaVariable</tt>s cannot be directly constructed. They are
    *  designed to be returned by <tt>LuaState</tt>'s subscript operator.
    *  @todo Add tests for expected exceptions.
    */
   class LuaVariable
   {
      friend class LuaState;

      public:
         /** Assigns a new value to this \c LuaVariable. The corresponding
          *  variable in the Lua state is updated accordingly.
          *  @param rhs The new value for the variable.
          *  @return \c *this, so that a sequence of assignments, like
          *          <tt>a = b = c = 1;</tt> works.
          *  @todo Considering that <tt>LuaVariable</tt>s shouldn't be copyable
          *        by the current design, perhaps it would make more sense to
          *        return a \c LuaValue (<tt>this->value()</tt>).
          */
         LuaVariable& operator= (const LuaValue& rhs);

         /** Assigns the value of a \c LuaVariable to this \c LuaVariable.
          *  @todo The assignment is based on the value of \c rhs. Recall that
          *        "real" copy of <tt>LuaVariable</tt>s is forbidden by design,
          *        so this makes some sense. But, the design is objectionable,
          *        and I expect to rethink it soon. If I decide that copying
          *        <tt>LuaVariable</tt>s is OK, then I must change the
          *        documentation accordingly. If not, then add this discussion
          *        as a note.
          *  @bug The current implementation is a no-op. Looks like some
          *       compilers will just accept this assignment operator if the
          *       copy constructor is public. So, I must decide whether copying
          *       <tt>LuaVariable</tt>s is OK before I can fix this
          *       definitively. Also, depending on the chosen fix, I'll be able
          *       to get rid of some <tt>.value()</tt> at some tests.
          *
And, also important: at
          *       \c TestLuaVariableAssignmentOperator()
          *       (\c TestLuaVariable.cpp) there some tests commented-out
          *       because they fail due to this no-op implementation.
          */
         LuaVariable& operator= (const LuaVariable& rhs)
         { /* *this = rhs.value(); */ return *this; }

         /** Returns the value associated with this variable.
          *  @todo Throw a more specific exception (instead \c LuaError), and a
          *        more adequate message (instead of "Duh"). When doing this,
          *        add some tests to ensure that the proper exception is thrown
          *        when calling \c value() and \c operator[].
          *  @bug In fact, no exception is currently being thrown. Must add test
          *       cases, check the code and document.
          */
         LuaValue value() const;

         /** Assuming that this \c LuaVariable holds a table, returns the value
          *  whose index is \c key.
          *  @param key The key whose value is desired.
          *  @return The value whose index is \c key.
          *  @todo What about exceptions? Must add test cases, check the code
          *        and document.
          */
         LuaVariable operator[] (const LuaValue& key);

         /** Checks whether the value stored in this variable is equal to the
          *  value at \c rhs.
          *  @param rhs The value against which the comparison will be done.
          *  @return \c true if this variable's value is equal to \c rhs.
          *          \c false otherwise.
          */
         bool operator== (const LuaValue& rhs)
         { return value() == rhs; }

      private:
         /// A sequence of keys, used to access nested tables.
         typedef std::vector<LuaValue> KeyList;

         /** Constructs a \c LuaVariable. Note that this is private, so that no
          *  one is expected to construct a \c LuaVariable directly. Use
          *  <tt>LuaState</tt>'s subscript operator instead.
          */
         LuaVariable (lua_State* state, const LuaValue& key,
                      const KeyList& predKeys = KeyList());

         /** <tt>LuaVariable</tt>'s copy constructor.
          *  @note This is just declared, not not defined. In other words,
          *        trying to copy a \c LuaVariable will result in a link-time
          *        error, and this is so by design.
          *  @todo I firstly disallowed copying because it sounded hard to keep
          *        multiple copies of the save variable in sync (for example, if
          *        a new value is assigned to a variable, its copies should
          *        refer to the new value if their \c value() method is called).
          *        Now, it sounds like it is not that hard to allow copies. Must
          *        check this.
          */
         LuaVariable (const LuaVariable&);

         /// The Lua state in which this \c LuaVariable lives.
         lua_State* state_;

         /** The sequence of keys used to get to this variable. For a global
          *  variable, this will consist of a single key; for variables inside
          *  nested tables, this sequence can be arbitrarily long.
          */
         std::vector<LuaValue> keys_;
   };

} // namespace Diluculum

#endif // _DILUCULUM_LUA_VARIABLE_HPP_
<commit_msg>Just removed an extra blank line at 'LuaVariable.hpp'.<commit_after>/******************************************************************************\
* LuaVariable.hpp                                                              *
* A variable living in a Lua interpreter.                                      *
* Leandro Motta Barros                                                         *
\******************************************************************************/

#ifndef _DILUCULUM_LUA_VARIABLE_HPP_
#define _DILUCULUM_LUA_VARIABLE_HPP_

#include <vector>
#include "LuaValue.hpp"

namespace Diluculum
{
   /** A variable living in a Lua state. Notice the crucial difference: unlike a
    *  \c LuaValue, a \c LuaVariable necessarily has a real counterpart in a Lua
    *  state. Thus, when something is assigned to a \c LuaVariable, the value of
    *  the corresponding variable in the Lua state is changed, too.
    *
<tt>LuaVariable</tt>s cannot be directly constructed. They are
    *  designed to be returned by <tt>LuaState</tt>'s subscript operator.
    *  @todo Add tests for expected exceptions.
    */
   class LuaVariable
   {
      friend class LuaState;

      public:
         /** Assigns a new value to this \c LuaVariable. The corresponding
          *  variable in the Lua state is updated accordingly.
          *  @param rhs The new value for the variable.
          *  @return \c *this, so that a sequence of assignments, like
          *          <tt>a = b = c = 1;</tt> works.
          *  @todo Considering that <tt>LuaVariable</tt>s shouldn't be copyable
          *        by the current design, perhaps it would make more sense to
          *        return a \c LuaValue (<tt>this->value()</tt>).
          */
         LuaVariable& operator= (const LuaValue& rhs);

         /** Assigns the value of a \c LuaVariable to this \c LuaVariable.
          *  @todo The assignment is based on the value of \c rhs. Recall that
          *        "real" copy of <tt>LuaVariable</tt>s is forbidden by design,
          *        so this makes some sense. But, the design is objectionable,
          *        and I expect to rethink it soon. If I decide that copying
          *        <tt>LuaVariable</tt>s is OK, then I must change the
          *        documentation accordingly. If not, then add this discussion
          *        as a note.
          *  @bug The current implementation is a no-op. Looks like some
          *       compilers will just accept this assignment operator if the
          *       copy constructor is public. So, I must decide whether copying
          *       <tt>LuaVariable</tt>s is OK before I can fix this
          *       definitively. Also, depending on the chosen fix, I'll be able
          *       to get rid of some <tt>.value()</tt> at some tests.
          *
And, also important: at
          *       \c TestLuaVariableAssignmentOperator()
          *       (\c TestLuaVariable.cpp) there some tests commented-out
          *       because they fail due to this no-op implementation.
          */
         LuaVariable& operator= (const LuaVariable& rhs)
         { /* *this = rhs.value(); */ return *this; }

         /** Returns the value associated with this variable.
          *  @todo Throw a more specific exception (instead \c LuaError), and a
          *        more adequate message (instead of "Duh"). When doing this,
          *        add some tests to ensure that the proper exception is thrown
          *        when calling \c value() and \c operator[].
          *  @bug In fact, no exception is currently being thrown. Must add test
          *       cases, check the code and document.
          */
         LuaValue value() const;

         /** Assuming that this \c LuaVariable holds a table, returns the value
          *  whose index is \c key.
          *  @param key The key whose value is desired.
          *  @return The value whose index is \c key.
          *  @todo What about exceptions? Must add test cases, check the code
          *        and document.
          */
         LuaVariable operator[] (const LuaValue& key);

         /** Checks whether the value stored in this variable is equal to the
          *  value at \c rhs.
          *  @param rhs The value against which the comparison will be done.
          *  @return \c true if this variable's value is equal to \c rhs.
          *          \c false otherwise.
          */
         bool operator== (const LuaValue& rhs)
         { return value() == rhs; }

      private:
         /// A sequence of keys, used to access nested tables.
         typedef std::vector<LuaValue> KeyList;

         /** Constructs a \c LuaVariable. Note that this is private, so that no
          *  one is expected to construct a \c LuaVariable directly. Use
          *  <tt>LuaState</tt>'s subscript operator instead.
          */
         LuaVariable (lua_State* state, const LuaValue& key,
                      const KeyList& predKeys = KeyList());

         /** <tt>LuaVariable</tt>'s copy constructor.
          *  @note This is just declared, not not defined. In other words,
          *        trying to copy a \c LuaVariable will result in a link-time
          *        error, and this is so by design.
          *  @todo I firstly disallowed copying because it sounded hard to keep
          *        multiple copies of the save variable in sync (for example, if
          *        a new value is assigned to a variable, its copies should
          *        refer to the new value if their \c value() method is called).
          *        Now, it sounds like it is not that hard to allow copies. Must
          *        check this.
          */
         LuaVariable (const LuaVariable&);

         /// The Lua state in which this \c LuaVariable lives.
         lua_State* state_;

         /** The sequence of keys used to get to this variable. For a global
          *  variable, this will consist of a single key; for variables inside
          *  nested tables, this sequence can be arbitrarily long.
          */
         std::vector<LuaValue> keys_;
   };

} // namespace Diluculum

#endif // _DILUCULUM_LUA_VARIABLE_HPP_
<|endoftext|>