<commit_before>#include "cmake.h"
#include "filesystem.h"
#include "dialogs.h"
#include "config.h"
#include "terminal.h"
#include <boost/regex.hpp>

CMake::CMake(const boost::filesystem::path &path) {
  const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) {
    for(auto &line: filesystem::read_lines(cmake_path)) {
      const boost::regex project_regex("^ *project *\\(.*$");
      boost::smatch sm;
      if(boost::regex_match(line, sm, project_regex))
        return true;
    }
    return false;
  };

  auto search_path=path;
  auto search_cmake_path=search_path;
  search_cmake_path+="/CMakeLists.txt";
  if(boost::filesystem::exists(search_cmake_path))
    paths.emplace(paths.begin(), search_cmake_path);
  if(find_cmake_project(search_cmake_path))
    project_path=search_path;
  else {
    do {
      search_path=search_path.parent_path();
      search_cmake_path=search_path;
      search_cmake_path+="/CMakeLists.txt";
      if(boost::filesystem::exists(search_cmake_path))
        paths.emplace(paths.begin(), search_cmake_path);
      if(find_cmake_project(search_cmake_path)) {
        project_path=search_path;
        break;
      }
    } while(search_path!=search_path.root_directory());
  }
  if(!project_path.empty()) {
    if(boost::filesystem::exists(project_path/"CMakeLists.txt") && !boost::filesystem::exists(project_path/"compile_commands.json"))
      create_compile_commands(project_path);
  }
}

bool CMake::create_compile_commands(const boost::filesystem::path &path) {
  Dialog::Message message("Creating "+path.string()+"/compile_commands.json");
  auto exit_status=Terminal::get().process(Config::get().terminal.cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path);
  message.hide();
  if(exit_status==EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
    auto compile_commands_path=path;
    compile_commands_path+="/compile_commands.json";
    auto compile_commands_file=filesystem::read(compile_commands_path);
    size_t pos=0;
    while((pos=compile_commands_file.find("-I/", pos))!=std::string::npos) {
      if(pos+3<compile_commands_file.size()) {
        std::string drive;
        drive+=compile_commands_file[pos+3];
        compile_commands_file.replace(pos, 4, "-I"+drive+":");
      }
      else
        break;
    }
    filesystem::write(compile_commands_path, compile_commands_file);
#endif
    return true;
  }
  return false;
}

void CMake::read_files() {
  for(auto &path: paths)
    files.emplace_back(filesystem::read(path));
}

void CMake::remove_tabs() {
  for(auto &file: files) {
    for(auto &chr: file) {
      if(chr=='\t')
        chr=' ';
    }
  }
}

void CMake::remove_comments() {
  for(auto &file: files) {
    size_t pos=0;
    size_t comment_start;
    bool inside_comment=false;
    while(pos<file.size()) {
      if(!inside_comment && file[pos]=='#') {
        comment_start=pos;
        inside_comment=true;
      }
      if(inside_comment && file[pos]=='\n') {
        file.erase(comment_start, pos-comment_start);
        pos-=pos-comment_start;
        inside_comment=false;
      }
      pos++;
    }
    if(inside_comment)
      file.erase(comment_start);
  }
}

void CMake::remove_newlines_inside_parentheses() {
  for(auto &file: files) {
    size_t pos=0;
    bool inside_para=false;
    bool inside_quote=false;
    char last_char=0;
    while(pos<file.size()) {
      if(!inside_quote && file[pos]=='"' && last_char!='\\')
        inside_quote=true;
      else if(inside_quote && file[pos]=='"' && last_char!='\\')
        inside_quote=false;

      else if(!inside_quote && file[pos]=='(')
        inside_para=true;
      else if(!inside_quote && file[pos]==')')
        inside_para=false;

      else if(inside_para && file[pos]=='\n')
        file.replace(pos, 1, 1, ' ');
      last_char=file[pos];
      pos++;
    }
  }
}

void CMake::find_variables() {
  for(auto &file: files) {
    size_t pos=0;
    while(pos<file.size()) {
      auto start_line=pos;
      auto end_line=file.find('\n', start_line);
      if(end_line==std::string::npos)
        end_line=file.size();
      if(end_line>start_line) {
        auto line=file.substr(start_line, end_line-start_line);
        boost::smatch sm;
        const boost::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$");
        if(boost::regex_match(line, sm, set_regex)) {
          auto data=sm[2].str();
          while(data.size()>0 && data.back()==' ')
            data.pop_back();
          parse_variable_parameters(data);
          variables[sm[1].str()]=data;
        }
        else {
          const boost::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$");
          if(boost::regex_match(line, sm, project_regex)) {
            auto data=sm[1].str();
            parse_variable_parameters(data);
            variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard?
            variables["PROJECT_NAME"]=data;
          }
        }
      }
      pos=end_line+1;
    }
  }
}

void CMake::parse_variable_parameters(std::string &data) {
  size_t pos=0;
  bool inside_quote=false;
  char last_char=0;
  while(pos<data.size()) {
    if(!inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=true;
      data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
      pos--;
    }
    else if(inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=false;
      data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
      pos--;
    }
    else if(!inside_quote && data[pos]==' ' && pos+1<data.size() && data[pos+1]==' ') {
      data.erase(pos, 1);
      pos--;
    }

    last_char=data[pos];
    pos++;
  }
  for(auto &var: variables) {
    auto pos=data.find("${"+var.first+'}');
    while(pos!=std::string::npos) {
      data.replace(pos, var.first.size()+3, var.second);
      pos=data.find("${"+var.first+'}');
    }
  }

  //Remove variables we do not know:
  pos=data.find("${");
  auto pos_end=data.find("}", pos+2);
  while(pos!=std::string::npos && pos_end!=std::string::npos) {
    data.erase(pos, pos_end-pos+1);
    pos=data.find("${");
    pos_end=data.find("}", pos+2);
  }
}

void CMake::parse() {
  read_files();
  remove_tabs();
  remove_comments();
  remove_newlines_inside_parentheses();
  find_variables();
  parsed=true;
}

std::vector<std::string> CMake::get_function_parameters(std::string &data) {
  std::vector<std::string> parameters;
  size_t pos=0;
  size_t parameter_pos=0;
  bool inside_quote=false;
  char last_char=0;
  while(pos<data.size()) {
    if(!inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=true;
      data.erase(pos, 1);
      pos--;
    }
    else if(inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=false;
      data.erase(pos, 1);
      pos--;
    }
    else if(!inside_quote && pos+1<data.size() && data[pos]==' ' && data[pos+1]==' ') {
      data.erase(pos, 1);
      pos--;
    }
    else if(!inside_quote && data[pos]==' ') {
      parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos));
      if(pos+1<data.size())
        parameter_pos=pos+1;
    }

    last_char=data[pos];
    pos++;
  }
  parameters.emplace_back(data.substr(parameter_pos));
  for(auto &var: variables) {
    for(auto &parameter: parameters) {
      auto pos=parameter.find("${"+var.first+'}');
      while(pos!=std::string::npos) {
        parameter.replace(pos, var.first.size()+3, var.second);
        pos=parameter.find("${"+var.first+'}');
      }
    }
  }
  return parameters;
}

std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) {
  if(!parsed)
    parse();
  std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
  size_t file_c=0;
  for(auto &file: files) {
    size_t pos=0;
    while(pos<file.size()) {
      auto start_line=pos;
      auto end_line=file.find('\n', start_line);
      if(end_line==std::string::npos)
        end_line=file.size();
      if(end_line>start_line) {
        auto line=file.substr(start_line, end_line-start_line);
        const boost::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$");
        boost::smatch sm;
        if(boost::regex_match(line, sm, function_regex)) {
          auto data=sm[1].str();
          while(data.size()>0 && data.back()==' ')
            data.pop_back();
          auto parameters=get_function_parameters(data);
          functions.emplace(functions.begin(), paths[file_c], parameters);
        }
      }
      pos=end_line+1;
    }
    file_c++;
  }
  return functions;
}
<commit_msg>Cleanup of cmake project search, no longer tried to search for cmakelists if starting/reaching a directory without cmakelists<commit_after>#include "cmake.h"
#include "filesystem.h"
#include "dialogs.h"
#include "config.h"
#include "terminal.h"
#include <boost/regex.hpp>

CMake::CMake(const boost::filesystem::path &path) {
  const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) {
    for(auto &line: filesystem::read_lines(cmake_path)) {
      const boost::regex project_regex("^ *project *\\(.*$");
      boost::smatch sm;
      if(boost::regex_match(line, sm, project_regex))
        return true;
    }
    return false;
  };

  auto search_path=path;
  while(true) {
    auto search_cmake_path=search_path/"CMakeLists.txt";
    if(boost::filesystem::exists(search_cmake_path))
      paths.emplace(paths.begin(), search_cmake_path);
    else
      break;
    if(find_cmake_project(search_cmake_path)) {
      project_path=search_path;
      break;
    }
    if(search_path==search_path.root_directory())
      break;
    search_path=search_path.parent_path();
  }

  if(!project_path.empty()) {
    if(boost::filesystem::exists(project_path/"CMakeLists.txt") && !boost::filesystem::exists(project_path/"compile_commands.json"))
      create_compile_commands(project_path);
  }
}

bool CMake::create_compile_commands(const boost::filesystem::path &path) {
  Dialog::Message message("Creating "+path.string()+"/compile_commands.json");
  auto exit_status=Terminal::get().process(Config::get().terminal.cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path);
  message.hide();
  if(exit_status==EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
    auto compile_commands_path=path;
    compile_commands_path+="/compile_commands.json";
    auto compile_commands_file=filesystem::read(compile_commands_path);
    size_t pos=0;
    while((pos=compile_commands_file.find("-I/", pos))!=std::string::npos) {
      if(pos+3<compile_commands_file.size()) {
        std::string drive;
        drive+=compile_commands_file[pos+3];
        compile_commands_file.replace(pos, 4, "-I"+drive+":");
      }
      else
        break;
    }
    filesystem::write(compile_commands_path, compile_commands_file);
#endif
    return true;
  }
  return false;
}

void CMake::read_files() {
  for(auto &path: paths)
    files.emplace_back(filesystem::read(path));
}

void CMake::remove_tabs() {
  for(auto &file: files) {
    for(auto &chr: file) {
      if(chr=='\t')
        chr=' ';
    }
  }
}

void CMake::remove_comments() {
  for(auto &file: files) {
    size_t pos=0;
    size_t comment_start;
    bool inside_comment=false;
    while(pos<file.size()) {
      if(!inside_comment && file[pos]=='#') {
        comment_start=pos;
        inside_comment=true;
      }
      if(inside_comment && file[pos]=='\n') {
        file.erase(comment_start, pos-comment_start);
        pos-=pos-comment_start;
        inside_comment=false;
      }
      pos++;
    }
    if(inside_comment)
      file.erase(comment_start);
  }
}

void CMake::remove_newlines_inside_parentheses() {
  for(auto &file: files) {
    size_t pos=0;
    bool inside_para=false;
    bool inside_quote=false;
    char last_char=0;
    while(pos<file.size()) {
      if(!inside_quote && file[pos]=='"' && last_char!='\\')
        inside_quote=true;
      else if(inside_quote && file[pos]=='"' && last_char!='\\')
        inside_quote=false;

      else if(!inside_quote && file[pos]=='(')
        inside_para=true;
      else if(!inside_quote && file[pos]==')')
        inside_para=false;

      else if(inside_para && file[pos]=='\n')
        file.replace(pos, 1, 1, ' ');
      last_char=file[pos];
      pos++;
    }
  }
}

void CMake::find_variables() {
  for(auto &file: files) {
    size_t pos=0;
    while(pos<file.size()) {
      auto start_line=pos;
      auto end_line=file.find('\n', start_line);
      if(end_line==std::string::npos)
        end_line=file.size();
      if(end_line>start_line) {
        auto line=file.substr(start_line, end_line-start_line);
        boost::smatch sm;
        const boost::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$");
        if(boost::regex_match(line, sm, set_regex)) {
          auto data=sm[2].str();
          while(data.size()>0 && data.back()==' ')
            data.pop_back();
          parse_variable_parameters(data);
          variables[sm[1].str()]=data;
        }
        else {
          const boost::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$");
          if(boost::regex_match(line, sm, project_regex)) {
            auto data=sm[1].str();
            parse_variable_parameters(data);
            variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard?
            variables["PROJECT_NAME"]=data;
          }
        }
      }
      pos=end_line+1;
    }
  }
}

void CMake::parse_variable_parameters(std::string &data) {
  size_t pos=0;
  bool inside_quote=false;
  char last_char=0;
  while(pos<data.size()) {
    if(!inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=true;
      data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
      pos--;
    }
    else if(inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=false;
      data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
      pos--;
    }
    else if(!inside_quote && data[pos]==' ' && pos+1<data.size() && data[pos+1]==' ') {
      data.erase(pos, 1);
      pos--;
    }

    last_char=data[pos];
    pos++;
  }
  for(auto &var: variables) {
    auto pos=data.find("${"+var.first+'}');
    while(pos!=std::string::npos) {
      data.replace(pos, var.first.size()+3, var.second);
      pos=data.find("${"+var.first+'}');
    }
  }

  //Remove variables we do not know:
  pos=data.find("${");
  auto pos_end=data.find("}", pos+2);
  while(pos!=std::string::npos && pos_end!=std::string::npos) {
    data.erase(pos, pos_end-pos+1);
    pos=data.find("${");
    pos_end=data.find("}", pos+2);
  }
}

void CMake::parse() {
  read_files();
  remove_tabs();
  remove_comments();
  remove_newlines_inside_parentheses();
  find_variables();
  parsed=true;
}

std::vector<std::string> CMake::get_function_parameters(std::string &data) {
  std::vector<std::string> parameters;
  size_t pos=0;
  size_t parameter_pos=0;
  bool inside_quote=false;
  char last_char=0;
  while(pos<data.size()) {
    if(!inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=true;
      data.erase(pos, 1);
      pos--;
    }
    else if(inside_quote && data[pos]=='"' && last_char!='\\') {
      inside_quote=false;
      data.erase(pos, 1);
      pos--;
    }
    else if(!inside_quote && pos+1<data.size() && data[pos]==' ' && data[pos+1]==' ') {
      data.erase(pos, 1);
      pos--;
    }
    else if(!inside_quote && data[pos]==' ') {
      parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos));
      if(pos+1<data.size())
        parameter_pos=pos+1;
    }

    last_char=data[pos];
    pos++;
  }
  parameters.emplace_back(data.substr(parameter_pos));
  for(auto &var: variables) {
    for(auto &parameter: parameters) {
      auto pos=parameter.find("${"+var.first+'}');
      while(pos!=std::string::npos) {
        parameter.replace(pos, var.first.size()+3, var.second);
        pos=parameter.find("${"+var.first+'}');
      }
    }
  }
  return parameters;
}

std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) {
  if(!parsed)
    parse();
  std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
  size_t file_c=0;
  for(auto &file: files) {
    size_t pos=0;
    while(pos<file.size()) {
      auto start_line=pos;
      auto end_line=file.find('\n', start_line);
      if(end_line==std::string::npos)
        end_line=file.size();
      if(end_line>start_line) {
        auto line=file.substr(start_line, end_line-start_line);
        const boost::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$");
        boost::smatch sm;
        if(boost::regex_match(line, sm, function_regex)) {
          auto data=sm[1].str();
          while(data.size()>0 && data.back()==' ')
            data.pop_back();
          auto parameters=get_function_parameters(data);
          functions.emplace(functions.begin(), paths[file_c], parameters);
        }
      }
      pos=end_line+1;
    }
    file_c++;
  }
  return functions;
}
<|endoftext|>


<commit_before>/* The MIT License (MIT)
 *
 * Copyright (c) 2015 Hironori Ishibashi
 *
 * 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 main.cpp
 * @author Hironori Ishibashi
 * @brief メイン関数。
 */

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <memory>
#include <fstream>

#include "sayuri.h"

// デバッグスイッチ。
// #define SAYURI_DEBUG_dd1bb50e_83bf_4b24_af8b_7c7bf60bc063

/**
 * UCI出力を標準出力に出力するコールバック関数。
 * @param message UCIShellからのメッセージ。
 */
void Print(const std::string& message) {
  std::cout << message << std::endl;
}

/**
 * 実行ループ。
 * @param shell 実行するUCIShell。
 */
void Run(Sayuri::UCIShell& shell) {
  while (true) {
    std::string input;
    std::getline(std::cin, input);

    if (input == "quit") {
      break;
    } else {
      shell.InputCommand(input);
    }
  }
}

/**
 * メイン関数。
 * @param argc コマンドの引数の数。
 * @param argv コマンドの引数。
 * @return 終了コード。
 */
int main(int argc, char* argv[]) {
#if defined(SAYURI_DEBUG_dd1bb50e_83bf_4b24_af8b_7c7bf60bc063)
  // デバッグ用。
  return Sayuri::DebugMain(argc, argv);

#else
  if ((argc >= 2)
  && (std::strcmp(argv[1], "--help") == 0)) {
    // ヘルプの表示。
    std::string usage_str =
R"...(Usage:
    $ sayuri [option]

[option]:
    --version
        Show version.

    --help
        Show help.

    --sayulisp <file name>
        Run Sayuri as Sayulisp Interpreter.
        <file name> is Sayulisp script.
        If <file name> is '-', Sayuri reads script from standard input.)...";
    std::cout << usage_str << std::endl;
  } else if ((argc >= 2)
  && (std::strcmp(argv[1], "--version") == 0)) {
    // バージョン番号の表示。
    std::cout << Sayuri::ID_NAME << std::endl;
  } else if ((argc >= 2)
  && (std::strcmp(argv[1], "--sayulisp") == 0)) {
    // Sayulispモード。
    // 引数がもう一つ必要。
    if (argc < 3) {
      std::cerr << "Insufficient arguments. '--sayulisp' needs <file name>."
      << std::endl;
      return 1;
    }

    // エンジン初期化。
    Sayuri::Init();

    // Sayurlispを作成。
    std::unique_ptr<Sayuri::Sayulisp>
    sayulisp_ptr(new Sayuri::Sayulisp());

    // 入力ストリームを得る。
    std::istream* stream_ptr = nullptr;
    std::ifstream file;
    if (std::strcmp(argv[2], "-") == 0) {
      stream_ptr = &(std::cin);
    } else {
      file.open(argv[2]);
      if (!file) {
        std::cerr << "Couldn't open '" << argv[2] << "'." << std::endl;
        return 1;
      }
      stream_ptr = &file;
    }

    // 実行。
    int status = 0;
    try {
      status = sayulisp_ptr->Run(stream_ptr);
    } catch (Sayuri::LispObjectPtr error) {
      return 1;
    }

    // 終了。
    if (file) file.close();
    return status;
  } else {
    // プログラムの起動。
    // 初期化。
    Sayuri::Init();

    // エンジン準備。
    std::unique_ptr<Sayuri::SearchParams>
    search_params_ptr(new Sayuri::SearchParams());

    std::unique_ptr<Sayuri::EvalParams>
    eval_params_ptr(new Sayuri::EvalParams());

    std::unique_ptr<Sayuri::TranspositionTable>
    table_ptr(new Sayuri::TranspositionTable(Sayuri::UCI_DEFAULT_TABLE_SIZE));

    std::unique_ptr<Sayuri::ChessEngine>
    engine_ptr(new Sayuri::ChessEngine(*search_params_ptr, *eval_params_ptr,
    *table_ptr));

    std::unique_ptr<Sayuri::UCIShell>
    shell_ptr(new Sayuri::UCIShell(*engine_ptr));

    shell_ptr->AddOutputListener(Print);

    // エンジン起動。
    Run(*shell_ptr);

    // 後処理。
    Sayuri::Postprocess();
  }

  return EXIT_SUCCESS;
#endif
}
<commit_msg>--licenseオプションでライセンス条文を表示するようにした<commit_after>/* The MIT License (MIT)
 *
 * Copyright (c) 2015 Hironori Ishibashi
 *
 * 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 main.cpp
 * @author Hironori Ishibashi
 * @brief メイン関数。
 */

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <memory>
#include <fstream>

#include "sayuri.h"

// デバッグスイッチ。
// #define SAYURI_DEBUG_dd1bb50e_83bf_4b24_af8b_7c7bf60bc063

/**
 * UCI出力を標準出力に出力するコールバック関数。
 * @param message UCIShellからのメッセージ。
 */
void Print(const std::string& message) {
  std::cout << message << std::endl;
}

/**
 * 実行ループ。
 * @param shell 実行するUCIShell。
 */
void Run(Sayuri::UCIShell& shell) {
  while (true) {
    std::string input;
    std::getline(std::cin, input);

    if (input == "quit") {
      break;
    } else {
      shell.InputCommand(input);
    }
  }
}

/**
 * メイン関数。
 * @param argc コマンドの引数の数。
 * @param argv コマンドの引数。
 * @return 終了コード。
 */
int main(int argc, char* argv[]) {
#if defined(SAYURI_DEBUG_dd1bb50e_83bf_4b24_af8b_7c7bf60bc063)
  // デバッグ用。
  return Sayuri::DebugMain(argc, argv);

#else
  if ((argc >= 2)
  && (std::strcmp(argv[1], "--help") == 0)) {
    // ヘルプの表示。
    std::string usage_str =
R"...(Usage:
    $ sayuri [option]

[option]:
    --version
        Shows version.

    --license
        Shows license terms.

    --help
        Shows help.

    --sayulisp <file name>
        Runs Sayuri as Sayulisp Interpreter.
        <file name> is Sayulisp script.
        If <file name> is '-', Sayuri reads script from standard input.)...";
    std::cout << usage_str << std::endl;
  } else if ((argc >= 2)
  && (std::strcmp(argv[1], "--version") == 0)) {
    // バージョン番号の表示。
    std::cout << Sayuri::ID_NAME << std::endl;
  } else if ((argc >= 2)
  && (std::strcmp(argv[1], "--license") == 0)) {
    // ライセンス分の表示。
    std::cout << Sayuri::LICENSE << std::endl;
  } else if ((argc >= 2)
  && (std::strcmp(argv[1], "--sayulisp") == 0)) {
    // Sayulispモード。
    // 引数がもう一つ必要。
    if (argc < 3) {
      std::cerr << "Insufficient arguments. '--sayulisp' needs <file name>."
      << std::endl;
      return 1;
    }

    // エンジン初期化。
    Sayuri::Init();

    // Sayurlispを作成。
    std::unique_ptr<Sayuri::Sayulisp>
    sayulisp_ptr(new Sayuri::Sayulisp());

    // 入力ストリームを得る。
    std::istream* stream_ptr = nullptr;
    std::ifstream file;
    if (std::strcmp(argv[2], "-") == 0) {
      stream_ptr = &(std::cin);
    } else {
      file.open(argv[2]);
      if (!file) {
        std::cerr << "Couldn't open '" << argv[2] << "'." << std::endl;
        return 1;
      }
      stream_ptr = &file;
    }

    // 実行。
    int status = 0;
    try {
      status = sayulisp_ptr->Run(stream_ptr);
    } catch (Sayuri::LispObjectPtr error) {
      return 1;
    }

    // 終了。
    if (file) file.close();
    return status;
  } else {
    // プログラムの起動。
    // 初期化。
    Sayuri::Init();

    // エンジン準備。
    std::unique_ptr<Sayuri::SearchParams>
    search_params_ptr(new Sayuri::SearchParams());

    std::unique_ptr<Sayuri::EvalParams>
    eval_params_ptr(new Sayuri::EvalParams());

    std::unique_ptr<Sayuri::TranspositionTable>
    table_ptr(new Sayuri::TranspositionTable(Sayuri::UCI_DEFAULT_TABLE_SIZE));

    std::unique_ptr<Sayuri::ChessEngine>
    engine_ptr(new Sayuri::ChessEngine(*search_params_ptr, *eval_params_ptr,
    *table_ptr));

    std::unique_ptr<Sayuri::UCIShell>
    shell_ptr(new Sayuri::UCIShell(*engine_ptr));

    shell_ptr->AddOutputListener(Print);

    // エンジン起動。
    Run(*shell_ptr);

    // 後処理。
    Sayuri::Postprocess();
  }

  return EXIT_SUCCESS;
#endif
}
<|endoftext|>


<commit_before>#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <streambuf>
#include <string>
#include <tuple>
#include <vector>

//#include "addr_space.hpp"
#include "const.hpp"
#include "cpu.hpp"
#include "cursor.hpp"
#include "drawing.hpp"
#include "printer.hpp"
#include "ram.hpp"
#include "renderer.hpp"
#include "util.hpp"
#include "output.hpp"

using namespace std;

extern "C" {
	void setEnvironment();
	void resetEnvironment();
	extern volatile sig_atomic_t pleaseExit;
}

//////////////////////////
////////// VARS //////////
//////////////////////////

// Two global variables.
bool interactivieMode;
bool executionCanceled = false;

Printer printer;
Ram ram;
Cpu cpu;

// Graphic representation of the computer state.
vector<string> buffer;

int executionCounter = 0;

// Saved state of a ram.
map<AddrSpace, vector<vector<bool>>> savedRamState;

// Class for keeping track of and moving around cursor.
Cursor cursor;

//////////////////////////
/////// FUNCTIONS ////////
//////////////////////////

void drawScreen() {
	string out = Renderer::renderState(printer, ram, cpu);
	buffer = Util::splitString(out);
	int i = 0;
	for (string line : buffer) {
		printString(line.c_str(), 0, i++);
	}
}

char getCharUnderCursor() {
	return buffer.at(cursor.getY()).at(cursor.getX());
}

void highlightCursor(bool highlight) {
	char c;
	try {
		c = getCharUnderCursor();
		//fprintf(stderr, "Char under cursor %c, y %d, x %d highlight %d",
		//		c, cursor.getY(), cursor.getX(), highlight);
	} catch (int e) {
		cout << "Cursor out of bounds. Exception Nr. " << e << '\n';
		return;
	}
	if (highlight) {
		printf("\e[%dm\e[%dm", 30, 47);
	}
	printCharImediately(c, cursor.getX(), cursor.getY());
	if (highlight) {
		printf("\e[%dm\e[%dm", 37, 40);
	}
	fflush(stdout);
}

void switchBitUnderCursor() {
	bool bitValue = cursor.getBit();
	cursor.setBit(!bitValue);
	buffer.at(cursor.getY()).at(cursor.getX()) =
		Util::getChar(!bitValue);
}

void eraseByteUnderCursor() {
	cursor.setWord(Util::getBoolByte(0));
	redrawScreen();
}

char readStdin(bool drawCursor) {
	char c = 0;
	errno = 0;
	ssize_t num = read(0, &c, 1);
	if (num == -1 && errno == EINTR) {
		// Exits if ctrl-c was pressed.
		if (pleaseExit) {
			exit(0);
		}
		redrawScreen();
		if (drawCursor) {
			highlightCursor(true);
		}
		return readStdin(drawCursor);
	}
	return c;
}

/*
 * Runs every cycle.
 */
void sleepAndCheckForKey() {
	usleep(FQ*1000);
	// Exit if ctrl-c was pressed.
	if (pleaseExit) {
		exit(0);
	}
	// Pauses execution if a key was hit, and waits for another key hit.
	if (int keyCode = Util::getKey()) {
		// If escape was pressed.
		if (keyCode == 27) {
			executionCanceled = true;
			return;
		}
		// Press key to continue.
		keyCode = readStdin(false);
		// If the key was escape.
		if (keyCode == 27) {
			executionCanceled = true;
		}
	}
}

void exec() {
	while(!executionCanceled) {
		bool shouldContinue = cpu.step();
		if (interactivieMode) {
			redrawScreen();
		}
		if (!shouldContinue) {
			return;
		}
		if (interactivieMode) {
			sleepAndCheckForKey();
		}
	}
}

/*
 * Saves the state of the ram and starts the execution of a program.
 * When execution stops, due to it reaching last address or user pressing
 * 'esc', it loads back the saved state of the ram, and resets the cpu.
 */
void run() {
	if (executionCounter > 0) {
		printer.printEmptyLine();
	}
	savedRamState = ram.state;
	exec();
	// If 'esc' was pressed then it doesn't wait for keypress at the end.
	if (executionCanceled) {
		executionCanceled = false;
	} else {
		readStdin(false);
	}
	ram = Ram();
	ram.state = savedRamState;
	cpu = Cpu();
	redrawScreen();
	executionCounter++;
}

string getFreeFileName() {
	int i = 0;
	while (Util::fileExists(SAVE_FILE_NAME + to_string(++i)));
	return SAVE_FILE_NAME + to_string(i);
}

void saveRamToFile() {
	string fileName = getFreeFileName();
	ofstream fileStream(fileName);
    fileStream << ram.getString();
    fileStream.close();
}

void userInput() {
	while(1) {
		char c = readStdin(true);
		highlightCursor(false);

		switch (c) {
			case 65:  // up
				cursor.decreaseY();
				break;
			case 66:  // down
				cursor.increaseY();
				break;
			case 67:  // right
				cursor.increaseX();
				break;
			case 68:  // left
				cursor.decreaseX();
				break;
			case 107:  // k
				cursor.moveByteUp();
				redrawScreen();
				break;
			case 106:  // j
				cursor.moveByteDown();
				redrawScreen();
				break;
			case 115:  // s
				saveRamToFile();
				break;
			case 32:  // space
			case 102:  // f
				switchBitUnderCursor();
				break;
			case 100:  // d
				eraseByteUnderCursor();
				break;
			case 9:  // tab
				cursor.switchAddressSpace();
				redrawScreen();
				break;
			case 10:  // enter
				run();
				break;
		}
		highlightCursor(true);
	}
}

/*
 * Initializes 'output.c' by sending dimensions of a 'drawing' and
 * a 'drawScreen' callback function, that output.c will use on every
 * screen redraw.
 */
void prepareOutput() {
	size_t drawingWidth = 0;
	size_t drawingHeight = 0;
	for (string line : Util::splitString(drawing)) {
		drawingWidth = std::max(drawingWidth, line.length());
		drawingHeight++;
	}
	setOutput(&drawScreen, drawingWidth, drawingHeight);
}

bool getBool(char c) {
	return c == '*';
}

void writeInstructionBitToRam(int address, int bitIndex, bool bitValue) {
	ram.state[CODE].at(address).at(bitIndex) = bitValue;
}

void writeDataBitToRam(int address, int bitIndex, bool bitValue) {
	ram.state[DATA].at(address).at(bitIndex) = bitValue;
}

void writeLineToRam(string line, int address) {
	int bitIndex = 0;
	for (char c : line) {
		if (address < RAM_SIZE) {
			writeInstructionBitToRam(address, bitIndex, getBool(c));
		} else {
			writeDataBitToRam(address-RAM_SIZE, bitIndex, getBool(c));
		}
		if (++bitIndex >= WORD_SIZE) {
			return;
		}
	}
}

void loadRamFromFileStream(ifstream* fileStream) {
	int address = 0;
	while (!fileStream->eof()) {
		string line;
    	getline(*fileStream, line);
    	bool lineEmptyOrAComment = line.empty() || line[0] == '#';
    	if (lineEmptyOrAComment) {
    		continue;
    	}
    	writeLineToRam(line, address);
		if (++address >= 2*RAM_SIZE) {
			return;
		}
  	}
}

void checkIfInputIsPiped() {
	interactivieMode = !Util::inputIsPiped();
}

void loadRamIfFileSpecified(int argc, const char* argv[]) {
	if (argc <= 1) {
		return;
	}
   	ifstream fileStream;
	fileStream.open(argv[1]);
	if (fileStream.fail()) {
    	fprintf(stderr, "Invalid filename '%s'. Aborting ram load.", argv[1]);
   	} else {
   		loadRamFromFileStream(&fileStream);
      	fileStream.close();
   	}
}

//////////////////////////
////////// MAIN //////////
//////////////////////////

void startInteractiveMode() {
	setEnvironment();
	prepareOutput();
	clearScreen();
	redrawScreen();
	highlightCursor(true);
	userInput();
}

int main(int argc, const char* argv[]) {
	srand(time(NULL));
	checkIfInputIsPiped();
	loadRamIfFileSpecified(argc, argv);
	if (interactivieMode) {
		startInteractiveMode();
	} else {
		exec();
	}
}

<commit_msg>Clears screen at startup<commit_after>#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <streambuf>
#include <string>
#include <tuple>
#include <vector>

#include "const.hpp"
#include "cpu.hpp"
#include "cursor.hpp"
#include "drawing.hpp"
#include "printer.hpp"
#include "ram.hpp"
#include "renderer.hpp"
#include "util.hpp"
#include "output.hpp"

using namespace std;

extern "C" {
	extern volatile sig_atomic_t pleaseExit;
	void setEnvironment();
	void resetEnvironment();
}

//////////////////////////
////////// VARS //////////
//////////////////////////

// Two global variables.
bool interactivieMode;
bool executionCanceled = false;

Printer printer;
Ram ram;
Cpu cpu;

// Graphic representation of the computer state.
vector<string> buffer;

int executionCounter = 0;

// Saved state of a ram.
map<AddrSpace, vector<vector<bool>>> savedRamState;

// Class for keeping track of and moving around cursor.
Cursor cursor;

//////////////////////////
/////// FUNCTIONS ////////
//////////////////////////

void drawScreen() {
	string out = Renderer::renderState(printer, ram, cpu);
	buffer = Util::splitString(out);
	int i = 0;
	for (string line : buffer) {
		printString(line.c_str(), 0, i++);
	}
}

char getCharUnderCursor() {
	return buffer.at(cursor.getY()).at(cursor.getX());
}

void highlightCursor(bool highlight) {
	char c;
	try {
		c = getCharUnderCursor();
		//fprintf(stderr, "Char under cursor %c, y %d, x %d highlight %d",
		//		c, cursor.getY(), cursor.getX(), highlight);
	} catch (int e) {
		cout << "Cursor out of bounds. Exception Nr. " << e << '\n';
		return;
	}
	if (highlight) {
		printf("\e[%dm\e[%dm", 30, 47);
	}
	printCharImediately(c, cursor.getX(), cursor.getY());
	if (highlight) {
		printf("\e[%dm\e[%dm", 37, 40);
	}
	fflush(stdout);
}

void switchBitUnderCursor() {
	bool bitValue = cursor.getBit();
	cursor.setBit(!bitValue);
	buffer.at(cursor.getY()).at(cursor.getX()) =
		Util::getChar(!bitValue);
}

void eraseByteUnderCursor() {
	cursor.setWord(Util::getBoolByte(0));
	redrawScreen();
}

char readStdin(bool drawCursor) {
	char c = 0;
	errno = 0;
	ssize_t num = read(0, &c, 1);
	if (num == -1 && errno == EINTR) {
		// Exits if ctrl-c was pressed.
		if (pleaseExit) {
			exit(0);
		}
		redrawScreen();
		if (drawCursor) {
			highlightCursor(true);
		}
		return readStdin(drawCursor);
	}
	return c;
}

/*
 * Runs every cycle.
 */
void sleepAndCheckForKey() {
	usleep(FQ*1000);
	// Exit if ctrl-c was pressed.
	if (pleaseExit) {
		exit(0);
	}
	// Pauses execution if a key was hit, and waits for another key hit.
	if (int keyCode = Util::getKey()) {
		// If escape was pressed.
		if (keyCode == 27) {
			executionCanceled = true;
			return;
		}
		// Press key to continue.
		keyCode = readStdin(false);
		// If the key was escape.
		if (keyCode == 27) {
			executionCanceled = true;
		}
	}
}

void exec() {
	while(!executionCanceled) {
		bool shouldContinue = cpu.step();
		if (interactivieMode) {
			redrawScreen();
		}
		if (!shouldContinue) {
			return;
		}
		if (interactivieMode) {
			sleepAndCheckForKey();
		}
	}
}

/*
 * Saves the state of the ram and starts the execution of a program.
 * When execution stops, due to it reaching last address or user pressing
 * 'esc', it loads back the saved state of the ram, and resets the cpu.
 */
void run() {
	if (executionCounter > 0) {
		printer.printEmptyLine();
	}
	savedRamState = ram.state;
	exec();
	// If 'esc' was pressed then it doesn't wait for keypress at the end.
	if (executionCanceled) {
		executionCanceled = false;
	} else {
		readStdin(false);
	}
	ram = Ram();
	ram.state = savedRamState;
	cpu = Cpu();
	redrawScreen();
	executionCounter++;
}

string getFreeFileName() {
	int i = 0;
	while (Util::fileExists(SAVE_FILE_NAME + to_string(++i)));
	return SAVE_FILE_NAME + to_string(i);
}

void saveRamToFile() {
	string fileName = getFreeFileName();
	ofstream fileStream(fileName);
    fileStream << ram.getString();
    fileStream.close();
}

void userInput() {
	while(1) {
		char c = readStdin(true);
		highlightCursor(false);

		switch (c) {
			case 65:  // up
				cursor.decreaseY();
				break;
			case 66:  // down
				cursor.increaseY();
				break;
			case 67:  // right
				cursor.increaseX();
				break;
			case 68:  // left
				cursor.decreaseX();
				break;
			case 107:  // k
				cursor.moveByteUp();
				redrawScreen();
				break;
			case 106:  // j
				cursor.moveByteDown();
				redrawScreen();
				break;
			case 115:  // s
				saveRamToFile();
				break;
			case 32:  // space
			case 102:  // f
				switchBitUnderCursor();
				break;
			case 100:  // d
				eraseByteUnderCursor();
				break;
			case 9:  // tab
				cursor.switchAddressSpace();
				redrawScreen();
				break;
			case 10:  // enter
				run();
				break;
		}
		highlightCursor(true);
	}
}

/*
 * Initializes 'output.c' by sending dimensions of a 'drawing' and
 * a 'drawScreen' callback function, that output.c will use on every
 * screen redraw.
 */
void prepareOutput() {
	size_t drawingWidth = 0;
	size_t drawingHeight = 0;
	for (string line : Util::splitString(drawing)) {
		drawingWidth = std::max(drawingWidth, line.length());
		drawingHeight++;
	}
	setOutput(&drawScreen, drawingWidth, drawingHeight);
}

bool getBool(char c) {
	return c == '*';
}

void writeInstructionBitToRam(int address, int bitIndex, bool bitValue) {
	ram.state[CODE].at(address).at(bitIndex) = bitValue;
}

void writeDataBitToRam(int address, int bitIndex, bool bitValue) {
	ram.state[DATA].at(address).at(bitIndex) = bitValue;
}

void writeLineToRam(string line, int address) {
	int bitIndex = 0;
	for (char c : line) {
		if (address < RAM_SIZE) {
			writeInstructionBitToRam(address, bitIndex, getBool(c));
		} else {
			writeDataBitToRam(address-RAM_SIZE, bitIndex, getBool(c));
		}
		if (++bitIndex >= WORD_SIZE) {
			return;
		}
	}
}

void loadRamFromFileStream(ifstream* fileStream) {
	int address = 0;
	while (!fileStream->eof()) {
		string line;
    	getline(*fileStream, line);
    	bool lineEmptyOrAComment = line.empty() || line[0] == '#';
    	if (lineEmptyOrAComment) {
    		continue;
    	}
    	writeLineToRam(line, address);
		if (++address >= 2*RAM_SIZE) {
			return;
		}
  	}
}

void checkIfInputIsPiped() {
	interactivieMode = !Util::inputIsPiped();
}

void loadRamIfFileSpecified(int argc, const char* argv[]) {
	if (argc <= 1) {
		return;
	}
   	ifstream fileStream;
	fileStream.open(argv[1]);
	if (fileStream.fail()) {
    	fprintf(stderr, "Invalid filename '%s'. Aborting ram load.", argv[1]);
   	} else {
   		loadRamFromFileStream(&fileStream);
      	fileStream.close();
   	}
}

//////////////////////////
////////// MAIN //////////
//////////////////////////

void startInteractiveMode() {
	setEnvironment();
	prepareOutput();
	clearScreen();
	clearScreen();
	redrawScreen();
	highlightCursor(true);
	userInput();
}

int main(int argc, const char* argv[]) {
	srand(time(NULL));
	checkIfInputIsPiped();
	loadRamIfFileSpecified(argc, argv);
	if (interactivieMode) {
		startInteractiveMode();
	} else {
		exec();
	}
}

<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <SDL.h>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "gl_core_4_4.h"
#include "util.h"

/*
 * The OpenGL DrawElementsIndirectCommand struct described in the docs
 */
struct DrawElementsIndirectCommand {
	GLuint count, instance_count, first_index,
		base_vertex, base_instance;

	DrawElementsIndirectCommand(GLuint count, GLuint instance_count, GLuint first_index,
		GLuint base_vertex, GLuint base_instance)
		: count(count), instance_count(instance_count), first_index(first_index),
		base_vertex(base_vertex), base_instance(base_instance)
	{}
};

int main(int, char**){
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
		std::cerr << "SDL_Init error: " << SDL_GetError() << "\n";
		return 1;
	}

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#ifdef DEBUG
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif

	SDL_Window *win = SDL_CreateWindow("3D Tiles", SDL_WINDOWPOS_CENTERED,
		SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL);
	SDL_GLContext context = SDL_GL_CreateContext(win);
	SDL_GL_SetSwapInterval(1);

	if (ogl_LoadFunctions() == ogl_LOAD_FAILED){
		std::cerr << "ogl load failed\n";
		SDL_GL_DeleteContext(context);
		SDL_DestroyWindow(win);
		SDL_Quit();
		return 1;
	}
	glClearColor(0.f, 0.f, 0.f, 1.f);
	glClearDepth(1.f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << "\n"
		<< "OpenGL Vendor: " << glGetString(GL_VENDOR) << "\n"
		<< "OpenGL Renderer: " << glGetString(GL_RENDERER) << "\n"
		<< "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";

#ifdef DEBUG
	glEnable(GL_DEBUG_OUTPUT);
	glDebugMessageCallback(util::gldebug_callback, NULL);
	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
#endif

	STD140Buffer<glm::mat4> viewing{2, GL_UNIFORM_BUFFER, GL_STATIC_DRAW};
	viewing.map(GL_WRITE_ONLY);
	viewing.write<0>(0) = glm::lookAt(glm::vec3{0.f, 4.f, 5.f}, glm::vec3{0.f, 0.f, 0.f},
		glm::vec3{0.f, 1.f, 0.f});
	viewing.write<0>(1) = glm::perspective(util::deg_to_rad(75.f), 640.f / 480.f, 1.f, 100.f);
	viewing.unmap();

	const std::string shader_path = util::get_resource_path("shaders");
	GLuint shader = util::load_program({std::make_tuple(GL_VERTEX_SHADER, shader_path + "vmdei_test.glsl"),
		std::make_tuple(GL_FRAGMENT_SHADER, shader_path + "fmdei_test.glsl")});
	glUseProgram(shader);
	GLuint viewing_block = glGetUniformBlockIndex(shader, "Viewing");
	glUniformBlockBinding(shader, viewing_block, 0);
	viewing.bind_base(0);

	const std::string model_path = util::get_resource_path("models");
	PackedBuffer<glm::vec3, glm::vec3, glm::vec3> vbo{0, GL_ARRAY_BUFFER, GL_STATIC_DRAW, true};
	PackedBuffer<GLushort> ebo{0, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, true};
	size_t ma_verts = 0, ma_elems = 0;
	if (!util::load_obj(model_path + "dented_tile.obj", vbo, ebo, ma_elems, &ma_verts)){
		std::cout << "Failed to load left triangle\n";
		return 1;
	}
	size_t mb_verts = 0, mb_elems = 0;
	if (!util::load_obj(model_path + "spike_tile.obj", vbo, ebo, mb_elems, &mb_verts, ma_verts, ma_elems)){
		std::cout << "Failed to load quad\n";
		return 1;
	}

	GLuint vao;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	vbo.bind();
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vbo.stride(), 0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vbo.stride(), (void*)vbo.offset(1));

	ebo.bind();

	PackedBuffer<glm::vec3, glm::mat4> attribs{2, GL_ARRAY_BUFFER, GL_STATIC_DRAW};
	attribs.map(GL_WRITE_ONLY);
	attribs.write<0>(0) = glm::vec3{1.f, 0.f, 0.f};
	attribs.write<1>(0) = glm::translate(glm::vec3{-1.f, 0.f, 0.f});
	attribs.write<0>(1) = glm::vec3{0.f, 0.f, 1.f};
	attribs.write<1>(1) = glm::translate(glm::vec3{1.f, 0.f, 0.f});
	attribs.unmap();
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, attribs.stride(), 0);
	glVertexAttribDivisor(2, 1);
	for (int i = 3; i < 7; ++i){
		glEnableVertexAttribArray(i);
		glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, attribs.stride(),
			(void*)(attribs.offset(1) + (i - 3) * sizeof(glm::vec4)));
		glVertexAttribDivisor(i, 1);
	}

	PackedBuffer<DrawElementsIndirectCommand> draw_commands{2, GL_DRAW_INDIRECT_BUFFER, GL_STATIC_DRAW};
	draw_commands.map(GL_WRITE_ONLY);
	draw_commands.write<0>(0) = DrawElementsIndirectCommand{ma_elems, 1, 0, 0, 0};
	draw_commands.write<0>(1) = DrawElementsIndirectCommand{mb_elems, 1, ma_elems, 0, 1};
	draw_commands.unmap();

	SDL_Event e;
	bool quit = false;
	while (!quit){
		while (SDL_PollEvent(&e)){
			if (e.type == SDL_QUIT || (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)){
				quit = true;
			}
		}
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, NULL,
				draw_commands.size(), draw_commands.stride());

		SDL_GL_SwapWindow(win);
	}

	glDeleteProgram(shader);
	glDeleteVertexArrays(1, &vao);

	SDL_GL_DeleteContext(context);
	SDL_DestroyWindow(win);
	SDL_Quit();
	return 0;
}

<commit_msg>Be explicit that we know that will be a reinterpret cast<commit_after>#include <iostream>
#include <string>
#include <SDL.h>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "gl_core_4_4.h"
#include "util.h"

/*
 * The OpenGL DrawElementsIndirectCommand struct described in the docs
 */
struct DrawElementsIndirectCommand {
	GLuint count, instance_count, first_index,
		base_vertex, base_instance;

	DrawElementsIndirectCommand(GLuint count, GLuint instance_count, GLuint first_index,
		GLuint base_vertex, GLuint base_instance)
		: count(count), instance_count(instance_count), first_index(first_index),
		base_vertex(base_vertex), base_instance(base_instance)
	{}
};

int main(int, char**){
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
		std::cerr << "SDL_Init error: " << SDL_GetError() << "\n";
		return 1;
	}

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#ifdef DEBUG
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif

	SDL_Window *win = SDL_CreateWindow("3D Tiles", SDL_WINDOWPOS_CENTERED,
		SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL);
	SDL_GLContext context = SDL_GL_CreateContext(win);
	SDL_GL_SetSwapInterval(1);

	if (ogl_LoadFunctions() == ogl_LOAD_FAILED){
		std::cerr << "ogl load failed\n";
		SDL_GL_DeleteContext(context);
		SDL_DestroyWindow(win);
		SDL_Quit();
		return 1;
	}
	glClearColor(0.f, 0.f, 0.f, 1.f);
	glClearDepth(1.f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << "\n"
		<< "OpenGL Vendor: " << glGetString(GL_VENDOR) << "\n"
		<< "OpenGL Renderer: " << glGetString(GL_RENDERER) << "\n"
		<< "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";

#ifdef DEBUG
	glEnable(GL_DEBUG_OUTPUT);
	glDebugMessageCallback(util::gldebug_callback, NULL);
	glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
#endif

	STD140Buffer<glm::mat4> viewing{2, GL_UNIFORM_BUFFER, GL_STATIC_DRAW};
	viewing.map(GL_WRITE_ONLY);
	viewing.write<0>(0) = glm::lookAt(glm::vec3{0.f, 4.f, 5.f}, glm::vec3{0.f, 0.f, 0.f},
		glm::vec3{0.f, 1.f, 0.f});
	viewing.write<0>(1) = glm::perspective(util::deg_to_rad(75.f), 640.f / 480.f, 1.f, 100.f);
	viewing.unmap();

	const std::string shader_path = util::get_resource_path("shaders");
	GLuint shader = util::load_program({std::make_tuple(GL_VERTEX_SHADER, shader_path + "vmdei_test.glsl"),
		std::make_tuple(GL_FRAGMENT_SHADER, shader_path + "fmdei_test.glsl")});
	glUseProgram(shader);
	GLuint viewing_block = glGetUniformBlockIndex(shader, "Viewing");
	glUniformBlockBinding(shader, viewing_block, 0);
	viewing.bind_base(0);

	const std::string model_path = util::get_resource_path("models");
	PackedBuffer<glm::vec3, glm::vec3, glm::vec3> vbo{0, GL_ARRAY_BUFFER, GL_STATIC_DRAW, true};
	PackedBuffer<GLushort> ebo{0, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, true};
	size_t ma_verts = 0, ma_elems = 0;
	if (!util::load_obj(model_path + "dented_tile.obj", vbo, ebo, ma_elems, &ma_verts)){
		std::cout << "Failed to load left triangle\n";
		return 1;
	}
	size_t mb_verts = 0, mb_elems = 0;
	if (!util::load_obj(model_path + "spike_tile.obj", vbo, ebo, mb_elems, &mb_verts, ma_verts, ma_elems)){
		std::cout << "Failed to load quad\n";
		return 1;
	}

	GLuint vao;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	vbo.bind();
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vbo.stride(), 0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vbo.stride(), reinterpret_cast<void*>(vbo.offset(1)));

	ebo.bind();

	PackedBuffer<glm::vec3, glm::mat4> attribs{2, GL_ARRAY_BUFFER, GL_STATIC_DRAW};
	attribs.map(GL_WRITE_ONLY);
	attribs.write<0>(0) = glm::vec3{1.f, 0.f, 0.f};
	attribs.write<1>(0) = glm::translate(glm::vec3{-1.f, 0.f, 0.f});
	attribs.write<0>(1) = glm::vec3{0.f, 0.f, 1.f};
	attribs.write<1>(1) = glm::translate(glm::vec3{1.f, 0.f, 0.f});
	attribs.unmap();
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, attribs.stride(), 0);
	glVertexAttribDivisor(2, 1);
	for (int i = 3; i < 7; ++i){
		glEnableVertexAttribArray(i);
		glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, attribs.stride(),
			(void*)(attribs.offset(1) + (i - 3) * sizeof(glm::vec4)));
		glVertexAttribDivisor(i, 1);
	}

	PackedBuffer<DrawElementsIndirectCommand> draw_commands{2, GL_DRAW_INDIRECT_BUFFER, GL_STATIC_DRAW};
	draw_commands.map(GL_WRITE_ONLY);
	draw_commands.write<0>(0) = DrawElementsIndirectCommand{ma_elems, 1, 0, 0, 0};
	draw_commands.write<0>(1) = DrawElementsIndirectCommand{mb_elems, 1, ma_elems, 0, 1};
	draw_commands.unmap();

	SDL_Event e;
	bool quit = false;
	while (!quit){
		while (SDL_PollEvent(&e)){
			if (e.type == SDL_QUIT || (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)){
				quit = true;
			}
		}
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, NULL,
				draw_commands.size(), draw_commands.stride());

		SDL_GL_SwapWindow(win);
	}

	glDeleteProgram(shader);
	glDeleteVertexArrays(1, &vao);

	SDL_GL_DeleteContext(context);
	SDL_DestroyWindow(win);
	SDL_Quit();
	return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <locale>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <SQLiteCpp/SQLiteCpp.h>

#define DB_PATH "/srv/http/vbus-data.db"

void find_and_replace(std::string& source, std::string const& find, std::string const& replace)
{
  for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
  {
    source.replace(i, find.length(), replace);
    i += replace.length();
  }
}

// See: http://stackoverflow.com/questions/15220861/how-can-i-set-the-comma-to-be-a-decimal-point
class punct_facet: public std::numpunct<char> {
  protected: char do_decimal_point() const { return '.'; }
};

int main(int argc, char const *argv[])
{
  // Set decimal seperator to '.'
  std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet()));

  std::cout << "Content-type: text/comma-separated-values\r\n"
            << "Access-Control-Allow-Origin: *\r\n\r\n";

  try {
    SQLite::Database db(DB_PATH);

    std::string time_query = std::string("-1 hour");

    char *cgi_query = std::getenv("QUERY_STRING");

    if (cgi_query) {
      time_query = std::string(cgi_query);
      find_and_replace(time_query, "%20", " ");
    }

    SQLite::Statement query(db, "SELECT "
                                "datetime(time, 'localtime') as time, "
                                "temp1, temp2, temp3, temp4, pump1, pump2 "
                                "FROM data "
                                "WHERE time > (SELECT DATETIME('now', ?)) "
                                "ORDER BY id");

    query.bind(1, time_query);

    //std::cout << time_query<< std::endl;

    std::cout << "Datum,Ofen,Speicher-unten,Speicher-oben,Heizung,Ventil-Ofen,Ventil-Heizung" << std::endl;

    while (query.executeStep()) {
      std::cout << query.getColumn(0).getText()   << ","
                << query.getColumn(1).getDouble() << ","
                << query.getColumn(2).getDouble() << ","
                << query.getColumn(3).getDouble() << ","
                << query.getColumn(4).getDouble() << ","
                << query.getColumn(5).getInt()    << ","
                << query.getColumn(6).getInt()    << std::endl;
    }
  }
  catch (std::exception& e) {
      return 1;
  }

  /* code */
  return 0;
}
<commit_msg>Send HTTP header to data does not get cached<commit_after>#include <iostream>
#include <locale>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <SQLiteCpp/SQLiteCpp.h>

#define DB_PATH "/srv/http/vbus-data.db"

void find_and_replace(std::string& source, std::string const& find, std::string const& replace)
{
  for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
  {
    source.replace(i, find.length(), replace);
    i += replace.length();
  }
}

// See: http://stackoverflow.com/questions/15220861/how-can-i-set-the-comma-to-be-a-decimal-point
class punct_facet: public std::numpunct<char> {
  protected: char do_decimal_point() const { return '.'; }
};

int main(int argc, char const *argv[])
{
  // Set decimal seperator to '.'
  std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet()));

  std::cout << "Content-type: text/comma-separated-values\r\n"
            << "Cache-Control: no-cache, no-store, must-revalidate\r\n"
            << "Pragma: no-cache\r\n"
            << "Expires: 0\r\n"
            << "Access-Control-Allow-Origin: *\r\n\r\n";

  try {
    SQLite::Database db(DB_PATH);

    std::string time_query = std::string("-1 hour");

    char *cgi_query = std::getenv("QUERY_STRING");

    if (cgi_query) {
      time_query = std::string(cgi_query);
      find_and_replace(time_query, "%20", " ");
    }

    SQLite::Statement query(db, "SELECT "
                                "datetime(time, 'localtime') as time, "
                                "temp1, temp2, temp3, temp4, pump1, pump2 "
                                "FROM data "
                                "WHERE time > (SELECT DATETIME('now', ?)) "
                                "ORDER BY id");

    query.bind(1, time_query);

    //std::cout << time_query<< std::endl;

    std::cout << "Datum,Ofen,Speicher-unten,Speicher-oben,Heizung,Ventil-Ofen,Ventil-Heizung" << std::endl;

    while (query.executeStep()) {
      std::cout << query.getColumn(0).getText()   << ","
                << query.getColumn(1).getDouble() << ","
                << query.getColumn(2).getDouble() << ","
                << query.getColumn(3).getDouble() << ","
                << query.getColumn(4).getDouble() << ","
                << query.getColumn(5).getInt()    << ","
                << query.getColumn(6).getInt()    << std::endl;
    }
  }
  catch (std::exception& e) {
      return 1;
  }

  /* code */
  return 0;
}
<|endoftext|>


<commit_before>#include "util/probing_hash_table.hh"

#define BOOST_TEST_MODULE ProbingHashTableTest
#include <boost/test/unit_test.hpp>
#include <boost/scoped_array.hpp>
#include <boost/functional/hash.hpp>
#include <stdio.h>
#include <stdint.h>

namespace util {
namespace {

struct Entry {
  unsigned char key;
  typedef unsigned char Key;

  unsigned char GetKey() const {
    return key;
  }

  uint64_t GetValue() const {
    return value;
  }

  uint64_t value;
};

typedef ProbingHashTable<Entry, boost::hash<unsigned char> > Table;

BOOST_AUTO_TEST_CASE(simple) {
  size_t size = Table::Size(10, 1.2);
  boost::scoped_array<char> mem(new char[size]);
  memset(mem.get(), 0, size);

  Table table(mem.get(), size);
  const Entry *i = NULL;
  BOOST_CHECK(!table.Find(2, i));
  Entry to_ins;
  to_ins.key = 3;
  to_ins.value = 328920;
  table.Insert(to_ins);
  BOOST_REQUIRE(table.Find(3, i));
  BOOST_CHECK_EQUAL(3, i->GetKey());
  BOOST_CHECK_EQUAL(static_cast<uint64_t>(328920), i->GetValue());
  BOOST_CHECK(!table.Find(2, i));
}

} // namespace
} // namespace util
<commit_msg>Missing #include <string.h> Yvette<commit_after>#include "util/probing_hash_table.hh"

#define BOOST_TEST_MODULE ProbingHashTableTest
#include <boost/test/unit_test.hpp>
#include <boost/scoped_array.hpp>
#include <boost/functional/hash.hpp>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

namespace util {
namespace {

struct Entry {
  unsigned char key;
  typedef unsigned char Key;

  unsigned char GetKey() const {
    return key;
  }

  uint64_t GetValue() const {
    return value;
  }

  uint64_t value;
};

typedef ProbingHashTable<Entry, boost::hash<unsigned char> > Table;

BOOST_AUTO_TEST_CASE(simple) {
  size_t size = Table::Size(10, 1.2);
  boost::scoped_array<char> mem(new char[size]);
  memset(mem.get(), 0, size);

  Table table(mem.get(), size);
  const Entry *i = NULL;
  BOOST_CHECK(!table.Find(2, i));
  Entry to_ins;
  to_ins.key = 3;
  to_ins.value = 328920;
  table.Insert(to_ins);
  BOOST_REQUIRE(table.Find(3, i));
  BOOST_CHECK_EQUAL(3, i->GetKey());
  BOOST_CHECK_EQUAL(static_cast<uint64_t>(328920), i->GetValue());
  BOOST_CHECK(!table.Find(2, i));
}

} // namespace
} // namespace util
<|endoftext|>


<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Copyright: 2010-2011 Razor team
 * Authors:
 *   Petr Vanek <petr@scribus.info>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include <LXQt/SingleApplication>
#include <QSettings>
#include "mainwindow.h"

int main(int argc, char **argv)
{
    LXQt::SingleApplication app(argc, argv);
    app.setOrganizationName(QStringLiteral("lxqt"));
    app.setApplicationName(QStringLiteral("lxqt-config"));
    app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);

    // ensure that we use lxqt-config.menu file.
    qputenv("XDG_MENU_PREFIX", "lxqt-");

    LXQtConfig::MainWindow w;
    app.setActivationWindow(&w);
    QSize s = QSettings{}.value("size").toSize();
    if (!s.isEmpty())
        w.resize(s);
    w.show();

    int ret = app.exec();

    QSettings{}.setValue("size", w.size());

    return ret;
}
<commit_msg>Added QCommandLineParser and basic cli interface to lxqt-config<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Copyright: 2010-2011 Razor team
 * Authors:
 *   Petr Vanek <petr@scribus.info>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include <LXQt/SingleApplication>
#include <QSettings>
#include "mainwindow.h"
#include "QCommandLineParser"

int main(int argc, char **argv)
{
    LXQt::SingleApplication app(argc, argv);
    app.setOrganizationName(QStringLiteral("lxqt"));
    app.setApplicationName(QStringLiteral("lxqt-config"));
    app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);

    QCommandLineParser parser;
    parser.setApplicationDescription(QStringLiteral("LXQt Config"));
    const QString VERINFO = LXQT_CONFIG_VERSION \
                            "\n\nliblxqt:   " LXQT_VERSION \
                            "\nQt:        " QT_VERSION_STR;
    app.setApplicationVersion(VERINFO);
    parser.addVersionOption();
    parser.addHelpOption();
    parser.process(app);

    // ensure that we use lxqt-config.menu file.
    qputenv("XDG_MENU_PREFIX", "lxqt-");

    LXQtConfig::MainWindow w;
    app.setActivationWindow(&w);
    QSize s = QSettings{}.value("size").toSize();
    if (!s.isEmpty())
        w.resize(s);
    w.show();

    int ret = app.exec();

    QSettings{}.setValue("size", w.size());

    return ret;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <queue>
#include <locale>
#include <map>
#include <dmusic/PlayingContext.h>
#include <dmusic/InstrumentPlayer.h>
#include <dmusic/Tracks.h>
#include <dmusic/dls/DownloadableSound.h>
#include <cassert>
#include <sndfile.h>
#include <cmath>
#define TSF_IMPLEMENTATION
#include "tsf.h"

using namespace DirectMusic;
using namespace DirectMusic::DLS;

class MyInstrumentPlayer : public InstrumentPlayer {
private:
    int m_preset;
    tsf* m_soundfont;
    float m_vol;
    float m_pan;
    int m_channels;
    float gainToDecibels(float gain) {
        return 10 * log10(gain);
    }
public:
    MyInstrumentPlayer(std::map<std::string, tsf*> soundfonts,
        std::uint8_t bankLo, std::uint8_t bankHi, std::uint8_t patch,
        const DownloadableSound& dls,
        std::uint32_t sampleRate,
        std::uint32_t channels,
        float volume,
        float pan)
        : InstrumentPlayer(bankLo, bankHi, patch, dls, sampleRate, channels, volume, pan)
        , m_soundfont(nullptr)
        , m_vol(volume)
        , m_pan(pan)
        , m_channels(channels) {
        assert(channels <= 2);
        std::uint32_t bank = (bankHi << 16) + bankLo;

        std::string soundfontFile = (dls.getInfo().getName() + ".sf2");
        if (soundfonts.find(soundfontFile) == soundfonts.end()) {
            m_soundfont = tsf_load_filename(soundfontFile.c_str());
            assert(m_soundfont != nullptr);
            TSFOutputMode outputMode = channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED;
            tsf_set_output(m_soundfont, outputMode, sampleRate, gainToDecibels(volume) - 3);
            pan = pan < -1 ? -1 : pan > 1 ? 1 : pan;
            float volFactorRight = sqrt((pan + 1) / 2);
            float volFactorLeft = sqrt((-pan + 1) / 2);
            tsf_set_panning(m_soundfont, volFactorLeft, volFactorRight);

            soundfonts[soundfontFile] = m_soundfont;
        } else {
            m_soundfont = soundfonts.at(soundfontFile);
        }
        m_preset = tsf_get_presetindex(m_soundfont, 0, patch);
        m_preset = 0;
        assert(m_preset >= 0);
    }

    virtual std::uint32_t renderBlock(std::int16_t *buffer, std::uint32_t count, float volume) noexcept {
        tsf_render_short(m_soundfont, buffer, count / m_channels, 1);
        return count;
    }

    /// Instructs the synthesizer to start playing a note
    virtual void noteOn(std::uint8_t note, std::uint8_t velocity) {
        tsf_note_on(m_soundfont, m_preset, note, velocity / 255.0f);
    }

    /// Instructs the synthesizer to stop playing a note
    virtual void noteOff(std::uint8_t note, std::uint8_t velocity) {
        tsf_note_off(m_soundfont, m_preset, note);
    }

    /// Sends a "channel pressure" message
    virtual void channelPressure(std::uint8_t val) {}

    /// Sends a "polyphonic aftertouch" message to a note
    virtual void polyAftertouch(std::uint8_t note, std::uint8_t val) {}

    /// Sends a "control change" message
    virtual void controlChange(DirectMusic::Midi::Control control, std::int32_t val) {}

    /// Sends a "program change" (aka "patch change") message
    virtual void programChange(std::uint8_t program) {}

    /// Sends a "pitch bend" message
    virtual void pitchBend(std::int16_t val) {}
};

int main(int argc, char **argv) {
    if (argc < 3) {
        std::cerr << "Usage: dmrender [inputfile] [outputfile] <length in seconds>\n";
        return 1;
    }

    // Store soundfonts based on their name
    std::map<std::string, tsf*> soundfonts;
    PlayingContext ctx(44100, 1, [soundfonts](std::uint8_t bankLo, std::uint8_t bankHi, std::uint8_t patch,
        const DownloadableSound& dls, std::uint32_t sampleRate, std::uint32_t channels, float vol, float pan) {
        return std::static_pointer_cast<InstrumentPlayer>(std::make_shared<MyInstrumentPlayer>(soundfonts, bankLo, bankHi, patch, dls, sampleRate, channels, vol, pan));
    });
    std::cout << "Loading segment...";
    auto segment = ctx.loadSegment(argv[1]);
    std::cout << " done.\nBeginning rendering... ";

    ctx.playSegment(*segment);
    int sampleRate = 44100;
    std::uint64_t length = 60 * sampleRate * 1; // Render 60 seconds of sound if nothing else is specified, 2 channels
    if (argc > 3) {
        length = std::stoi(argv[3]) * sampleRate * 1;
    }

    // Each instrument is going to sum its output into the buffer,
    // so we have to start from a blank state, hence calloc
    std::int16_t* buffer = (std::int16_t*)calloc(length, sizeof(std::int16_t));
    for (std::uint64_t i = 0; i < length; i += sampleRate) {
        ctx.renderBlock(buffer + i, sampleRate);
        std::cout << ceil((i / (float)length) * 100) << "% ";
    }

    // Close all soundfont handles
    for (const auto& kvp : soundfonts) {
        tsf_close(kvp.second);
    }

    SF_INFO info;
    info.channels = 1;
    info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    info.samplerate = 44100;
    info.frames = 0;
    info.sections = 0;
    info.seekable = 0;
    SNDFILE* sndfile = sf_open(argv[2], SFM_WRITE, &info);
    sf_write_short(sndfile, buffer, length);
    sf_close(sndfile);
    free(buffer);
    std::cout << "Rendering done.";
    return 0;
}
<commit_msg>dmrender: Arbitrary number of channels<commit_after>#include <iostream>
#include <queue>
#include <locale>
#include <map>
#include <dmusic/PlayingContext.h>
#include <dmusic/InstrumentPlayer.h>
#include <dmusic/Tracks.h>
#include <dmusic/dls/DownloadableSound.h>
#include <cassert>
#include <sndfile.h>
#include <cmath>
#define TSF_IMPLEMENTATION
#include "tsf.h"

using namespace DirectMusic;
using namespace DirectMusic::DLS;

class MyInstrumentPlayer : public InstrumentPlayer {
private:
    int m_preset;
    tsf* m_soundfont;
    float m_vol;
    float m_pan;
    int m_channels;
    float gainToDecibels(float gain) {
        return 10 * log10(gain);
    }
public:
    MyInstrumentPlayer(std::map<std::string, tsf*> soundfonts,
        std::uint8_t bankLo, std::uint8_t bankHi, std::uint8_t patch,
        const DownloadableSound& dls,
        std::uint32_t sampleRate,
        std::uint32_t channels,
        float volume,
        float pan)
        : InstrumentPlayer(bankLo, bankHi, patch, dls, sampleRate, channels, volume, pan)
        , m_soundfont(nullptr)
        , m_vol(volume)
        , m_pan(pan)
        , m_channels(channels) {
        assert(channels <= 2);
        std::uint32_t bank = (bankHi << 16) + bankLo;

        std::string soundfontFile = (dls.getInfo().getName() + ".sf2");
        if (soundfonts.find(soundfontFile) == soundfonts.end()) {
            m_soundfont = tsf_load_filename(soundfontFile.c_str());
            assert(m_soundfont != nullptr);
            TSFOutputMode outputMode = channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED;
            tsf_set_output(m_soundfont, outputMode, sampleRate, gainToDecibels(volume) - 3);
            pan = pan < -1 ? -1 : pan > 1 ? 1 : pan;
            float volFactorRight = sqrt((pan + 1) / 2);
            float volFactorLeft = sqrt((-pan + 1) / 2);
            tsf_set_panning(m_soundfont, volFactorLeft, volFactorRight);

            soundfonts[soundfontFile] = m_soundfont;
        } else {
            m_soundfont = soundfonts.at(soundfontFile);
        }
        m_preset = tsf_get_presetindex(m_soundfont, 0, patch);
        m_preset = 0;
        assert(m_preset >= 0);
    }

    virtual std::uint32_t renderBlock(std::int16_t *buffer, std::uint32_t count, float volume) noexcept {
        tsf_render_short(m_soundfont, buffer, count / m_channels, 1);
        return count;
    }

    /// Instructs the synthesizer to start playing a note
    virtual void noteOn(std::uint8_t note, std::uint8_t velocity) {
        tsf_note_on(m_soundfont, m_preset, note, velocity / 255.0f);
    }

    /// Instructs the synthesizer to stop playing a note
    virtual void noteOff(std::uint8_t note, std::uint8_t velocity) {
        tsf_note_off(m_soundfont, m_preset, note);
    }

    /// Sends a "channel pressure" message
    virtual void channelPressure(std::uint8_t val) {}

    /// Sends a "polyphonic aftertouch" message to a note
    virtual void polyAftertouch(std::uint8_t note, std::uint8_t val) {}

    /// Sends a "control change" message
    virtual void controlChange(DirectMusic::Midi::Control control, std::int32_t val) {}

    /// Sends a "program change" (aka "patch change") message
    virtual void programChange(std::uint8_t program) {}

    /// Sends a "pitch bend" message
    virtual void pitchBend(std::int16_t val) {}
};

int main(int argc, char **argv) {
    if (argc < 3) {
        std::cerr << "Usage: dmrender [inputfile] [outputfile] <length in seconds> <channels>\n";
        return 1;
    }

    int sampleRate = 44100;
    int channels = 1;
    std::uint64_t length = 60 * sampleRate; // Render 60 seconds of sound if nothing else is specified
    if (argc > 3) {
        length = std::stoi(argv[3]) * sampleRate;
    }

    if (argc > 4) {
        channels = std::stoi(argv[4]);
        length = length * channels;
    }

    // Store soundfonts based on their name
    std::map<std::string, tsf*> soundfonts;
    PlayingContext ctx(44100, channels, [soundfonts](std::uint8_t bankLo, std::uint8_t bankHi, std::uint8_t patch,
        const DownloadableSound& dls, std::uint32_t sampleRate, std::uint32_t chans, float vol, float pan) {
        return std::static_pointer_cast<InstrumentPlayer>(std::make_shared<MyInstrumentPlayer>(soundfonts, bankLo, bankHi, patch, dls, sampleRate, chans, vol, pan));
    });
    std::cout << "Loading segment...";
    auto segment = ctx.loadSegment(argv[1]);
    std::cout << " done.\nStart playback... ";
    ctx.playSegment(*segment);
    std::cout << " done.\nBegin rendering... ";

    // Each instrument is going to sum its output into the buffer,
    // so we have to start from a blank state, hence calloc
    std::int16_t* buffer = (std::int16_t*)calloc(length, sizeof(std::int16_t));
    for (std::uint64_t i = 0; i < length; i += sampleRate) {
        ctx.renderBlock(buffer + i, sampleRate);
        std::cout << ceil((i / (float)length) * 100) << "% ";
    }

    // Close all soundfont handles
    for (const auto& kvp : soundfonts) {
        tsf_close(kvp.second);
    }

    SF_INFO info;
    info.channels = channels;
    info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    info.samplerate = 44100;
    info.frames = 0;
    info.sections = 0;
    info.seekable = 0;
    SNDFILE* sndfile = sf_open(argv[2], SFM_WRITE, &info);
    sf_write_short(sndfile, buffer, length);
    sf_close(sndfile);
    free(buffer);
    std::cout << "Rendering done.";
    return 0;
}
<|endoftext|>


<commit_before>#pragma once
#include "bits.hpp"
#include <algorithm>
#include <iostream>
#include "optional.hpp"
#include "exception.hpp"

namespace spn {
	//! 任意の型の縦横サイズ
	template <class T>
	struct _Size {
		T	width, height;

		_Size() = default;
		_Size(const T& s): width(s), height(s) {}
		_Size(const T& w, const T& h): width(w), height(h) {}
		template <class T2>
		_Size(const _Size<T2>& s): width(s.width), height(s.height) {}
		_Size& operator *= (const T& s) {
			return *this = *this * s; }
		_Size& operator /= (const T& s) {
			return *this = *this / s; }
		_Size operator / (const T& s) const {
			return _Size(width/s, height/s); }
		_Size operator * (const T& s) const {
			return _Size(width*s, height*s); }
		template <class V>
		void shiftR(int n, typename std::enable_if<std::is_integral<V>::value>::type* = nullptr) {
			width >>= n;
			height >>= n;
		}
		template <class V>
		void shiftR(int n, typename std::enable_if<!std::is_integral<V>::value>::type* = nullptr) { *this *= std::pow(2.f, n); }
		_Size& operator >>= (int n) {
			shiftR<T>(n);
			return *this;
		}
		//! 指定したビット数分、右シフトしてもし値がゼロになったら1をセットする
		void shiftR_one(int n) {
			width >>= n;
			width |= ~Bit::ZeroOrFull(width) & 0x01;
			height >>= n;
			height |= ~Bit::ZeroOrFull(height) & 0x01;
		}
		//! 1右ビットシフトした値を2の累乗に合わせる
		void shiftR_2pow() {
			auto fn = [](const T& t) -> T {
				using UT = std::make_unsigned_t<T>;
				UT ut(t);
				auto v = Bit::LowClear(ut);
				if(v != ut)
					return v;
				return v >> 1;
			};
			width = fn(width);
			height = fn(height);
		}
		bool operator == (const _Size& s) const {
			return width == s.width &&
					height == s.height;
		}
		bool operator != (const _Size& s) const { return !(this->operator == (s)); }
		bool operator <= (const _Size& s) const {
			return width <= s.width &&
					height <= s.height;
		}
		//! 領域の拡大縮小
		/*!
			負数で領域の縮小
			\return		縮小によって面積が0になった場合はtrue, それ以外はfalse
		*/
		bool expand(const T& w, const T& h) {
			bool ret = false;
			width += w;
			height += h;
			if(width <= 0) {
				width = 0;
				ret = true;
			}
			if(height <= 0) {
				height = 0;
				ret = true;
			}
			return ret;
		}
		bool expand(const T& w) {
			return expand(w,w);
		}
		template <class V>
		auto toSize() const {
			return _Size<V>(width, height);
		}
	};
	template <class T>
	std::ostream& operator << (std::ostream& os, const spn::_Size<T>& s) {
		return os << "Size {width=" << s.width << ", height=" << s.height << "}";
	}
	using Size = _Size<int32_t>;
	using SizeF = _Size<float>;

	//! 任意の型の2D矩形
	template <class T>
	class _Rect {
		private:
			template <class E>
			void _checkValidnessT() const {
				if(x1 < x0 ||
					y1 < y0)
					throw E((boost::format("invalid region: x1(%1%)<x0(%2%) or y1(%3%)<y0(%4%)") % x1 % x0 % y1 % y0).str());
			}
		protected:
			void _checkValidness() const {
				#ifdef DEBUG
					_checkValidnessT<ValidationFail>();
				#endif
			}
		public:
			union {
				struct {
					T	x0, x1, y0, y1;
				};
				T	ar[4];
			};

			_Rect() = default;
			_Rect(const _Rect& r) = default;
			_Rect(const T& x_0, const T& x_1, const T& y_0, const T& y_1):
				x0(x_0), x1(x_1), y0(y_0), y1(y_1)
			{
				_checkValidness();
			}

			T width() const {
				_checkValidness();
				return x1-x0;
			}
			T height() const {
				_checkValidness();
				return y1-y0;
			}
			void checkValidness() const {
				_checkValidnessT<ValidationFailRuntime>();
			}
			void addOffset(const T& x, const T& y) {
				_checkValidness();
				x0 += x;
				x1 += x;
				y0 += y;
				y1 += y;
			}
			bool hit(const _Rect& r) const {
				_checkValidness();
				return !(r.x1 <= x0 ||
						r.x0 >= x1 ||
						r.y1 <= y0 ||
						r.y0 >= y1);
			}
			//! 面積
			T area() const {
				return width() * height();
			}
			_Rect move(const T& dx, const T& dy) const {
				return
					_Rect(
						x0 + dx,
						x1 + dx,
						y0 + dy,
						y1 + dy
					);
			}
			void shrinkRight(const T& s) {
				x1 = std::max(x0, x1-s);
			}
			void shrinkBottom(const T& s) {
				y1 = std::max(y0, y1-s);
			}
			void shrinkLeft(const T& s) {
				x0 = std::min(x1, x0+s);
			}
			void shrinkTop(const T& s) {
				y0 = std::min(y1, y0+s);
			}
			_Size<T> size() const {
				return _Size<T>(width(), height());
			}
			#define DEF_OP(op) \
				_Rect& operator op##= (const T& t) { \
					return *this = *this op t; } \
				_Rect operator op (const T& t) const { \
					_Rect r; \
					for(int i=0 ; i<int(countof(ar)) ; i++) \
						r.ar[i] = ar[i] op t; \
					return r; \
				}
			DEF_OP(+)
			DEF_OP(-)
			DEF_OP(*)
			DEF_OP(/)
			#undef DEF_OP
			//! 領域の拡大縮小
			/*!
				負数で領域の縮小
				\return		縮小によって面積が0になった場合はtrue, それ以外はfalse
			*/
			bool expand(const T& w, const T& h) {
				bool ret = false;
				x0 -= w;
				x1 += w;
				y0 -= h;
				y1 += h;
				if(x0 >= x1) {
					auto d = x0 - x1;
					x1 += d/2;
					x0 = x1;
					ret = true;
				}
				if(y0 >= y1) {
					auto d = y0 - y1;
					y1 += d/2;
					y0 = y1;
					ret = true;
				}
				return ret;
			}
			bool expand(const T& w) {
				return expand(w,w);
			}
			template <class V>
			auto toRect() const {
				return _Rect<V>(x0, x1, y0,y1);
			}
			bool operator == (const _Rect& r) const {
				return x0 == r.x0 &&
						x1 == r.x1 &&
						y0 == r.y0 &&
						y1 == r.y1;
			}
			bool operator != (const _Rect& r) const {
				return !(operator == (r));
			}
			//! 重なっている領域を算出
			Optional<_Rect> cross(const _Rect& r) const {
				if(hit(r)) {
					return _Rect(
								std::max(x0, r.x0),
								std::min(x1, r.x1),
								std::max(y0, r.y0),
								std::min(y1, r.y1)
							);
				}
				return spn::none;
			}
	};
	using Rect = _Rect<int32_t>;
	using RectF = _Rect<float>;
	template <class T>
	std::ostream& operator << (std::ostream& os, const spn::_Rect<T>& r) {
		return os << "Rect {x0=" << r.x0 << ", x1=" << r.x1 <<
				", y0=" << r.y0 << ", y1=" << r.y1 << "}";
	}

	//! 値の低い方に補正
	template <class T>
	struct PP_Lower {
		static T proc(const T& t) {
			T ret = spn::Bit::LowClear(t);
			return std::max(T(1), ret);
		}
	};
	//! 値の高い方に補正
	template <class T>
	struct PP_Higher {
		static T proc(const T& t) {
			T ret = spn::Bit::LowClear(t);
			if(t & ~ret)
				ret <<= 1;
			return std::max(T(1), ret);
		}
	};

	//! 2の乗数しか代入できない型
	/*! 0も許可されない */
	template <class T, template <class> class Policy>
	class PowValue {
		using P = Policy<T>;
		T _value;

		public:
			//TODO: テンプレートによる静的な値補正
			PowValue(const T& t): _value(P::proc(t)) {}
			PowValue(const PowValue& p) = default;

			PowValue& operator = (const PowValue& p) = default;
			PowValue& operator = (const T& t) {
				// 2の乗数に補正 (動的なチェック)
				_value = P::proc(t);
				return *this;
			}
			const T& get() const { return _value; }
			operator const T& () const { return _value; }
	};
	using PowInt = PowValue<uint32_t, PP_Higher>;
	using PowSize = _Size<PowInt>;
	template <class... Ts>
	std::ostream& operator << (std::ostream& os, const spn::PowValue<Ts...>& p) {
		return os << p.get();
	}
}
<commit_msg>Rect: sizeとの四則演算<commit_after>#pragma once
#include "bits.hpp"
#include <algorithm>
#include <iostream>
#include "optional.hpp"
#include "exception.hpp"

namespace spn {
	//! 任意の型の縦横サイズ
	template <class T>
	struct _Size {
		T	width, height;

		_Size() = default;
		_Size(const T& s): width(s), height(s) {}
		_Size(const T& w, const T& h): width(w), height(h) {}
		template <class T2>
		_Size(const _Size<T2>& s): width(s.width), height(s.height) {}
		_Size& operator *= (const T& s) {
			return *this = *this * s; }
		_Size& operator /= (const T& s) {
			return *this = *this / s; }
		_Size operator / (const T& s) const {
			return _Size(width/s, height/s); }
		_Size operator * (const T& s) const {
			return _Size(width*s, height*s); }
		template <class V>
		void shiftR(int n, typename std::enable_if<std::is_integral<V>::value>::type* = nullptr) {
			width >>= n;
			height >>= n;
		}
		template <class V>
		void shiftR(int n, typename std::enable_if<!std::is_integral<V>::value>::type* = nullptr) { *this *= std::pow(2.f, n); }
		_Size& operator >>= (int n) {
			shiftR<T>(n);
			return *this;
		}
		//! 指定したビット数分、右シフトしてもし値がゼロになったら1をセットする
		void shiftR_one(int n) {
			width >>= n;
			width |= ~Bit::ZeroOrFull(width) & 0x01;
			height >>= n;
			height |= ~Bit::ZeroOrFull(height) & 0x01;
		}
		//! 1右ビットシフトした値を2の累乗に合わせる
		void shiftR_2pow() {
			auto fn = [](const T& t) -> T {
				using UT = std::make_unsigned_t<T>;
				UT ut(t);
				auto v = Bit::LowClear(ut);
				if(v != ut)
					return v;
				return v >> 1;
			};
			width = fn(width);
			height = fn(height);
		}
		bool operator == (const _Size& s) const {
			return width == s.width &&
					height == s.height;
		}
		bool operator != (const _Size& s) const { return !(this->operator == (s)); }
		bool operator <= (const _Size& s) const {
			return width <= s.width &&
					height <= s.height;
		}
		//! 領域の拡大縮小
		/*!
			負数で領域の縮小
			\return		縮小によって面積が0になった場合はtrue, それ以外はfalse
		*/
		bool expand(const T& w, const T& h) {
			bool ret = false;
			width += w;
			height += h;
			if(width <= 0) {
				width = 0;
				ret = true;
			}
			if(height <= 0) {
				height = 0;
				ret = true;
			}
			return ret;
		}
		bool expand(const T& w) {
			return expand(w,w);
		}
		template <class V>
		auto toSize() const {
			return _Size<V>(width, height);
		}
	};
	template <class T>
	std::ostream& operator << (std::ostream& os, const spn::_Size<T>& s) {
		return os << "Size {width=" << s.width << ", height=" << s.height << "}";
	}
	using Size = _Size<int32_t>;
	using SizeF = _Size<float>;

	//! 任意の型の2D矩形
	template <class T>
	class _Rect {
		private:
			template <class E>
			void _checkValidnessT() const {
				if(x1 < x0 ||
					y1 < y0)
					throw E((boost::format("invalid region: x1(%1%)<x0(%2%) or y1(%3%)<y0(%4%)") % x1 % x0 % y1 % y0).str());
			}
		protected:
			void _checkValidness() const {
				#ifdef DEBUG
					_checkValidnessT<ValidationFail>();
				#endif
			}
		public:
			union {
				struct {
					T	x0, x1, y0, y1;
				};
				T	ar[4];
			};

			_Rect() = default;
			_Rect(const _Rect& r) = default;
			_Rect(const T& x_0, const T& x_1, const T& y_0, const T& y_1):
				x0(x_0), x1(x_1), y0(y_0), y1(y_1)
			{
				_checkValidness();
			}

			T width() const {
				_checkValidness();
				return x1-x0;
			}
			T height() const {
				_checkValidness();
				return y1-y0;
			}
			void checkValidness() const {
				_checkValidnessT<ValidationFailRuntime>();
			}
			void addOffset(const T& x, const T& y) {
				_checkValidness();
				x0 += x;
				x1 += x;
				y0 += y;
				y1 += y;
			}
			bool hit(const _Rect& r) const {
				_checkValidness();
				return !(r.x1 <= x0 ||
						r.x0 >= x1 ||
						r.y1 <= y0 ||
						r.y0 >= y1);
			}
			//! 面積
			T area() const {
				return width() * height();
			}
			_Rect move(const T& dx, const T& dy) const {
				return
					_Rect(
						x0 + dx,
						x1 + dx,
						y0 + dy,
						y1 + dy
					);
			}
			void shrinkRight(const T& s) {
				x1 = std::max(x0, x1-s);
			}
			void shrinkBottom(const T& s) {
				y1 = std::max(y0, y1-s);
			}
			void shrinkLeft(const T& s) {
				x0 = std::min(x1, x0+s);
			}
			void shrinkTop(const T& s) {
				y0 = std::min(y1, y0+s);
			}
			_Size<T> size() const {
				return _Size<T>(width(), height());
			}
			#define DEF_OP(op) \
				template <class V> \
				_Rect& operator op##= (const V& t) { \
					return *this = *this op t; } \
				_Rect operator op (const T& t) const { \
					_Rect r; \
					for(int i=0 ; i<int(countof(ar)) ; i++) \
						r.ar[i] = ar[i] op t; \
					return r; \
				} \
				template <class V> \
				_Rect operator op (const _Size<V>& s) const { \
					return {x0 op s.width, x1 op s.width, \
							y0 op s.height, y1 op s.height}; \
				}
			DEF_OP(+)
			DEF_OP(-)
			DEF_OP(*)
			DEF_OP(/)
			#undef DEF_OP

			//! 領域の拡大縮小
			/*!
				負数で領域の縮小
				\return		縮小によって面積が0になった場合はtrue, それ以外はfalse
			*/
			bool expand(const T& w, const T& h) {
				bool ret = false;
				x0 -= w;
				x1 += w;
				y0 -= h;
				y1 += h;
				if(x0 >= x1) {
					auto d = x0 - x1;
					x1 += d/2;
					x0 = x1;
					ret = true;
				}
				if(y0 >= y1) {
					auto d = y0 - y1;
					y1 += d/2;
					y0 = y1;
					ret = true;
				}
				return ret;
			}
			bool expand(const T& w) {
				return expand(w,w);
			}
			template <class V>
			auto toRect() const {
				return _Rect<V>(x0, x1, y0,y1);
			}
			bool operator == (const _Rect& r) const {
				return x0 == r.x0 &&
						x1 == r.x1 &&
						y0 == r.y0 &&
						y1 == r.y1;
			}
			bool operator != (const _Rect& r) const {
				return !(operator == (r));
			}
			//! 重なっている領域を算出
			Optional<_Rect> cross(const _Rect& r) const {
				if(hit(r)) {
					return _Rect(
								std::max(x0, r.x0),
								std::min(x1, r.x1),
								std::max(y0, r.y0),
								std::min(y1, r.y1)
							);
				}
				return spn::none;
			}
	};
	using Rect = _Rect<int32_t>;
	using RectF = _Rect<float>;
	template <class T>
	std::ostream& operator << (std::ostream& os, const spn::_Rect<T>& r) {
		return os << "Rect {x0=" << r.x0 << ", x1=" << r.x1 <<
				", y0=" << r.y0 << ", y1=" << r.y1 << "}";
	}

	//! 値の低い方に補正
	template <class T>
	struct PP_Lower {
		static T proc(const T& t) {
			T ret = spn::Bit::LowClear(t);
			return std::max(T(1), ret);
		}
	};
	//! 値の高い方に補正
	template <class T>
	struct PP_Higher {
		static T proc(const T& t) {
			T ret = spn::Bit::LowClear(t);
			if(t & ~ret)
				ret <<= 1;
			return std::max(T(1), ret);
		}
	};

	//! 2の乗数しか代入できない型
	/*! 0も許可されない */
	template <class T, template <class> class Policy>
	class PowValue {
		using P = Policy<T>;
		T _value;

		public:
			//TODO: テンプレートによる静的な値補正
			PowValue(const T& t): _value(P::proc(t)) {}
			PowValue(const PowValue& p) = default;

			PowValue& operator = (const PowValue& p) = default;
			PowValue& operator = (const T& t) {
				// 2の乗数に補正 (動的なチェック)
				_value = P::proc(t);
				return *this;
			}
			const T& get() const { return _value; }
			operator const T& () const { return _value; }
	};
	using PowInt = PowValue<uint32_t, PP_Higher>;
	using PowSize = _Size<PowInt>;
	template <class... Ts>
	std::ostream& operator << (std::ostream& os, const spn::PowValue<Ts...>& p) {
		return os << p.get();
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018, Rauli Laine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <fstream>
#include <unistd.h>

#include <peelo/unicode.hpp>

#include "laskin/context.hpp"
#include "laskin/error.hpp"
#include "laskin/quote.hpp"

namespace laskin
{
  void run_repl(context&);
}

static void run_file(laskin::context&, std::istream&);

int main(int argc, char** argv)
{
  laskin::context context;

  if (argc > 1)
  {
    for (int i = 1; i < argc; ++i)
    {
      std::ifstream input(argv[i], std::ios_base::in);

      if (input.good())
      {
        run_file(context, input);
      } else {
        std::cerr << argv[0]
                  << ": Unable to open file `"
                  << argv[i]
                  << "' for reading."
                  << std::endl;
        std::exit(EXIT_FAILURE);
      }
    }
  }
  else if (isatty(fileno(stdin)))
  {
    laskin::run_repl(context);
  } else {
    run_file(context, std::cin);
  }

  return EXIT_SUCCESS;
}

static void run_file(laskin::context& context, std::istream& input)
{
  const auto source = peelo::unicode::utf8::decode(
    std::string(
      std::istreambuf_iterator<char>(input),
      std::istreambuf_iterator<char>()
    )
  );

  try
  {
    laskin::quote::parse(source).call(context, std::cout);
  }
  catch (const laskin::error& error)
  {
    std::cerr << error << std::endl;
    std::exit(EXIT_FAILURE);
  }
}
<commit_msg>Add some command line arguments<commit_after>/*
 * Copyright (c) 2018, Rauli Laine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <fstream>
#include <unistd.h>

#include <peelo/unicode.hpp>

#include "laskin/context.hpp"
#include "laskin/error.hpp"
#include "laskin/quote.hpp"

static std::string programfile;
static std::vector<std::u32string> inline_scripts;

namespace laskin
{
  void run_repl(context&);
}

static void run_file(laskin::context&, std::istream&);
static void parse_args(int, char**);
static void print_usage(std::ostream&, const char*);

int main(int argc, char** argv)
{
  laskin::context context;

  parse_args(argc, argv);

  if (!inline_scripts.empty())
  {
    for (const auto& source : inline_scripts)
    {
      try
      {
        laskin::quote::parse(source).call(context, std::cout);
      }
      catch (const laskin::error& error)
      {
        std::cerr << error << std::endl;
        std::exit(EXIT_FAILURE);
      }
    }
  }
  else if (!programfile.empty())
  {
    std::ifstream input(programfile, std::ios_base::in);

    if (!input.good())
    {
      std::cerr << argv[0]
                << ": Unable to open file `"
                << programfile
                << "' for reading."
                << std::endl;
      std::exit(EXIT_FAILURE);
    }
    run_file(context, input);
  }
  else if (isatty(fileno(stdin)))
  {
    laskin::run_repl(context);
  } else {
    run_file(context, std::cin);
  }

  return EXIT_SUCCESS;
}

static void run_file(laskin::context& context, std::istream& input)
{
  const auto source = peelo::unicode::utf8::decode(
    std::string(
      std::istreambuf_iterator<char>(input),
      std::istreambuf_iterator<char>()
    )
  );

  try
  {
    laskin::quote::parse(source).call(context, std::cout);
  }
  catch (const laskin::error& error)
  {
    std::cerr << error << std::endl;
    std::exit(EXIT_FAILURE);
  }
}

static void parse_args(int argc, char** argv)
{
  int offset = 1;

  while (offset < argc)
  {
    auto arg = argv[offset++];

    if (!*arg)
    {
      continue;
    }
    else if (*arg != '-')
    {
      programfile = arg;
      break;
    }
    else if (!arg[1])
    {
      break;
    }
    else if (arg[1] == '-')
    {
      if (!std::strcmp(arg, "--help"))
      {
        print_usage(std::cout, argv[0]);
        std::exit(EXIT_SUCCESS);
      }
      else if (!std::strcmp(arg, "--version"))
      {
        std::cerr << "Laskin 2.0.0" << std::endl;
        std::exit(EXIT_SUCCESS);
      } else {
        std::cerr << "Unrecognized switch: " << arg << std::endl;
        print_usage(std::cerr, argv[0]);
        std::exit(EXIT_FAILURE);
      }
    }
    for (int i = 1; arg[i]; ++i)
    {
      switch (arg[i])
      {
        case 'e':
          if (offset < argc)
          {
            std::u32string script;

            if (!peelo::unicode::utf8::decode_validate(argv[offset++], script))
            {
              std::cerr << "Unable to decode given inline script as UTF-8."
                        << std::endl;
              std::exit(EXIT_FAILURE);
            }
            inline_scripts.push_back(script);
          } else {
            std::cerr << "Argument expected for the -e option." << std::endl;
            print_usage(std::cerr, argv[0]);
            std::exit(EXIT_FAILURE);
          }
          break;

        case 'h':
          print_usage(std::cout, argv[0]);
          std::exit(EXIT_SUCCESS);
          break;

        default:
          std::cerr << "Unrecognized switch: `" << arg[i] << "'" << std::endl;
          std::exit(EXIT_FAILURE);
          break;
      }
    }
  }

  if (offset < argc)
  {
    std::cerr << "Too many arguments given." << std::endl;
    print_usage(std::cerr, argv[0]);
    std::exit(EXIT_FAILURE);
  }
}

static void print_usage(std::ostream& output, const char* executable_name)
{
  output << std::endl
         << "Usage: "
         << executable_name
         << " [switches] [programfile]"
         << std::endl
         << "  -e program        One line of program. (Omit programfile.)"
         << std::endl
         << "  --version         Print the version."
         << std::endl
         << "  --help            Display this message."
         << std::endl
         << std::endl;
}
<|endoftext|>