<commit_before>/*
 * Copyright 2002-2013 Jose Fonseca
 *
 * This 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.
 *
 * 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
 */

#include <assert.h>
#include <stdlib.h>

#include <string>

#include <windows.h>
#include <tlhelp32.h>

#include <process.h>

#include "getopt.h"
#include "debugger.h"
#include "symbols.h"
#include "dialog.h"
#include "errmsg.h"
#include "log.h"
#include "outdbg.h"

static int process_id_given = 0; /* Whether process-id was given.  */
static int install_given = 0;    /* Whether install was given.  */
static int uninstall_given = 0;  /* Whether uninstall was given.  */

static void
help(void)
{
    MessageBoxA(NULL,
                "Usage: drmingw [OPTIONS]\r\n"
                "\r\n"
                "Options:\r\n"
                "  -h, --help\tPrint help and exit\r\n"
                "  -V, --version\tPrint version and exit\r\n"
                "  -i, --install\tInstall as the default JIT debugger\r\n"
                "  -u, --uninstall\tUninstall\r\n"
                "  -pLONG, --process-id=LONG\r\n"
                "\t\tAttach to the process with the given identifier\r\n"
                "  -eLONG, --event=LONG\r\n"
                "\t\tSignal an event after process is attached\r\n"
                "  -b, --breakpoint\tTreat debug breakpoints as exceptions\r\n"
                "  -v, --verbose\tVerbose output\r\n"
                "  -d, --debug\tDebug output\r\n",
                PACKAGE, MB_OK | MB_ICONINFORMATION);
}

static LSTATUS
regSetStr(HKEY hKey, LPCSTR lpValueName, LPCSTR szStr)
{
    return RegSetValueExA(hKey, lpValueName, 0, REG_SZ, reinterpret_cast<LPCBYTE>(szStr),
                          strlen(szStr) + 1);
}

static DWORD
install(REGSAM samDesired)
{
    char szFile[MAX_PATH];
    if (!GetModuleFileNameA(NULL, szFile, MAX_PATH)) {
        return GetLastError();
    }

    std::string debuggerCommand;
    debuggerCommand += '"';
    debuggerCommand += szFile;
    debuggerCommand += "\" -p %ld -e %ld";
    if (debugOptions.verbose_flag)
        debuggerCommand += " -v";
    if (debugOptions.breakpoint_flag)
        debuggerCommand += " -b";
    if (debugOptions.debug_flag)
        debuggerCommand += " -d";

    HKEY hKey;
    long lRet;
    DWORD dwDisposition;

    lRet =
        RegCreateKeyExA(HKEY_LOCAL_MACHINE,
                        "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // The AeDebug
                                                                                // registry key.
                        0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | samDesired, NULL, &hKey,
                        &dwDisposition);
    if (lRet != ERROR_SUCCESS) {
        return lRet;
    }

    // Write the Debugger value.
    lRet = regSetStr(hKey, "Debugger", debuggerCommand.c_str());
    if (lRet == ERROR_SUCCESS) {
        // Write the Auto value.
        lRet = regSetStr(hKey, "Auto", "1");
    }

    // Close the key no matter what happened.
    RegCloseKey(hKey);

    return lRet;
}

static DWORD
uninstall(REGSAM samDesired)
{
    HKEY hKey;
    long lRet;

    lRet =
        RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                      "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // The AeDebug
                                                                                // registry key.
                      0, KEY_ALL_ACCESS | samDesired, &hKey);
    if (lRet != ERROR_SUCCESS) {
        return lRet;
    }

    // Write the Debugger value.
    lRet = regSetStr(hKey, "Debugger", "");

    // Leave Auto value as "1".  It is the default
    // (https://docs.microsoft.com/en-us/windows/desktop/Debug/configuring-automatic-debugging)
    // and setting it to "0" doesn't seem to work as documented on Windows 10
    // (https://github.com/jrfonseca/drmingw/issues/38)

    // Close the key no matter what happened.
    RegCloseKey(hKey);

    return lRet;
}

static DWORD
getProcessIdByName(const char *szProcessName)
{
    DWORD dwProcessId = 0;

    HANDLE hProcessSnap;
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof pe32;
        if (Process32First(hProcessSnap, &pe32)) {
            do {
                if (stricmp(szProcessName, pe32.szExeFile) == 0) {
                    dwProcessId = pe32.th32ProcessID;
                    break;
                }
            } while (Process32Next(hProcessSnap, &pe32));
        }
        CloseHandle(hProcessSnap);
    }

    return dwProcessId;
}

static void
debugThread(void *arg)
{
    DWORD dwProcessId = (DWORD)(UINT_PTR)arg;

    // attach debuggee
    if (!DebugActiveProcess(dwProcessId)) {
        ErrorMessageBox("DebugActiveProcess: %s", LastErrorMessage());
        return;
    }

    setDumpCallback(appendText);

    SetSymOptions(debugOptions.debug_flag);

    DebugMainLoop();
}

int
main(int argc, char **argv)
{
    DWORD dwProcessId = 0;
    int c; /* Character of the parsed option.  */

    debugOptions.first_chance = 1;

    while (1) {
        int option_index = 0;
        static const struct option long_options[] = {{"help", 0, NULL, 'h'},
                                                     {"version", 0, NULL, 'V'},
                                                     {"install", 0, NULL, 'i'},
                                                     {"uninstall", 0, NULL, 'u'},
                                                     {"process-id", 1, NULL, 'p'},
                                                     {"event", 1, NULL, 'e'},
                                                     {"tid", 1, NULL, 't'},
                                                     {"breakpoint", 0, NULL, 'b'},
                                                     {"verbose", 0, NULL, 'v'},
                                                     {"debug", 0, NULL, 'd'},
                                                     {NULL, 0, NULL, 0}};

        c = getopt_long_only(argc, argv, "?hViup:e:t:vbd", long_options, &option_index);

        if (c == -1)
            break; /* Exit from `while (1)' loop.  */

        switch (c) {
        case '?':
            if (optopt != '?') {
                /* Invalid option.  */
                char szErrMsg[512];
                sprintf(szErrMsg, "Invalid option '%c'", optopt);
                MessageBoxA(NULL, szErrMsg, PACKAGE, MB_OK | MB_ICONSTOP);
                return 1;
            }
            /* fall-through */
        case 'h': /* Print help and exit.  */
            help();
            return 0;

        case 'V': /* Print version and exit.  */
            MessageBoxA(NULL, PACKAGE " " VERSION, PACKAGE, MB_OK | MB_ICONINFORMATION);
            return 0;

        case 'i': /* Install as the default JIT debugger.  */
            if (uninstall_given) {
                MessageBoxA(NULL, "conficting options `--uninstall' (`-u') and `--install' (`-i')",
                            PACKAGE, MB_OK | MB_ICONSTOP);
                return 0;
            }
            install_given = 1;
            break;

        case 'u': /* Uninstall.  */
            if (install_given) {
                MessageBoxA(NULL, "conficting options `--install' (`-i') and `--uninstall' (`-u')",
                            PACKAGE, MB_OK | MB_ICONSTOP);
                return 0;
            }
            uninstall_given = 1;
            break;

        case 'p': /* Attach to the process with the given identifier.  */
            if (process_id_given) {
                MessageBoxA(NULL, "`--process-id' (`-p') option redeclared", PACKAGE,
                            MB_OK | MB_ICONSTOP);
                return 1;
            }
            process_id_given = 1;
            if (optarg[0] >= '0' && optarg[0] <= '9') {
                dwProcessId = strtoul(optarg, NULL, 0);
            } else {
                debugOptions.breakpoint_flag = true;
                dwProcessId = getProcessIdByName(optarg);
            }
            if (!dwProcessId) {
                MessageBoxA(NULL, "Invalid process", PACKAGE, MB_OK | MB_ICONSTOP);
                return 1;
            }
            break;

        case 'e': /* Signal an event after process is attached.  */
            if (debugOptions.hEvent) {
                MessageBoxA(NULL, "`--event' (`-e') option redeclared", PACKAGE,
                            MB_OK | MB_ICONSTOP);
                return 1;
            }
            debugOptions.hEvent = (HANDLE)(INT_PTR)atol(optarg);
            break;

        case 't': {
            /* Thread id.  Used when debugging WinRT apps. */
            debugOptions.dwThreadId = strtoul(optarg, NULL, 0);
            break;
        }

        case 'b': /* Treat debug breakpoints as exceptions */
            debugOptions.breakpoint_flag = true;
            break;

        case 'v': /* Verbose output.  */
            debugOptions.verbose_flag = true;
            break;

        case 'd': /* Debug output.  */
            debugOptions.debug_flag = true;
            break;

        default: /* bug: option not considered.  */
        {
            char szErrMsg[512];
            sprintf(szErrMsg, "Unexpected option '-%c'", c);
            MessageBoxA(NULL, szErrMsg, PACKAGE, MB_OK | MB_ICONSTOP);
            return 1;
        }
        }
    }

    if (install_given) {
        DWORD dwRet = install(0);
#if defined(_WIN64)
        if (dwRet == ERROR_SUCCESS) {
            dwRet = install(KEY_WOW64_32KEY);
        }
#endif
        if (dwRet != ERROR_SUCCESS) {
            MessageBoxA(NULL,
                        dwRet == ERROR_ACCESS_DENIED
                            ? "You must have administrator privileges to install Dr. Mingw as the "
                              "default application debugger"
                            : "Unexpected error when trying to install Dr. Mingw as the default "
                              "application debugger",
                        "DrMingw", MB_OK | MB_ICONERROR);
            return 1;
        }
        MessageBoxA(NULL, "Dr. Mingw has been installed as the default application debugger",
                    "DrMingw", MB_OK | MB_ICONINFORMATION);
        return 0;
    }

    if (uninstall_given) {
        DWORD dwRet = uninstall(0);
#if defined(_WIN64)
        if (dwRet == ERROR_SUCCESS) {
            dwRet = uninstall(KEY_WOW64_32KEY);
        }
#endif
        if (dwRet != ERROR_SUCCESS) {
            MessageBoxA(NULL,
                        dwRet == ERROR_ACCESS_DENIED
                            ? "You must have administrator privileges to uninstall Dr. Mingw as "
                              "the default application debugger"
                            : "Unexpected error when trying to uninstall Dr. Mingw as the default "
                              "application debugger",
                        "DrMingw", MB_OK | MB_ICONERROR);
            return 1;
        }
        MessageBoxA(NULL, "Dr. Mingw has been uninstalled", "DrMingw", MB_OK | MB_ICONINFORMATION);
        return 0;
    }

    if (process_id_given) {
        SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);

        if (!ObtainSeDebugPrivilege())
            MessageBoxA(NULL,
                        "An error occurred while obtaining debug privileges.\nDrMingw will not "
                        "debug system processes.",
                        "DrMingw", MB_OK | MB_ICONERROR);

        createDialog();

        _beginthread(debugThread, 0, (void *)(UINT_PTR)dwProcessId);

        return mainLoop();
    } else {
        help();
    }

    return 0;
}
<commit_msg>Show version and copyright in Dr. Mingw usage dialog<commit_after>/*
 * Copyright 2002-2013 Jose Fonseca
 *
 * This 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.
 *
 * 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
 */

#include <assert.h>
#include <stdlib.h>

#include <string>

#include <windows.h>
#include <tlhelp32.h>

#include <process.h>

#include "getopt.h"
#include "debugger.h"
#include "symbols.h"
#include "dialog.h"
#include "errmsg.h"
#include "log.h"
#include "outdbg.h"
#include "version.h"

static int process_id_given = 0; /* Whether process-id was given.  */
static int install_given = 0;    /* Whether install was given.  */
static int uninstall_given = 0;  /* Whether uninstall was given.  */

static void
help(void)
{
    MessageBoxA(NULL,
                VER_PRODUCTNAME_STR " version " VER_PRODUCTVERSION_STR "\r\n"
                VER_LEGALCOPYRIGHT_STR "\r\n"
                "\r\n"
                "Usage: drmingw [OPTIONS]\r\n"
                "\r\n"
                "Options:\r\n"
                "  -h, --help\tPrint help and exit\r\n"
                "  -V, --version\tPrint version and exit\r\n"
                "  -i, --install\tInstall as the default JIT debugger\r\n"
                "  -u, --uninstall\tUninstall\r\n"
                "  -pLONG, --process-id=LONG\r\n"
                "\t\tAttach to the process with the given identifier\r\n"
                "  -eLONG, --event=LONG\r\n"
                "\t\tSignal an event after process is attached\r\n"
                "  -b, --breakpoint\tTreat debug breakpoints as exceptions\r\n"
                "  -v, --verbose\tVerbose output\r\n"
                "  -d, --debug\tDebug output\r\n",
                PACKAGE, MB_OK | MB_ICONINFORMATION);
}

static LSTATUS
regSetStr(HKEY hKey, LPCSTR lpValueName, LPCSTR szStr)
{
    return RegSetValueExA(hKey, lpValueName, 0, REG_SZ, reinterpret_cast<LPCBYTE>(szStr),
                          strlen(szStr) + 1);
}

static DWORD
install(REGSAM samDesired)
{
    char szFile[MAX_PATH];
    if (!GetModuleFileNameA(NULL, szFile, MAX_PATH)) {
        return GetLastError();
    }

    std::string debuggerCommand;
    debuggerCommand += '"';
    debuggerCommand += szFile;
    debuggerCommand += "\" -p %ld -e %ld";
    if (debugOptions.verbose_flag)
        debuggerCommand += " -v";
    if (debugOptions.breakpoint_flag)
        debuggerCommand += " -b";
    if (debugOptions.debug_flag)
        debuggerCommand += " -d";

    HKEY hKey;
    long lRet;
    DWORD dwDisposition;

    lRet =
        RegCreateKeyExA(HKEY_LOCAL_MACHINE,
                        "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // The AeDebug
                                                                                // registry key.
                        0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | samDesired, NULL, &hKey,
                        &dwDisposition);
    if (lRet != ERROR_SUCCESS) {
        return lRet;
    }

    // Write the Debugger value.
    lRet = regSetStr(hKey, "Debugger", debuggerCommand.c_str());
    if (lRet == ERROR_SUCCESS) {
        // Write the Auto value.
        lRet = regSetStr(hKey, "Auto", "1");
    }

    // Close the key no matter what happened.
    RegCloseKey(hKey);

    return lRet;
}

static DWORD
uninstall(REGSAM samDesired)
{
    HKEY hKey;
    long lRet;

    lRet =
        RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                      "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // The AeDebug
                                                                                // registry key.
                      0, KEY_ALL_ACCESS | samDesired, &hKey);
    if (lRet != ERROR_SUCCESS) {
        return lRet;
    }

    // Write the Debugger value.
    lRet = regSetStr(hKey, "Debugger", "");

    // Leave Auto value as "1".  It is the default
    // (https://docs.microsoft.com/en-us/windows/desktop/Debug/configuring-automatic-debugging)
    // and setting it to "0" doesn't seem to work as documented on Windows 10
    // (https://github.com/jrfonseca/drmingw/issues/38)

    // Close the key no matter what happened.
    RegCloseKey(hKey);

    return lRet;
}

static DWORD
getProcessIdByName(const char *szProcessName)
{
    DWORD dwProcessId = 0;

    HANDLE hProcessSnap;
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof pe32;
        if (Process32First(hProcessSnap, &pe32)) {
            do {
                if (stricmp(szProcessName, pe32.szExeFile) == 0) {
                    dwProcessId = pe32.th32ProcessID;
                    break;
                }
            } while (Process32Next(hProcessSnap, &pe32));
        }
        CloseHandle(hProcessSnap);
    }

    return dwProcessId;
}

static void
debugThread(void *arg)
{
    DWORD dwProcessId = (DWORD)(UINT_PTR)arg;

    // attach debuggee
    if (!DebugActiveProcess(dwProcessId)) {
        ErrorMessageBox("DebugActiveProcess: %s", LastErrorMessage());
        return;
    }

    setDumpCallback(appendText);

    SetSymOptions(debugOptions.debug_flag);

    DebugMainLoop();
}

int
main(int argc, char **argv)
{
    DWORD dwProcessId = 0;
    int c; /* Character of the parsed option.  */

    debugOptions.first_chance = 1;

    while (1) {
        int option_index = 0;
        static const struct option long_options[] = {{"help", 0, NULL, 'h'},
                                                     {"version", 0, NULL, 'V'},
                                                     {"install", 0, NULL, 'i'},
                                                     {"uninstall", 0, NULL, 'u'},
                                                     {"process-id", 1, NULL, 'p'},
                                                     {"event", 1, NULL, 'e'},
                                                     {"tid", 1, NULL, 't'},
                                                     {"breakpoint", 0, NULL, 'b'},
                                                     {"verbose", 0, NULL, 'v'},
                                                     {"debug", 0, NULL, 'd'},
                                                     {NULL, 0, NULL, 0}};

        c = getopt_long_only(argc, argv, "?hViup:e:t:vbd", long_options, &option_index);

        if (c == -1)
            break; /* Exit from `while (1)' loop.  */

        switch (c) {
        case '?':
            if (optopt != '?') {
                /* Invalid option.  */
                char szErrMsg[512];
                sprintf(szErrMsg, "Invalid option '%c'", optopt);
                MessageBoxA(NULL, szErrMsg, PACKAGE, MB_OK | MB_ICONSTOP);
                return 1;
            }
            /* fall-through */
        case 'h': /* Print help and exit.  */
            help();
            return 0;

        case 'V': /* Print version and exit.  */
            MessageBoxA(NULL, PACKAGE " " VERSION, PACKAGE, MB_OK | MB_ICONINFORMATION);
            return 0;

        case 'i': /* Install as the default JIT debugger.  */
            if (uninstall_given) {
                MessageBoxA(NULL, "conficting options `--uninstall' (`-u') and `--install' (`-i')",
                            PACKAGE, MB_OK | MB_ICONSTOP);
                return 0;
            }
            install_given = 1;
            break;

        case 'u': /* Uninstall.  */
            if (install_given) {
                MessageBoxA(NULL, "conficting options `--install' (`-i') and `--uninstall' (`-u')",
                            PACKAGE, MB_OK | MB_ICONSTOP);
                return 0;
            }
            uninstall_given = 1;
            break;

        case 'p': /* Attach to the process with the given identifier.  */
            if (process_id_given) {
                MessageBoxA(NULL, "`--process-id' (`-p') option redeclared", PACKAGE,
                            MB_OK | MB_ICONSTOP);
                return 1;
            }
            process_id_given = 1;
            if (optarg[0] >= '0' && optarg[0] <= '9') {
                dwProcessId = strtoul(optarg, NULL, 0);
            } else {
                debugOptions.breakpoint_flag = true;
                dwProcessId = getProcessIdByName(optarg);
            }
            if (!dwProcessId) {
                MessageBoxA(NULL, "Invalid process", PACKAGE, MB_OK | MB_ICONSTOP);
                return 1;
            }
            break;

        case 'e': /* Signal an event after process is attached.  */
            if (debugOptions.hEvent) {
                MessageBoxA(NULL, "`--event' (`-e') option redeclared", PACKAGE,
                            MB_OK | MB_ICONSTOP);
                return 1;
            }
            debugOptions.hEvent = (HANDLE)(INT_PTR)atol(optarg);
            break;

        case 't': {
            /* Thread id.  Used when debugging WinRT apps. */
            debugOptions.dwThreadId = strtoul(optarg, NULL, 0);
            break;
        }

        case 'b': /* Treat debug breakpoints as exceptions */
            debugOptions.breakpoint_flag = true;
            break;

        case 'v': /* Verbose output.  */
            debugOptions.verbose_flag = true;
            break;

        case 'd': /* Debug output.  */
            debugOptions.debug_flag = true;
            break;

        default: /* bug: option not considered.  */
        {
            char szErrMsg[512];
            sprintf(szErrMsg, "Unexpected option '-%c'", c);
            MessageBoxA(NULL, szErrMsg, PACKAGE, MB_OK | MB_ICONSTOP);
            return 1;
        }
        }
    }

    if (install_given) {
        DWORD dwRet = install(0);
#if defined(_WIN64)
        if (dwRet == ERROR_SUCCESS) {
            dwRet = install(KEY_WOW64_32KEY);
        }
#endif
        if (dwRet != ERROR_SUCCESS) {
            MessageBoxA(NULL,
                        dwRet == ERROR_ACCESS_DENIED
                            ? "You must have administrator privileges to install Dr. Mingw as the "
                              "default application debugger"
                            : "Unexpected error when trying to install Dr. Mingw as the default "
                              "application debugger",
                        "DrMingw", MB_OK | MB_ICONERROR);
            return 1;
        }
        MessageBoxA(NULL, "Dr. Mingw has been installed as the default application debugger",
                    "DrMingw", MB_OK | MB_ICONINFORMATION);
        return 0;
    }

    if (uninstall_given) {
        DWORD dwRet = uninstall(0);
#if defined(_WIN64)
        if (dwRet == ERROR_SUCCESS) {
            dwRet = uninstall(KEY_WOW64_32KEY);
        }
#endif
        if (dwRet != ERROR_SUCCESS) {
            MessageBoxA(NULL,
                        dwRet == ERROR_ACCESS_DENIED
                            ? "You must have administrator privileges to uninstall Dr. Mingw as "
                              "the default application debugger"
                            : "Unexpected error when trying to uninstall Dr. Mingw as the default "
                              "application debugger",
                        "DrMingw", MB_OK | MB_ICONERROR);
            return 1;
        }
        MessageBoxA(NULL, "Dr. Mingw has been uninstalled", "DrMingw", MB_OK | MB_ICONINFORMATION);
        return 0;
    }

    if (process_id_given) {
        SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);

        if (!ObtainSeDebugPrivilege())
            MessageBoxA(NULL,
                        "An error occurred while obtaining debug privileges.\nDrMingw will not "
                        "debug system processes.",
                        "DrMingw", MB_OK | MB_ICONERROR);

        createDialog();

        _beginthread(debugThread, 0, (void *)(UINT_PTR)dwProcessId);

        return mainLoop();
    } else {
        help();
    }

    return 0;
}
<|endoftext|>


<commit_before>/*********************************************************************
 * node-dvbtee for Node.js
 *
 * Copyright (c) 2017 Michael Ira Krufky <https://github.com/mkrufky>
 *
 * MIT License <https://github.com/mkrufky/node-dvbtee/blob/master/LICENSE>
 *
 * See https://github.com/mkrufky/node-dvbtee for more information
 ********************************************************************/

#include <nan.h>
#include "dvbtee-parser.h"  // NOLINT(build/include)

TableData::TableData(const uint8_t &tableId,
                     const std::string &decoderName,
                     const std::string &json)
: tableId(tableId)
, decoderName(decoderName)
, json(json)
{}

TableData::TableData()
  : tableId(0)
{}

TableData::TableData(const TableData &t)
  : tableId(t.tableId)
  , decoderName(t.decoderName)
  , json(t.json)
{}

TableData &TableData::operator=(const TableData &cSource) {
  if (this == &cSource)
      return *this;

  tableId     = cSource.tableId;
  decoderName = cSource.decoderName;
  json        = cSource.json;

  return *this;
}

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

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser() {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE exports) {
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "enableEttCollection", enableEttCollection);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

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

void dvbteeParser::enableEttCollection(
  const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  obj->m_parser.enable_ett_collection();

  info.GetReturnValue().Set(info.Holder());
}

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

class ResetWorker : public Nan::AsyncWorker {
 public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

 private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

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

class FeedWorker: public Nan::AsyncProgressQueueWorker<TableData> {
 public:
  FeedWorker(Nan::Callback *callback,
             Nan::Callback *progress,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncProgressQueueWorker<TableData>(callback)
    , m_cb_progress(progress)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        Nan::MaybeLocal<v8::Object> mbBuf =
          Nan::To<v8::Object>(GetFromPersistent(key));

        if (!mbBuf.IsEmpty()) {
          m_buf = node::Buffer::Data(mbBuf.ToLocalChecked());
          m_buf_len = info[1]->Uint32Value();
        }
      }
    }
  ~FeedWorker() {
    }

  void Execute(const AsyncProgressQueueWorker::ExecutionProgress& progress) {
    if ((m_buf) && (m_buf_len)) {

      Watcher watch(m_obj->m_parser, progress);

      m_ret = m_obj->m_parser.feed(
        m_buf_len, reinterpret_cast<uint8_t*>(m_buf)
      );
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  void HandleProgressCallback(const TableData *async_data, size_t count) {
    Nan::HandleScope scope;

    if (!m_cb_progress->IsEmpty()) {
      for (unsigned int i = 0; i < count; i++) {
        const TableData *data = &async_data[i];

        Nan::MaybeLocal<v8::String> jsonStr = Nan::New(data->json);
        if (!jsonStr.IsEmpty()) {
          Nan::MaybeLocal<v8::Value> jsonVal =
            NanJSON.Parse(jsonStr.ToLocalChecked());

          if (!jsonVal.IsEmpty()) {
            Nan::MaybeLocal<v8::String> decoderName =
              Nan::New(data->decoderName);

            v8::Local<v8::Value> decoderNameArg;
            if (decoderName.IsEmpty())
              decoderNameArg = Nan::Null();
            else
              decoderNameArg = decoderName.ToLocalChecked();

            v8::Local<v8::Value> argv[] = {
              Nan::New(data->tableId),
              decoderNameArg,
              jsonVal.ToLocalChecked()
            };

            m_cb_progress->Call(3, argv);
          }
        }
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

 private:
  Nan::Callback *m_cb_progress;
  Nan::JSON NanJSON;
  const AsyncProgressQueueWorker::ExecutionProgress *m_progress;
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;

  class Watcher: public dvbtee::decode::TableWatcher {
   public:
    explicit Watcher(parse& p, const AsyncProgressQueueWorker::ExecutionProgress& progress)
     : m_parser(p)
     , m_progress(progress) {
      m_parser.subscribeTables(this);
    }
    ~Watcher() {
      m_parser.subscribeTables(NULL);
    }

    void updateTable(uint8_t tId, dvbtee::decode::Table *table) {
      m_progress.Send(
        new TableData(table->getTableid(),
        table->getDecoderName(),
        table->toJson()), 1
      );
    }

   private:
    parse& m_parser;
    const AsyncProgressQueueWorker::ExecutionProgress& m_progress;
  };
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {

  int lastArg = info.Length() - 1;

  if ((lastArg >= 1) && (info[lastArg]->IsFunction()) &&
      (info[lastArg - 1]->IsFunction())) {
    Nan::Callback *progress = new Nan::Callback(
      info[lastArg - 1].As<v8::Function>()
    );
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, progress, info));

  } else {
    info.GetReturnValue().Set(Nan::New(-1));
  }
}
<commit_msg>FeedWorker: Watcher takes `this` FeedWorker<commit_after>/*********************************************************************
 * node-dvbtee for Node.js
 *
 * Copyright (c) 2017 Michael Ira Krufky <https://github.com/mkrufky>
 *
 * MIT License <https://github.com/mkrufky/node-dvbtee/blob/master/LICENSE>
 *
 * See https://github.com/mkrufky/node-dvbtee for more information
 ********************************************************************/

#include <nan.h>
#include "dvbtee-parser.h"  // NOLINT(build/include)

TableData::TableData(const uint8_t &tableId,
                     const std::string &decoderName,
                     const std::string &json)
: tableId(tableId)
, decoderName(decoderName)
, json(json)
{}

TableData::TableData()
  : tableId(0)
{}

TableData::TableData(const TableData &t)
  : tableId(t.tableId)
  , decoderName(t.decoderName)
  , json(t.json)
{}

TableData &TableData::operator=(const TableData &cSource) {
  if (this == &cSource)
      return *this;

  tableId     = cSource.tableId;
  decoderName = cSource.decoderName;
  json        = cSource.json;

  return *this;
}

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

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser() {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE exports) {
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "enableEttCollection", enableEttCollection);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

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

void dvbteeParser::enableEttCollection(
  const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  obj->m_parser.enable_ett_collection();

  info.GetReturnValue().Set(info.Holder());
}

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

class ResetWorker : public Nan::AsyncWorker {
 public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

 private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

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

class FeedWorker: public Nan::AsyncProgressQueueWorker<TableData> {
 public:
  FeedWorker(Nan::Callback *callback,
             Nan::Callback *progress,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncProgressQueueWorker<TableData>(callback)
    , m_cb_progress(progress)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        Nan::MaybeLocal<v8::Object> mbBuf =
          Nan::To<v8::Object>(GetFromPersistent(key));

        if (!mbBuf.IsEmpty()) {
          m_buf = node::Buffer::Data(mbBuf.ToLocalChecked());
          m_buf_len = info[1]->Uint32Value();
        }
      }
    }
  ~FeedWorker() {
    }

  void Execute(const AsyncProgressQueueWorker::ExecutionProgress& progress) {
    if ((m_buf) && (m_buf_len)) {
      Watcher watch(this, progress);

      m_ret = m_obj->m_parser.feed(
        m_buf_len, reinterpret_cast<uint8_t*>(m_buf)
      );
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  void HandleProgressCallback(const TableData *async_data, size_t count) {
    Nan::HandleScope scope;

    if (!m_cb_progress->IsEmpty()) {
      for (unsigned int i = 0; i < count; i++) {
        const TableData *data = &async_data[i];

        Nan::MaybeLocal<v8::String> jsonStr = Nan::New(data->json);
        if (!jsonStr.IsEmpty()) {
          Nan::MaybeLocal<v8::Value> jsonVal =
            NanJSON.Parse(jsonStr.ToLocalChecked());

          if (!jsonVal.IsEmpty()) {
            Nan::MaybeLocal<v8::String> decoderName =
              Nan::New(data->decoderName);

            v8::Local<v8::Value> decoderNameArg;
            if (decoderName.IsEmpty())
              decoderNameArg = Nan::Null();
            else
              decoderNameArg = decoderName.ToLocalChecked();

            v8::Local<v8::Value> argv[] = {
              Nan::New(data->tableId),
              decoderNameArg,
              jsonVal.ToLocalChecked()
            };

            m_cb_progress->Call(3, argv);
          }
        }
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

 private:
  Nan::Callback *m_cb_progress;
  Nan::JSON NanJSON;
  const AsyncProgressQueueWorker::ExecutionProgress *m_progress;
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;

  class Watcher: public dvbtee::decode::TableWatcher {
   public:
    explicit Watcher(FeedWorker* w,
      const AsyncProgressQueueWorker::ExecutionProgress& progress)
     : m_worker(w)
     , m_progress(progress) {
      m_worker->m_obj->m_parser.subscribeTables(this);
    }
    ~Watcher() {
      m_worker->m_obj->m_parser.subscribeTables(NULL);
    }

    void updateTable(uint8_t tId, dvbtee::decode::Table *table) {
      m_progress.Send(
        new TableData(table->getTableid(),
        table->getDecoderName(),
        table->toJson()), 1
      );
    }

   private:
    FeedWorker* m_worker;
    const AsyncProgressQueueWorker::ExecutionProgress& m_progress;
  };
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  int lastArg = info.Length() - 1;

  if ((lastArg >= 1) && (info[lastArg]->IsFunction()) &&
      (info[lastArg - 1]->IsFunction())) {
    Nan::Callback *progress = new Nan::Callback(
      info[lastArg - 1].As<v8::Function>()
    );
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, progress, info));

  } else {
    info.GetReturnValue().Set(Nan::New(-1));
  }
}
<|endoftext|>


<commit_before>#include "terminal.h"

/// Sets a newline operator to Terminal output.
Terminal& Terminal::newline(Terminal& term)
{
   term.set_ops(Ops::newline);
   return *this;
}

/// Sets a newline operator then a flush operator to Terminal output.
Terminal& Terminal::endl(Terminal& term)
{
   term.set_ops(Ops::newline);
   term.set_ops(Ops::flush);
   return *this;
}

/// Sets a flush operator to Terminal output.
Terminal& Terminal::flush(Terminal& term)
{
   term.set_ops(Ops::flush);
   return *this;
}

/// Sets a general message state operator to Terminal output.
Terminal& Terminal::general(Terminal& term)
{
   term.set_ops(Ops::general);
   return *this;
}

/// Sets a warning message state operator to Terminal output.
Terminal& Terminal::warning(Terminal& term)
{
   term.set_ops(Ops::warning);
   return *this;
}

/// Sets a error message state operator to Terminal output.
Terminal& Terminal::error(Terminal& term)
{
   term.set_ops(Ops::error);
   return *this;
}

// Self-referencing print functions that redirect to output operators.
Terminal& Terminal::print(short n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned short n)
{
   return *this << n;
}
Terminal& Terminal::print(int n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned int n)
{
   return *this << n;
}
Terminal& Terminal::print(long n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned long n)
{
   return *this << n;
}
Terminal& Terminal::print(float n)
{
   return *this << n;
}
Terminal& Terminal::print(double n)
{
   return *this << n;
}
Terminal& Terminal::print(const char* n)
{
   return *this << n;
}
Terminal& Terminal::print(const std::string& n)
{
   return *this << n;
}

Terminal& Terminal::operator<<(Terminal& (*pf)(Terminal&))
{
   return pf(*this);
}
<commit_msg>Fixed terminal class.<commit_after>#include "terminal.h"

/// Sets a newline operator to Terminal output.
Terminal& Terminal::newline(Terminal& term)
{
   term.set_ops(Ops::newline);
}

/// Sets a newline operator then a flush operator to Terminal output.
Terminal& Terminal::endl(Terminal& term)
{
   term.set_ops(Ops::newline);
   term.set_ops(Ops::flush);
}

/// Sets a flush operator to Terminal output.
Terminal& Terminal::flush(Terminal& term)
{
   term.set_ops(Ops::flush);
}

/// Sets a general message state operator to Terminal output.
Terminal& Terminal::general(Terminal& term)
{
   term.set_ops(Ops::general);
}

/// Sets a warning message state operator to Terminal output.
Terminal& Terminal::warning(Terminal& term)
{
   term.set_ops(Ops::warning);
}

/// Sets a error message state operator to Terminal output.
Terminal& Terminal::error(Terminal& term)
{
   term.set_ops(Ops::error);
}

// Self-referencing print functions that redirect to output operators.
Terminal& Terminal::print(short n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned short n)
{
   return *this << n;
}
Terminal& Terminal::print(int n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned int n)
{
   return *this << n;
}
Terminal& Terminal::print(long n)
{
   return *this << n;
}
Terminal& Terminal::print(unsigned long n)
{
   return *this << n;
}
Terminal& Terminal::print(float n)
{
   return *this << n;
}
Terminal& Terminal::print(double n)
{
   return *this << n;
}
Terminal& Terminal::print(const char* n)
{
   return *this << n;
}
Terminal& Terminal::print(const std::string& n)
{
   return *this << n;
}

Terminal& Terminal::operator<<(Terminal& (*pf)(Terminal&))
{
   return pf(*this);
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "after_initialization_fixture.h"
#include "test/testsupport/fileutils.h"

namespace {

const int kSampleRateHz = 16000;
const int kTestDurationMs = 1000;
const int16_t kInputValue = 15000;
const int16_t kSilenceValue = 0;

}  // namespace

class FileBeforeStreamingTest : public AfterInitializationFixture {
 protected:
  FileBeforeStreamingTest()
      : input_filename_(webrtc::test::OutputPath() + "file_test_input.pcm"),
        output_filename_(webrtc::test::OutputPath() + "file_test_output.pcm") {
  }

  void SetUp() {
    channel_ = voe_base_->CreateChannel();
  }

  void TearDown() {
    voe_base_->DeleteChannel(channel_);
  }

  // TODO(andrew): consolidate below methods in a shared place?

  // Generate input file with constant values as |kInputValue|. The file
  // will be one second longer than the duration of the test.
  void GenerateInputFile() {
    FILE* input_file = fopen(input_filename_.c_str(), "wb");
    ASSERT_TRUE(input_file != NULL);
    for (int i = 0; i < kSampleRateHz / 1000 * (kTestDurationMs + 1000); i++) {
      ASSERT_EQ(1u, fwrite(&kInputValue, sizeof(kInputValue), 1, input_file));
    }
    ASSERT_EQ(0, fclose(input_file));
  }

  void RecordOutput() {
    // Start recording the mixed output for |kTestDurationMs| long.
    EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1,
        output_filename_.c_str()));
    Sleep(kTestDurationMs);
    EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1));
  }

  void VerifyOutput(int16_t target_value) {
    FILE* output_file = fopen(output_filename_.c_str(), "rb");
    ASSERT_TRUE(output_file != NULL);
    int16_t output_value = 0;
    int samples_read = 0;

    while (fread(&output_value, sizeof(output_value), 1, output_file) == 1) {
      samples_read++;
      EXPECT_EQ(output_value, target_value);
    }

    // Ensure the recording length is close to the duration of the test.
    ASSERT_GE((samples_read * 1000.0) / kSampleRateHz, 0.9 * kTestDurationMs);

    // Ensure we read the entire file.
    ASSERT_NE(0, feof(output_file));
    ASSERT_EQ(0, fclose(output_file));
  }

void VerifyEmptyOutput() {
  FILE* output_file = fopen(output_filename_.c_str(), "rb");
  ASSERT_TRUE(output_file != NULL);
  ASSERT_EQ(0, fseek(output_file, 0, SEEK_END));
  EXPECT_EQ(0, ftell(output_file));
  ASSERT_EQ(0, fclose(output_file));
}

  int channel_;
  const std::string input_filename_;
  const std::string output_filename_;
};

// This test case is to ensure that StartPlayingFileLocally() and
// StartPlayout() can be called in any order.
// A DC signal is used as input. And the output of mixer is supposed to be:
// 1. the same DC signal if file is played out,
// 2. total silence if file is not played out,
// 3. no output if playout is not started.
TEST_F(FileBeforeStreamingTest,
       DISABLED_TestStartPlayingFileLocallyWithStartPlayout) {
  GenerateInputFile();

  TEST_LOG("Playout is not started. File will not be played out.\n");
  EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(
      channel_, input_filename_.c_str(), true));
  EXPECT_EQ(1, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyEmptyOutput();

  TEST_LOG("Playout is now started. File will be played out.\n");
  EXPECT_EQ(0, voe_base_->StartPlayout(channel_));
  RecordOutput();
  VerifyOutput(kInputValue);

  TEST_LOG("Stop playing file. Only silence will be played out.\n");
  EXPECT_EQ(0, voe_file_->StopPlayingFileLocally(channel_));
  EXPECT_EQ(0, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyOutput(kSilenceValue);

  TEST_LOG("Start playing file again. File will be played out.\n");
  EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(
      channel_, input_filename_.c_str(), true));
  EXPECT_EQ(1, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyOutput(kInputValue);

  EXPECT_EQ(0, voe_base_->StopPlayout(channel_));
  EXPECT_EQ(0, voe_file_->StopPlayingFileLocally(channel_));
}
<commit_msg>Fix the flakiness in FileBeforeStreamingTest<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "after_initialization_fixture.h"
#include "test/testsupport/fileutils.h"

namespace {

const int kSampleRateHz = 16000;
const int kTestDurationMs = 1000;
const int kSkipOutputMs = 50;
const int16_t kInputValue = 15000;
const int16_t kSilenceValue = 0;

}  // namespace

class FileBeforeStreamingTest : public AfterInitializationFixture {
 protected:
  FileBeforeStreamingTest()
      : input_filename_(webrtc::test::OutputPath() + "file_test_input.pcm"),
        output_filename_(webrtc::test::OutputPath() + "file_test_output.pcm") {
  }

  void SetUp() {
    channel_ = voe_base_->CreateChannel();
  }

  void TearDown() {
    voe_base_->DeleteChannel(channel_);
  }

  // TODO(andrew): consolidate below methods in a shared place?

  // Generate input file with constant values as |kInputValue|. The file
  // will be one second longer than the duration of the test.
  void GenerateInputFile() {
    FILE* input_file = fopen(input_filename_.c_str(), "wb");
    ASSERT_TRUE(input_file != NULL);
    for (int i = 0; i < kSampleRateHz / 1000 * (kTestDurationMs + 1000); i++) {
      ASSERT_EQ(1u, fwrite(&kInputValue, sizeof(kInputValue), 1, input_file));
    }
    ASSERT_EQ(0, fclose(input_file));
  }

  void RecordOutput() {
    // Start recording the mixed output for |kTestDurationMs| long.
    EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1,
        output_filename_.c_str()));
    Sleep(kTestDurationMs);
    EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1));
  }

  void VerifyOutput(int16_t target_value) {
    FILE* output_file = fopen(output_filename_.c_str(), "rb");
    ASSERT_TRUE(output_file != NULL);
    int16_t output_value = 0;
    int samples_read = 0;

    // Skip the first segment to avoid initialization and ramping-in effects.
    EXPECT_EQ(0, fseek(output_file, sizeof(output_value) *
                       kSampleRateHz / 1000 * kSkipOutputMs, SEEK_SET));
    while (fread(&output_value, sizeof(output_value), 1, output_file) == 1) {
      samples_read++;
      EXPECT_EQ(output_value, target_value);
    }

    // Ensure the recording length is close to the duration of the test.
    ASSERT_GE((samples_read * 1000.0) / kSampleRateHz, 0.9 * kTestDurationMs);

    // Ensure we read the entire file.
    ASSERT_NE(0, feof(output_file));
    ASSERT_EQ(0, fclose(output_file));
  }

void VerifyEmptyOutput() {
  FILE* output_file = fopen(output_filename_.c_str(), "rb");
  ASSERT_TRUE(output_file != NULL);
  ASSERT_EQ(0, fseek(output_file, 0, SEEK_END));
  EXPECT_EQ(0, ftell(output_file));
  ASSERT_EQ(0, fclose(output_file));
}

  int channel_;
  const std::string input_filename_;
  const std::string output_filename_;
};

// This test case is to ensure that StartPlayingFileLocally() and
// StartPlayout() can be called in any order.
// A DC signal is used as input. And the output of mixer is supposed to be:
// 1. the same DC signal if file is played out,
// 2. total silence if file is not played out,
// 3. no output if playout is not started.
TEST_F(FileBeforeStreamingTest, TestStartPlayingFileLocallyWithStartPlayout) {
  GenerateInputFile();

  TEST_LOG("Playout is not started. File will not be played out.\n");
  EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(
      channel_, input_filename_.c_str(), true));
  EXPECT_EQ(1, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyEmptyOutput();

  TEST_LOG("Playout is now started. File will be played out.\n");
  EXPECT_EQ(0, voe_base_->StartPlayout(channel_));
  RecordOutput();
  VerifyOutput(kInputValue);

  TEST_LOG("Stop playing file. Only silence will be played out.\n");
  EXPECT_EQ(0, voe_file_->StopPlayingFileLocally(channel_));
  EXPECT_EQ(0, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyOutput(kSilenceValue);

  TEST_LOG("Start playing file again. File will be played out.\n");
  EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(
      channel_, input_filename_.c_str(), true));
  EXPECT_EQ(1, voe_file_->IsPlayingFileLocally(channel_));
  RecordOutput();
  VerifyOutput(kInputValue);

  EXPECT_EQ(0, voe_base_->StopPlayout(channel_));
  EXPECT_EQ(0, voe_file_->StopPlayingFileLocally(channel_));
}
<|endoftext|>


<commit_before>/**
 * Derived from this blog post:
 * http://simmesimme.github.io/tutorials/2015/09/20/signal-slot
 */

#ifndef SIGNAL_HPP
#define SIGNAL_HPP

#include <functional>
#include <map>

// A signal object may call multiple slots with the
// same signature. You can connect functions to the signal
// which will be called when the emit() method on the
// signal object is invoked. Any argument passed to emit()
// will be passed to the given functions.

template <typename... Args> class Signal {

public:
  Signal() : current_id_(0) {}

  // copy creates new signal
  Signal(Signal const &other) : current_id_(0) {}

  // connects a member function to this Signal
  template <typename T> int ConnectMember(T *inst, void (T::*func)(Args...)) {
    return Connect([=](Args... args) { (inst->*func)(args...); });
  }

  // connects a member function to this Signal
  template <typename T>
  int ConnectMember(std::shared_ptr<T> inst, void (T::*func)(Args...)) {
    return Connect([=](Args... args) { ((*inst).*func)(args...); });
  }

  // connects a const member function to this Signal
  template <typename T>
  int ConnectMember(T *inst, void (T::*func)(Args...) const) {
    return Connect([=](Args... args) { (inst->*func)(args...); });
  }

  // connects a std::function to the signal. The returned
  // value can be used to disconnect the function again
  int Connect(std::function<void(Args...)> const &slot) const {
    slots_.insert(std::make_pair(++current_id_, slot));
    return current_id_;
  }

  // disconnects a previously connected function
  void Disconnect(int id) const { slots_.erase(id); }

  // disconnects all previously connected functions
  void DisconnectAll() const { slots_.clear(); }

  // calls all connected functions
  void Emit(Args... p) {
    for (auto it : slots_) {
      it.second(p...);
    }
  }

  // assignment creates new Signal
  Signal &operator=(Signal const &other) { DisconnectAll(); }

private:
  mutable std::map<int, std::function<void(Args...)>> slots_;
  mutable int current_id_;
};

#endif /* SIGNAL_HPP */<commit_msg>removed unneeded Signal::ConnectMember function<commit_after>/**
 * Derived from this blog post:
 * http://simmesimme.github.io/tutorials/2015/09/20/signal-slot
 */

#ifndef SIGNAL_HPP
#define SIGNAL_HPP

#include <functional>
#include <map>

// A signal object may call multiple slots with the
// same signature. You can connect functions to the signal
// which will be called when the emit() method on the
// signal object is invoked. Any argument passed to emit()
// will be passed to the given functions.

template <typename... Args> class Signal {

public:
  Signal() : current_id_(0) {}

  // copy creates new signal
  Signal(Signal const &other) : current_id_(0) {}

  // connects a member function to this Signal
  template <typename T> int ConnectMember(T *inst, void (T::*func)(Args...)) {
    return Connect([=](Args... args) { (inst->*func)(args...); });
  }

  // connects a const member function to this Signal
  template <typename T>
  int ConnectMember(T *inst, void (T::*func)(Args...) const) {
    return Connect([=](Args... args) { (inst->*func)(args...); });
  }

  // connects a std::function to the signal. The returned
  // value can be used to disconnect the function again
  int Connect(std::function<void(Args...)> const &slot) const {
    slots_.insert(std::make_pair(++current_id_, slot));
    return current_id_;
  }

  // disconnects a previously connected function
  void Disconnect(int id) const { slots_.erase(id); }

  // disconnects all previously connected functions
  void DisconnectAll() const { slots_.clear(); }

  // calls all connected functions
  void Emit(Args... p) {
    for (auto it : slots_) {
      it.second(p...);
    }
  }

  // assignment creates new Signal
  Signal &operator=(Signal const &other) { DisconnectAll(); }

private:
  mutable std::map<int, std::function<void(Args...)>> slots_;
  mutable int current_id_;
};

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


<commit_before>// Copyright 2010-2013 RethinkDB, all rights reserved.
#ifndef EXTPROC_POOL_HPP_
#define EXTPROC_POOL_HPP_

#include <sys/types.h>          // pid_t

#include "errors.hpp"

#include "concurrency/one_per_thread.hpp"
#include "concurrency/semaphore.hpp"
#include "concurrency/signal.hpp"
#include "containers/archive/interruptible_stream.hpp"
#include "containers/archive/socket_stream.hpp"
#include "containers/intrusive_list.hpp"
#include "extproc/job.hpp"
#include "extproc/spawner.hpp"

namespace extproc {

class job_handle_t;
class pool_t;

// Use this to create one process pool per thread, and access the appropriate
// one (via `get()`).
class pool_group_t {
    friend class pool_t;

public:
    static const int DEFAULT_MIN_WORKERS = 2;
    static const int DEFAULT_MAX_WORKERS = 2;

    struct config_t {
        config_t()
            : min_workers(DEFAULT_MIN_WORKERS), max_workers(DEFAULT_MAX_WORKERS) {}
        int min_workers;        // >= 0
        int max_workers;        // >= min_workers, > 0
    };

    static const config_t DEFAULTS;

    pool_group_t(spawner_info_t *info, const config_t &config);

    pool_t *get() { return pool_maker_.get(); }

private:
    spawner_t spawner_;
    config_t config_;
    one_per_thread_t<pool_t> pool_maker_;
};

// A worker process.
class pool_worker_t : public intrusive_list_node_t<pool_worker_t> {
    friend class job_handle_t;

public:
    pool_worker_t(pool_t *pool, pid_t pid, scoped_fd_t *fd);
    ~pool_worker_t();

    // Called when we get an error on a worker process socket, which usually
    // indicates the worker process' death.
    void on_error();

    // Inherited from unix_socket_stream_t. Called when epoll finds an error
    // condition on our socket. Calls on_error().
    virtual void do_on_event(int events);

    unix_socket_stream_t unix_socket;

private:
    friend class pool_t;

    pool_t *const pool_;
    const pid_t pid_;
    bool attached_;

    DISABLE_COPYING(pool_worker_t);
};

// A per-thread worker pool.
class pool_t : public home_thread_mixin_debug_only_t {
public:
    explicit pool_t(pool_group_t *group);
    ~pool_t();

private:
    friend class job_handle_t;

    pool_group_t::config_t *config() { return &group_->config_; }
    spawner_t *spawner() { return &group_->spawner_; }

  private:
    // Checks & repairs invariants, namely:
    // - num_workers() >= config()->min_workers
    void repair_invariants();

    // Connects us to a worker. Private; used only by job_handle_t::spawn().
    pool_worker_t *acquire_worker();

    // Called by job_handle_t to indicate the job has finished or errored.
    void release_worker(pool_worker_t *worker) THROWS_NOTHING;

    // Called by job_handle_t to interrupt a running job.
    void interrupt_worker(pool_worker_t *worker) THROWS_NOTHING;

    // Detaches the worker from the pool, sends it SIGKILL, and ignores further
    // errors from it (ie. on_event will not call on_error, and hence will not
    // crash). Does not block.
    //
    // This is used by the interruptor-signal logic in
    // job_handle_t::{read,write}_interruptible(), where interrupt_worker()
    // cannot be used because it destroys the worker.
    //
    // It is the caller's responsibility to call cleanup_detached_worker() at
    // some point in the near future.
    void detach_worker(pool_worker_t *worker);

    // Cleans up after a detached worker. Destroys the worker. May block.
    void cleanup_detached_worker(pool_worker_t *worker);

    void spawn_workers(int n);
    void end_worker(intrusive_list_t<pool_worker_t> *list, pool_worker_t *worker);

    int num_workers() {
        return idle_workers_.size() + busy_workers_.size() + num_spawning_workers_;
    }

private:
    pool_group_t *group_;

    // Worker processes.
    intrusive_list_t<pool_worker_t> idle_workers_;
    intrusive_list_t<pool_worker_t> busy_workers_;

    // Count of the number of workers in the process of being spawned. Necessary
    // in order to maintain (min_workers, max_workers) bounds without race
    // conditions.
    int num_spawning_workers_;

    // Used to signify that you're using a worker. In particular, lets us block
    // for a worker to become available when we already have
    // config_->max_workers workers running.
    semaphore_t worker_semaphore_;
};

// A handle to a running job.
class job_handle_t : public interruptible_read_stream_t, public interruptible_write_stream_t {
public:
    // When constructed, job handles are "disconnected", not associated with a
    // job. They are connected by pool_t::spawn_job().
    job_handle_t();

    // You MUST call release() to finish a job normally, and you SHOULD call
    // interrupt() explicitly to interrupt a job (or signal the interruptor
    // during {read,write}_interruptible()).
    //
    // However, as a convenience in the case of exception-raising code, if the
    // handle is connected, the destructor will log a warning and interrupt the
    // job.
    virtual ~job_handle_t();

    bool connected() { return NULL != worker_; }

    // Begins running `job` on `pool`. Must be disconnected beforehand. On
    // success, returns 0 and connects us to the spawned job. Returns -1 on
    // error.
    int begin(pool_t *pool, const job_t &job);

    // Indicates the job has either finished normally or experienced an I/O
    // error; disconnects the job handle.
    void release() THROWS_NOTHING;

    // Forcibly interrupts a running job; disconnects the job handle.
    //
    // MAY NOT be called concurrently with any I/O methods. Instead, pass a
    // signal to {read,write}_interruptible().
    void interrupt() THROWS_NOTHING;

    // On either read or write error or interruption, the job handle becomes
    // disconnected and must not be used.
    virtual MUST_USE int64_t read_interruptible(void *p, int64_t n, signal_t *interruptor);
    virtual int64_t write_interruptible(const void *p, int64_t n, signal_t *interruptor);

private:
    friend class pool_t;
    friend class interruptor_wrapper_t;

    void check_attached();

    class interruptor_wrapper_t : public signal_t, public signal_t::subscription_t {
    public:
        interruptor_wrapper_t(job_handle_t *handle, signal_t *signal);
        virtual void run() THROWS_NOTHING;
        job_handle_t *handle_;
    };

    pool_worker_t *worker_;

    DISABLE_COPYING(job_handle_t);
};

} // namespace extproc

#endif // EXTPROC_POOL_HPP_
<commit_msg>Added DISABLE_COPYINGs in pool.hpp.<commit_after>// Copyright 2010-2013 RethinkDB, all rights reserved.
#ifndef EXTPROC_POOL_HPP_
#define EXTPROC_POOL_HPP_

#include <sys/types.h>          // pid_t

#include "errors.hpp"

#include "concurrency/one_per_thread.hpp"
#include "concurrency/semaphore.hpp"
#include "concurrency/signal.hpp"
#include "containers/archive/interruptible_stream.hpp"
#include "containers/archive/socket_stream.hpp"
#include "containers/intrusive_list.hpp"
#include "extproc/job.hpp"
#include "extproc/spawner.hpp"

namespace extproc {

class job_handle_t;
class pool_t;

// Use this to create one process pool per thread, and access the appropriate
// one (via `get()`).
class pool_group_t {
    friend class pool_t;

public:
    static const int DEFAULT_MIN_WORKERS = 2;
    static const int DEFAULT_MAX_WORKERS = 2;

    struct config_t {
        config_t()
            : min_workers(DEFAULT_MIN_WORKERS), max_workers(DEFAULT_MAX_WORKERS) {}
        int min_workers;        // >= 0
        int max_workers;        // >= min_workers, > 0
    };

    static const config_t DEFAULTS;

    pool_group_t(spawner_info_t *info, const config_t &config);

    pool_t *get() { return pool_maker_.get(); }

private:
    spawner_t spawner_;
    config_t config_;
    one_per_thread_t<pool_t> pool_maker_;

    DISABLE_COPYING(pool_group_t);
};

// A worker process.
class pool_worker_t : public intrusive_list_node_t<pool_worker_t> {
    friend class job_handle_t;

public:
    pool_worker_t(pool_t *pool, pid_t pid, scoped_fd_t *fd);
    ~pool_worker_t();

    // Called when we get an error on a worker process socket, which usually
    // indicates the worker process' death.
    void on_error();

    // Inherited from unix_socket_stream_t. Called when epoll finds an error
    // condition on our socket. Calls on_error().
    virtual void do_on_event(int events);

    unix_socket_stream_t unix_socket;

private:
    friend class pool_t;

    pool_t *const pool_;
    const pid_t pid_;
    bool attached_;

    DISABLE_COPYING(pool_worker_t);
};

// A per-thread worker pool.
class pool_t : public home_thread_mixin_debug_only_t {
public:
    explicit pool_t(pool_group_t *group);
    ~pool_t();

private:
    friend class job_handle_t;

    pool_group_t::config_t *config() { return &group_->config_; }
    spawner_t *spawner() { return &group_->spawner_; }

  private:
    // Checks & repairs invariants, namely:
    // - num_workers() >= config()->min_workers
    void repair_invariants();

    // Connects us to a worker. Private; used only by job_handle_t::spawn().
    pool_worker_t *acquire_worker();

    // Called by job_handle_t to indicate the job has finished or errored.
    void release_worker(pool_worker_t *worker) THROWS_NOTHING;

    // Called by job_handle_t to interrupt a running job.
    void interrupt_worker(pool_worker_t *worker) THROWS_NOTHING;

    // Detaches the worker from the pool, sends it SIGKILL, and ignores further
    // errors from it (ie. on_event will not call on_error, and hence will not
    // crash). Does not block.
    //
    // This is used by the interruptor-signal logic in
    // job_handle_t::{read,write}_interruptible(), where interrupt_worker()
    // cannot be used because it destroys the worker.
    //
    // It is the caller's responsibility to call cleanup_detached_worker() at
    // some point in the near future.
    void detach_worker(pool_worker_t *worker);

    // Cleans up after a detached worker. Destroys the worker. May block.
    void cleanup_detached_worker(pool_worker_t *worker);

    void spawn_workers(int n);
    void end_worker(intrusive_list_t<pool_worker_t> *list, pool_worker_t *worker);

    int num_workers() const {
        return idle_workers_.size() + busy_workers_.size() + num_spawning_workers_;
    }

private:
    pool_group_t *group_;

    // Worker processes.
    intrusive_list_t<pool_worker_t> idle_workers_;
    intrusive_list_t<pool_worker_t> busy_workers_;

    // Count of the number of workers in the process of being spawned. Necessary
    // in order to maintain (min_workers, max_workers) bounds without race
    // conditions.
    int num_spawning_workers_;

    // Used to signify that you're using a worker. In particular, lets us block
    // for a worker to become available when we already have
    // config_->max_workers workers running.
    semaphore_t worker_semaphore_;

    DISABLE_COPYING(pool_t);
};

// A handle to a running job.
class job_handle_t : public interruptible_read_stream_t, public interruptible_write_stream_t {
public:
    // When constructed, job handles are "disconnected", not associated with a
    // job. They are connected by pool_t::spawn_job().
    job_handle_t();

    // You MUST call release() to finish a job normally, and you SHOULD call
    // interrupt() explicitly to interrupt a job (or signal the interruptor
    // during {read,write}_interruptible()).
    //
    // However, as a convenience in the case of exception-raising code, if the
    // handle is connected, the destructor will log a warning and interrupt the
    // job.
    virtual ~job_handle_t();

    bool connected() { return NULL != worker_; }

    // Begins running `job` on `pool`. Must be disconnected beforehand. On
    // success, returns 0 and connects us to the spawned job. Returns -1 on
    // error.
    int begin(pool_t *pool, const job_t &job);

    // Indicates the job has either finished normally or experienced an I/O
    // error; disconnects the job handle.
    void release() THROWS_NOTHING;

    // Forcibly interrupts a running job; disconnects the job handle.
    //
    // MAY NOT be called concurrently with any I/O methods. Instead, pass a
    // signal to {read,write}_interruptible().
    void interrupt() THROWS_NOTHING;

    // On either read or write error or interruption, the job handle becomes
    // disconnected and must not be used.
    virtual MUST_USE int64_t read_interruptible(void *p, int64_t n, signal_t *interruptor);
    virtual int64_t write_interruptible(const void *p, int64_t n, signal_t *interruptor);

private:
    friend class pool_t;
    friend class interruptor_wrapper_t;

    void check_attached();

    class interruptor_wrapper_t : public signal_t, public signal_t::subscription_t {
    public:
        interruptor_wrapper_t(job_handle_t *handle, signal_t *signal);
        virtual void run() THROWS_NOTHING;
        job_handle_t *handle_;
    };

    pool_worker_t *worker_;

    DISABLE_COPYING(job_handle_t);
};

} // namespace extproc

#endif // EXTPROC_POOL_HPP_
<|endoftext|>


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

#include "./transform.h"

#include <complex>  // for std::abs

#include "./buffer.h"
#include "./font.h"
#include "./glyph.h"
#include "./table_tags.h"
#include "./variable_length.h"

namespace woff2 {

namespace {

const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;

void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) {
  if (len == 0) return;
  size_t offset = out->size();
  out->resize(offset + len);
  memcpy(&(*out)[offset], data, len);
}

void WriteBytes(std::vector<uint8_t>* out, const std::vector<uint8_t>& in) {
  for (int i = 0; i < in.size(); ++i) {
    out->push_back(in[i]);
  }
}

void WriteUShort(std::vector<uint8_t>* out, int value) {
  out->push_back(value >> 8);
  out->push_back(value & 255);
}

void WriteLong(std::vector<uint8_t>* out, int value) {
  out->push_back((value >> 24) & 255);
  out->push_back((value >> 16) & 255);
  out->push_back((value >> 8) & 255);
  out->push_back(value & 255);
}

// Glyf table preprocessing, based on
// GlyfEncoder.java
class GlyfEncoder {
 public:
  explicit GlyfEncoder(int num_glyphs)
      : n_glyphs_(num_glyphs) {
    bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2);
  }

  bool Encode(int glyph_id, const Glyph& glyph) {
    if (glyph.composite_data_size > 0) {
      WriteCompositeGlyph(glyph_id, glyph);
    } else if (glyph.contours.size() > 0) {
      WriteSimpleGlyph(glyph_id, glyph);
    } else {
      WriteUShort(&n_contour_stream_, 0);
    }
    return true;
  }

  void GetTransformedGlyfBytes(std::vector<uint8_t>* result) {
    WriteLong(result, 0);  // version
    WriteUShort(result, n_glyphs_);
    WriteUShort(result, 0);  // index_format, will be set later
    WriteLong(result, n_contour_stream_.size());
    WriteLong(result, n_points_stream_.size());
    WriteLong(result, flag_byte_stream_.size());
    WriteLong(result, glyph_stream_.size());
    WriteLong(result, composite_stream_.size());
    WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size());
    WriteLong(result, instruction_stream_.size());
    WriteBytes(result, n_contour_stream_);
    WriteBytes(result, n_points_stream_);
    WriteBytes(result, flag_byte_stream_);
    WriteBytes(result, glyph_stream_);
    WriteBytes(result, composite_stream_);
    WriteBytes(result, bbox_bitmap_);
    WriteBytes(result, bbox_stream_);
    WriteBytes(result, instruction_stream_);
  }

 private:
  void WriteInstructions(const Glyph& glyph) {
    Write255UShort(&glyph_stream_, glyph.instructions_size);
    WriteBytes(&instruction_stream_,
               glyph.instructions_data, glyph.instructions_size);
  }

  bool ShouldWriteSimpleGlyphBbox(const Glyph& glyph) {
    if (glyph.contours.empty() || glyph.contours[0].empty()) {
      return glyph.x_min || glyph.y_min || glyph.x_max || glyph.y_max;
    }

    int16_t x_min = glyph.contours[0][0].x;
    int16_t y_min = glyph.contours[0][0].y;
    int16_t x_max = x_min;
    int16_t y_max = y_min;
    for (const auto& contour : glyph.contours) {
      for (const auto& point : contour) {
        if (point.x < x_min) x_min = point.x;
        if (point.x > x_max) x_max = point.x;
        if (point.y < y_min) y_min = point.y;
        if (point.y > y_max) y_max = point.y;
      }
    }

    if (glyph.x_min != x_min)
      return true;
    if (glyph.y_min != y_min)
      return true;
    if (glyph.x_max != x_max)
      return true;
    if (glyph.y_max != y_max)
      return true;

    return false;
  }

  void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) {
    int num_contours = glyph.contours.size();
    WriteUShort(&n_contour_stream_, num_contours);
    if (ShouldWriteSimpleGlyphBbox(glyph)) {
      WriteBbox(glyph_id, glyph);
    }
    // TODO: check that bbox matches, write bbox if not
    for (int i = 0; i < num_contours; i++) {
      Write255UShort(&n_points_stream_, glyph.contours[i].size());
    }
    int lastX = 0;
    int lastY = 0;
    for (int i = 0; i < num_contours; i++) {
      int num_points = glyph.contours[i].size();
      for (int j = 0; j < num_points; j++) {
        int x = glyph.contours[i][j].x;
        int y = glyph.contours[i][j].y;
        int dx = x - lastX;
        int dy = y - lastY;
        WriteTriplet(glyph.contours[i][j].on_curve, dx, dy);
        lastX = x;
        lastY = y;
      }
    }
    if (num_contours > 0) {
      WriteInstructions(glyph);
    }
  }

  void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) {
    WriteUShort(&n_contour_stream_, -1);
    WriteBbox(glyph_id, glyph);
    WriteBytes(&composite_stream_,
               glyph.composite_data,
               glyph.composite_data_size);
    if (glyph.have_instructions) {
      WriteInstructions(glyph);
    }
  }

  void WriteBbox(int glyph_id, const Glyph& glyph) {
    bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7);
    WriteUShort(&bbox_stream_, glyph.x_min);
    WriteUShort(&bbox_stream_, glyph.y_min);
    WriteUShort(&bbox_stream_, glyph.x_max);
    WriteUShort(&bbox_stream_, glyph.y_max);
  }

  void WriteTriplet(bool on_curve, int x, int y) {
    int abs_x = std::abs(x);
    int abs_y = std::abs(y);
    int on_curve_bit = on_curve ? 0 : 128;
    int x_sign_bit = (x < 0) ? 0 : 1;
    int y_sign_bit = (y < 0) ? 0 : 1;
    int xy_sign_bits = x_sign_bit + 2 * y_sign_bit;
    if (x == 0 && abs_y < 1280) {
      flag_byte_stream_.push_back(on_curve_bit +
                                  ((abs_y & 0xf00) >> 7) + y_sign_bit);
      glyph_stream_.push_back(abs_y & 0xff);
    } else if (y == 0 && abs_x < 1280) {
      flag_byte_stream_.push_back(on_curve_bit + 10 +
                                  ((abs_x & 0xf00) >> 7) + x_sign_bit);
      glyph_stream_.push_back(abs_x & 0xff);
    } else if (abs_x < 65 && abs_y < 65) {
      flag_byte_stream_.push_back(on_curve_bit + 20 +
                                  ((abs_x - 1) & 0x30) +
                                  (((abs_y - 1) & 0x30) >> 2) +
                                  xy_sign_bits);
      glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf));
    } else if (abs_x < 769 && abs_y < 769) {
      flag_byte_stream_.push_back(on_curve_bit + 84 +
                                  12 * (((abs_x - 1) & 0x300) >> 8) +
                                  (((abs_y - 1) & 0x300) >> 6) + xy_sign_bits);
      glyph_stream_.push_back((abs_x - 1) & 0xff);
      glyph_stream_.push_back((abs_y - 1) & 0xff);
    } else if (abs_x < 4096 && abs_y < 4096) {
      flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits);
      glyph_stream_.push_back(abs_x >> 4);
      glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8));
      glyph_stream_.push_back(abs_y & 0xff);
    } else {
      flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits);
      glyph_stream_.push_back(abs_x >> 8);
      glyph_stream_.push_back(abs_x & 0xff);
      glyph_stream_.push_back(abs_y >> 8);
      glyph_stream_.push_back(abs_y & 0xff);
    }
  }

  std::vector<uint8_t> n_contour_stream_;
  std::vector<uint8_t> n_points_stream_;
  std::vector<uint8_t> flag_byte_stream_;
  std::vector<uint8_t> composite_stream_;
  std::vector<uint8_t> bbox_bitmap_;
  std::vector<uint8_t> bbox_stream_;
  std::vector<uint8_t> glyph_stream_;
  std::vector<uint8_t> instruction_stream_;
  int n_glyphs_;
};

}  // namespace

bool TransformGlyfAndLocaTables(Font* font) {
  // no transform for CFF
  const Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
  const Font::Table* loca_table = font->FindTable(kLocaTableTag);
  if (font->FindTable(kCffTableTag) != NULL
      && glyf_table == NULL
      && loca_table == NULL) {
    return true;
  }
  // Must share neither or both loca/glyf
  if (glyf_table->IsReused() != loca_table->IsReused()) {
    return FONT_COMPRESSION_FAILURE();
  }
  if (glyf_table->IsReused()) {
    return true;
  }
  Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
  Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080];

  int num_glyphs = NumGlyphs(*font);
  GlyfEncoder encoder(num_glyphs);
  for (int i = 0; i < num_glyphs; ++i) {
    Glyph glyph;
    const uint8_t* glyph_data;
    size_t glyph_size;
    if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
        (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
      return FONT_COMPRESSION_FAILURE();
    }
    encoder.Encode(i, glyph);
  }
  encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer);

  const Font::Table* head_table = font->FindTable(kHeadTableTag);
  if (head_table == NULL || head_table->length < 52) {
    return FONT_COMPRESSION_FAILURE();
  }
  transformed_glyf->buffer[7] = head_table->data[51];  // index_format

  transformed_glyf->tag = kGlyfTableTag ^ 0x80808080;
  transformed_glyf->length = transformed_glyf->buffer.size();
  transformed_glyf->data = transformed_glyf->buffer.data();

  transformed_loca->tag = kLocaTableTag ^ 0x80808080;
  transformed_loca->length = 0;
  transformed_loca->data = NULL;

  return true;
}

} // namespace woff2
<commit_msg>remove comment that has been addressed<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Library for preprocessing fonts as part of the WOFF 2.0 conversion.

#include "./transform.h"

#include <complex>  // for std::abs

#include "./buffer.h"
#include "./font.h"
#include "./glyph.h"
#include "./table_tags.h"
#include "./variable_length.h"

namespace woff2 {

namespace {

const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;

void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) {
  if (len == 0) return;
  size_t offset = out->size();
  out->resize(offset + len);
  memcpy(&(*out)[offset], data, len);
}

void WriteBytes(std::vector<uint8_t>* out, const std::vector<uint8_t>& in) {
  for (int i = 0; i < in.size(); ++i) {
    out->push_back(in[i]);
  }
}

void WriteUShort(std::vector<uint8_t>* out, int value) {
  out->push_back(value >> 8);
  out->push_back(value & 255);
}

void WriteLong(std::vector<uint8_t>* out, int value) {
  out->push_back((value >> 24) & 255);
  out->push_back((value >> 16) & 255);
  out->push_back((value >> 8) & 255);
  out->push_back(value & 255);
}

// Glyf table preprocessing, based on
// GlyfEncoder.java
class GlyfEncoder {
 public:
  explicit GlyfEncoder(int num_glyphs)
      : n_glyphs_(num_glyphs) {
    bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2);
  }

  bool Encode(int glyph_id, const Glyph& glyph) {
    if (glyph.composite_data_size > 0) {
      WriteCompositeGlyph(glyph_id, glyph);
    } else if (glyph.contours.size() > 0) {
      WriteSimpleGlyph(glyph_id, glyph);
    } else {
      WriteUShort(&n_contour_stream_, 0);
    }
    return true;
  }

  void GetTransformedGlyfBytes(std::vector<uint8_t>* result) {
    WriteLong(result, 0);  // version
    WriteUShort(result, n_glyphs_);
    WriteUShort(result, 0);  // index_format, will be set later
    WriteLong(result, n_contour_stream_.size());
    WriteLong(result, n_points_stream_.size());
    WriteLong(result, flag_byte_stream_.size());
    WriteLong(result, glyph_stream_.size());
    WriteLong(result, composite_stream_.size());
    WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size());
    WriteLong(result, instruction_stream_.size());
    WriteBytes(result, n_contour_stream_);
    WriteBytes(result, n_points_stream_);
    WriteBytes(result, flag_byte_stream_);
    WriteBytes(result, glyph_stream_);
    WriteBytes(result, composite_stream_);
    WriteBytes(result, bbox_bitmap_);
    WriteBytes(result, bbox_stream_);
    WriteBytes(result, instruction_stream_);
  }

 private:
  void WriteInstructions(const Glyph& glyph) {
    Write255UShort(&glyph_stream_, glyph.instructions_size);
    WriteBytes(&instruction_stream_,
               glyph.instructions_data, glyph.instructions_size);
  }

  bool ShouldWriteSimpleGlyphBbox(const Glyph& glyph) {
    if (glyph.contours.empty() || glyph.contours[0].empty()) {
      return glyph.x_min || glyph.y_min || glyph.x_max || glyph.y_max;
    }

    int16_t x_min = glyph.contours[0][0].x;
    int16_t y_min = glyph.contours[0][0].y;
    int16_t x_max = x_min;
    int16_t y_max = y_min;
    for (const auto& contour : glyph.contours) {
      for (const auto& point : contour) {
        if (point.x < x_min) x_min = point.x;
        if (point.x > x_max) x_max = point.x;
        if (point.y < y_min) y_min = point.y;
        if (point.y > y_max) y_max = point.y;
      }
    }

    if (glyph.x_min != x_min)
      return true;
    if (glyph.y_min != y_min)
      return true;
    if (glyph.x_max != x_max)
      return true;
    if (glyph.y_max != y_max)
      return true;

    return false;
  }

  void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) {
    int num_contours = glyph.contours.size();
    WriteUShort(&n_contour_stream_, num_contours);
    if (ShouldWriteSimpleGlyphBbox(glyph)) {
      WriteBbox(glyph_id, glyph);
    }
    for (int i = 0; i < num_contours; i++) {
      Write255UShort(&n_points_stream_, glyph.contours[i].size());
    }
    int lastX = 0;
    int lastY = 0;
    for (int i = 0; i < num_contours; i++) {
      int num_points = glyph.contours[i].size();
      for (int j = 0; j < num_points; j++) {
        int x = glyph.contours[i][j].x;
        int y = glyph.contours[i][j].y;
        int dx = x - lastX;
        int dy = y - lastY;
        WriteTriplet(glyph.contours[i][j].on_curve, dx, dy);
        lastX = x;
        lastY = y;
      }
    }
    if (num_contours > 0) {
      WriteInstructions(glyph);
    }
  }

  void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) {
    WriteUShort(&n_contour_stream_, -1);
    WriteBbox(glyph_id, glyph);
    WriteBytes(&composite_stream_,
               glyph.composite_data,
               glyph.composite_data_size);
    if (glyph.have_instructions) {
      WriteInstructions(glyph);
    }
  }

  void WriteBbox(int glyph_id, const Glyph& glyph) {
    bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7);
    WriteUShort(&bbox_stream_, glyph.x_min);
    WriteUShort(&bbox_stream_, glyph.y_min);
    WriteUShort(&bbox_stream_, glyph.x_max);
    WriteUShort(&bbox_stream_, glyph.y_max);
  }

  void WriteTriplet(bool on_curve, int x, int y) {
    int abs_x = std::abs(x);
    int abs_y = std::abs(y);
    int on_curve_bit = on_curve ? 0 : 128;
    int x_sign_bit = (x < 0) ? 0 : 1;
    int y_sign_bit = (y < 0) ? 0 : 1;
    int xy_sign_bits = x_sign_bit + 2 * y_sign_bit;
    if (x == 0 && abs_y < 1280) {
      flag_byte_stream_.push_back(on_curve_bit +
                                  ((abs_y & 0xf00) >> 7) + y_sign_bit);
      glyph_stream_.push_back(abs_y & 0xff);
    } else if (y == 0 && abs_x < 1280) {
      flag_byte_stream_.push_back(on_curve_bit + 10 +
                                  ((abs_x & 0xf00) >> 7) + x_sign_bit);
      glyph_stream_.push_back(abs_x & 0xff);
    } else if (abs_x < 65 && abs_y < 65) {
      flag_byte_stream_.push_back(on_curve_bit + 20 +
                                  ((abs_x - 1) & 0x30) +
                                  (((abs_y - 1) & 0x30) >> 2) +
                                  xy_sign_bits);
      glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf));
    } else if (abs_x < 769 && abs_y < 769) {
      flag_byte_stream_.push_back(on_curve_bit + 84 +
                                  12 * (((abs_x - 1) & 0x300) >> 8) +
                                  (((abs_y - 1) & 0x300) >> 6) + xy_sign_bits);
      glyph_stream_.push_back((abs_x - 1) & 0xff);
      glyph_stream_.push_back((abs_y - 1) & 0xff);
    } else if (abs_x < 4096 && abs_y < 4096) {
      flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits);
      glyph_stream_.push_back(abs_x >> 4);
      glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8));
      glyph_stream_.push_back(abs_y & 0xff);
    } else {
      flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits);
      glyph_stream_.push_back(abs_x >> 8);
      glyph_stream_.push_back(abs_x & 0xff);
      glyph_stream_.push_back(abs_y >> 8);
      glyph_stream_.push_back(abs_y & 0xff);
    }
  }

  std::vector<uint8_t> n_contour_stream_;
  std::vector<uint8_t> n_points_stream_;
  std::vector<uint8_t> flag_byte_stream_;
  std::vector<uint8_t> composite_stream_;
  std::vector<uint8_t> bbox_bitmap_;
  std::vector<uint8_t> bbox_stream_;
  std::vector<uint8_t> glyph_stream_;
  std::vector<uint8_t> instruction_stream_;
  int n_glyphs_;
};

}  // namespace

bool TransformGlyfAndLocaTables(Font* font) {
  // no transform for CFF
  const Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
  const Font::Table* loca_table = font->FindTable(kLocaTableTag);
  if (font->FindTable(kCffTableTag) != NULL
      && glyf_table == NULL
      && loca_table == NULL) {
    return true;
  }
  // Must share neither or both loca/glyf
  if (glyf_table->IsReused() != loca_table->IsReused()) {
    return FONT_COMPRESSION_FAILURE();
  }
  if (glyf_table->IsReused()) {
    return true;
  }
  Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
  Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080];

  int num_glyphs = NumGlyphs(*font);
  GlyfEncoder encoder(num_glyphs);
  for (int i = 0; i < num_glyphs; ++i) {
    Glyph glyph;
    const uint8_t* glyph_data;
    size_t glyph_size;
    if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
        (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
      return FONT_COMPRESSION_FAILURE();
    }
    encoder.Encode(i, glyph);
  }
  encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer);

  const Font::Table* head_table = font->FindTable(kHeadTableTag);
  if (head_table == NULL || head_table->length < 52) {
    return FONT_COMPRESSION_FAILURE();
  }
  transformed_glyf->buffer[7] = head_table->data[51];  // index_format

  transformed_glyf->tag = kGlyfTableTag ^ 0x80808080;
  transformed_glyf->length = transformed_glyf->buffer.size();
  transformed_glyf->data = transformed_glyf->buffer.data();

  transformed_loca->tag = kLocaTableTag ^ 0x80808080;
  transformed_loca->length = 0;
  transformed_loca->data = NULL;

  return true;
}

} // namespace woff2
<|endoftext|>


<commit_before>//
//  fdf2nii_main.cpp
//
//  Created by Tobias Wood on 29/08/2013.
//
//

#include <string>
#include <iostream>
#include <iterator>
#include <getopt.h>

#include "fdf.h"
#include "Nifti.h"

using namespace std;

static bool zip = false, procpar = false;
static double scale = 1.;
static string outPrefix;
static struct option long_options[] =
{
	{"scale", required_argument, 0, 's'},
	{"out", required_argument, 0, 'o'},
	{"zip", no_argument, 0, 'z'},
	{"procpar", no_argument, 0, 'p'},
	{0, 0, 0, 0}
};

const string usage {
"fdf2nii - A utility to convert Agilent fdf files to nifti.\n\
\n\
Usage: fdf2nii [opts] image1 image2 ... imageN\n\
image1 to imageN are paths to the Agilent .img folders, not individual .fdf\n\
files\n\
Options:\n\
 -s, --scale:   Scale factor for image dimensions (set to 10 for use with SPM).\n\
 -o, --out:     Specify an output prefix.\n\
 -z, --zip:     Create .nii.gz files\n\
 -p, --procpar: Embed procpar in the nifti header.\n"
};

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "s:o:zp", long_options, &indexptr)) != -1) {
		switch (c) {
			case 0: break; // It was an option that just sets a flag.
			case 's': scale = atof(optarg); break;
			case 'o': outPrefix = string(optarg); break;
			case 'z': zip = true; break;
			case 'p': procpar = true; break;
			default: cout << "Unknown option " << optarg << endl;
		}
	}

	if ((argc - optind) <= 0) {
		cout << "No input images specified." << endl << usage << endl;
		exit(EXIT_FAILURE);
	}
	while (optind < argc) {
		string inPath(argv[optind]);
		optind++;
		string outPath = outPrefix + inPath.substr(inPath.find_last_of("/") + 1, inPath.find_last_of(".")) + ".nii";
		if (zip)
			outPath += ".gz";
		cout << "Converting " << inPath << " to " << outPath << endl;
		try {
			Agilent::fdfImage input(inPath);
			try {
				Nifti::File output(input.dim(0), input.dim(1), input.dim(2), input.dim(3),
								   input.voxdim(0) * scale, input.voxdim(1) * scale, input.voxdim(2) * scale, 1.,
								   DT_FLOAT32, input.ijk_to_xyz().cast<float>());
				if (procpar) {
					ifstream pp_file(inPath + "/procpar", ios::binary);
					pp_file.seekg(ios::end);
					size_t fileSize = pp_file.tellg();
					pp_file.seekg(ios::beg);
					vector<char> data; data.reserve(fileSize);
					data.assign(istreambuf_iterator<char>(pp_file), istreambuf_iterator<char>());
					output.addExtension(NIFTI_ECODE_COMMENT, data);
				}
				output.open(outPath, Nifti::Modes::Write);
				for (size_t v = 0; v < input.dim(3); v++) {
					output.writeVolume<float>(v, input.readVolume<float>(v));
				}
				output.close();
			} catch (exception &e) {
				cerr << "Error, skipping to next input. " << e.what() << outPath << endl;
				continue;
			}
		} catch (exception &e) {
			cerr << "Error, skipping to next input. " << e.what() << endl;
			continue;
		}
	}
    return EXIT_SUCCESS;
}
<commit_msg>Fixed a bug with working out the output path.<commit_after>//
//  fdf2nii_main.cpp
//
//  Created by Tobias Wood on 29/08/2013.
//
//

#include <string>
#include <iostream>
#include <iterator>
#include <getopt.h>

#include "fdf.h"
#include "Nifti.h"

using namespace std;

static bool zip = false, procpar = false;
static double scale = 1.;
static string outPrefix;
static struct option long_options[] =
{
	{"scale", required_argument, 0, 's'},
	{"out", required_argument, 0, 'o'},
	{"zip", no_argument, 0, 'z'},
	{"procpar", no_argument, 0, 'p'},
	{0, 0, 0, 0}
};

const string usage {
"fdf2nii - A utility to convert Agilent fdf files to nifti.\n\
\n\
Usage: fdf2nii [opts] image1 image2 ... imageN\n\
image1 to imageN are paths to the Agilent .img folders, not individual .fdf\n\
files\n\
Options:\n\
 -s, --scale:   Scale factor for image dimensions (set to 10 for use with SPM).\n\
 -o, --out:     Specify an output prefix.\n\
 -z, --zip:     Create .nii.gz files\n\
 -p, --procpar: Embed procpar in the nifti header.\n"
};

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "s:o:zp", long_options, &indexptr)) != -1) {
		switch (c) {
			case 0: break; // It was an option that just sets a flag.
			case 's': scale = atof(optarg); break;
			case 'o': outPrefix = string(optarg); break;
			case 'z': zip = true; break;
			case 'p': procpar = true; break;
			default: cout << "Unknown option " << optarg << endl;
		}
	}

	if ((argc - optind) <= 0) {
		cout << "No input images specified." << endl << usage << endl;
		exit(EXIT_FAILURE);
	}
	while (optind < argc) {
		string inPath(argv[optind]);
		optind++;
		size_t fileSep = inPath.find_last_of("/") + 1;
		size_t fileExt = inPath.find_last_of(".");
		if (inPath.substr(fileExt) != ".img") {
			cerr << inPath << " is not a valid .img folder. Skipping." << endl;
			continue;
		}
		string outPath = outPrefix + inPath.substr(fileSep, fileExt - fileSep) + ".nii";

		if (zip)
			outPath += ".gz";
		cout << "Converting " << inPath << " to " << outPath << endl;
		try {
			Agilent::fdfImage input(inPath);
			try {
				Nifti::File output(input.dim(0), input.dim(1), input.dim(2), input.dim(3),
								   input.voxdim(0) * scale, input.voxdim(1) * scale, input.voxdim(2) * scale, 1.,
								   DT_FLOAT32, input.ijk_to_xyz().cast<float>());
				if (procpar) {
					ifstream pp_file(inPath + "/procpar", ios::binary);
					pp_file.seekg(ios::end);
					size_t fileSize = pp_file.tellg();
					pp_file.seekg(ios::beg);
					vector<char> data; data.reserve(fileSize);
					data.assign(istreambuf_iterator<char>(pp_file), istreambuf_iterator<char>());
					output.addExtension(NIFTI_ECODE_COMMENT, data);
				}
				output.open(outPath, Nifti::Modes::Write);
				for (size_t v = 0; v < input.dim(3); v++) {
					output.writeVolume<float>(v, input.readVolume<float>(v));
				}
				output.close();
			} catch (exception &e) {
				cerr << "Error, skipping to next input. " << e.what() << outPath << endl;
				continue;
			}
		} catch (exception &e) {
			cerr << "Error, skipping to next input. " << e.what() << endl;
			continue;
		}
	}
    return EXIT_SUCCESS;
}
<|endoftext|>