[
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8.11)\n\nset(PROJECT_NAME airwave)\nproject(${PROJECT_NAME})\n\n# Project version\nset(VERSION_MAJOR 1)\nset(VERSION_MINOR 3)\nset(VERSION_PATCH 3)\n\n# Set plugin shared library base name\nset(PLUGIN_BASENAME ${PROJECT_NAME}-plugin)\n\n# Set host binary base name\nset(HOST_BASENAME ${PROJECT_NAME}-host)\n\n# Set installation path\nset(INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH \"\")\n\n# Check for 64-bit platform\nif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\tset(PLATFORM_64BIT 1)\nendif()\n\n# Generate config header\nconfigure_file(\n\t${CMAKE_CURRENT_SOURCE_DIR}/config.h.in\n\t${CMAKE_CURRENT_BINARY_DIR}/src/common/config.h\n)\n\n\n# Check the build type and ask the user to set concrete one\nif(NOT CMAKE_BUILD_TYPE)\n   set(CMAKE_BUILD_TYPE RelWithDebInfo)\n   message(WARNING \"CMAKE_BUILD_TYPE is not set, forcing to RelWithDebInfo\")\nendif()\n\n\n# Set compiler flags\nif(${CMAKE_CXX_COMPILER_ID} MATCHES \"GNU\" OR ${CMAKE_CXX_COMPILER_ID} MATCHES \"Clang\")\n    set(CMAKE_CXX_FLAGS \"-std=c++11 -Wall -Wextra -D__WIDL_objidl_generated_name_0000000C=\")\n\tset(CMAKE_CXX_FLAGS_DEBUG \"-O0 -g3\")\n\tset(CMAKE_CXX_FLAGS_RELEASE \"-O3\")\n\tset(CMAKE_CXX_FLAGS_RELWITHDEBINFO \"-O3 -g3\")\n\tset(CMAKE_CXX_FLAGS_MINSIZEREL \"-Os\")\nendif()\n\n\n# Setup path, where CMake would search for additional modules\nset(CMAKE_MODULE_PATH\n\t${CMAKE_MODULE_PATH}\n\t${CMAKE_CURRENT_SOURCE_DIR}/cmake\n)\n\n# Configure the VST SDK path\nset(VSTSDK_PATH ${PROJECT_SOURCE_DIR}/VST3\\ SDK CACHE PATH\n\t\"Path to the Steinberg VST Audio Plugins SDK\")\n\nmessage(STATUS \"VSTSDK_PATH is set to \" ${VSTSDK_PATH})\n\nfind_path(VSTSDK_INCLUDE_DIR NAMES aeffect.h aeffectx.h\n\tPATHS \"${VSTSDK_PATH}/pluginterfaces/vst2.x/\")\n\nif(NOT VSTSDK_INCLUDE_DIR)\n\tmessage(FATAL_ERROR \"VST SDK is not found. You should set the VSTSDK_PATH variable \"\n\t\t\t\"to the directory, where your copy of the VST SDK is located.\")\nendif()\n\nmessage(STATUS \"VST SDK headers are found in ${VSTSDK_INCLUDE_DIR}\")\n\ninclude_directories(\n\t${CMAKE_CURRENT_BINARY_DIR}/src\n\t${CMAKE_CURRENT_SOURCE_DIR}/src\n)\n\nadd_subdirectory(src/plugin)\nadd_subdirectory(src/host)\nadd_subdirectory(src/manager)\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Anton Kalmykov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README",
    "content": "About\nAirwave is a wine based VST bridge, that allows for the use of Windows 32- and 64-bit VST 2.4 audio plugins with Linux VST hosts.\nDue to the use of shared memory, only one extra copying is made for each data transfer. Airwave also uses the XEMBED protocol to correctly embed the plugin editor into the host window.\n\nRequirements\n- wine, supporting XEMBED protocol (versions greater than 1.7.19 were tested,\nbut earlier versions also may work)\n- libmagic\n- Qt5 for the airwave manager application (GUI)\n\nBuilding the source\n1. Install the required packages: multilib-enabled GCC, cmake, git, wine, Qt5, libmagic.\n  Arch Linux (x86_64) example:\n    sudo pacman -S gcc-multilib cmake git wine qt5-base\n\n  Fedora 20 (x86_64) example:\n    sudo yum -y install gcc-c++ git cmake wine wine-devel wine-devel.i686 file file-devel libX11-devel libX11-devel.i686 qt5-devel glibc-devel.i686 glibc-devel\n\n  Ubuntu 14.04 (x86_64) example:\n    sudo apt-get install git cmake gcc-multilib g++-multilib libx11-dev libx11-dev:i386 qt5-default libmagic-dev\n    sudo add-apt-repository ppa:ubuntu-wine/ppa\n    sudo apt-get update\n    sudo apt-get install wine1.7 wine1.7-dev\n\n2. Get the VST Audio Plugins SDK from Steinberg (http://www.steinberg.net/en/company/developers.html). I cannot distribute it myself due to the license restrictions.\n\n3. Unpack the VST SDK archive. Further I'll assume that you have unpacked it in your home directory: ${HOME}/VST3\\ SDK.\n\n4. Clone the airwave GIT repository\n  git clone https://github.com/phantom-code/airwave.git\n\n5. Go to the airwave source directory and execute the following commands:\n  mkdir build && cd build\n  cmake -DCMAKE_BUILD_TYPE=\"Release\" -DCMAKE_INSTALL_PREFIX=/opt/airwave -DVSTSDK_PATH=${HOME}/VST3\\ SDK ..\n  make\n  sudo make install\n\nOf course, you can change the CMAKE_INSTALL_PREFIX as you like.\n\nUsage\n1. Run the airwave-manager\n2. Press the \"Create link\" button on the toolbar.\n3. Select desired wine loader and wine prefix in the appropriate combo boxes.\n4. Enter a path to VST plugin DLL file in the \"VST plugin\" field (you can use the \"Browse\" button for convenience). Note, that the path is relative to the selected wine prefix.\n5. Enter a \"Link location\" path (the directory, where your VST host looks for the plugins).\n6. Enter a link name, if you don't like the auto-suggested one.\n7. Select a desired log level for this link. The higher the log level, the more messages you'll receive. The 'default' log level is a special value. It corresponds to the 'Default log level' value from the settings dialog. In most cases, the 'default' log level is the right choice. For maximum performance do not use a higher level than 'trace'.\n7. Press the OK button. At this point, your VST host should be able to find a new plugin inside of the \"Link location\" directory.\n\nNote: After you have created the link you cannot move/rename it with a file manager. All updates have to be done inside the airwave-manager. Also, you should update your links after updating the airwave itself. This could be achived by pressing the \"Update links\" button.\n\nUnder the hood\nThe bridge consists of four components:\n- Plugin endpoint (airwave-plugin-<arch>.so)\n- Host endpoint (airwave-host-<arch>.exe.so and airwave-host-<arch>.exe launcher script)\n- Configuration file (${XDG_CONFIG_PATH}/airwave/airwave.conf)\n- GUI configurator (airwave-manager)\n\nWhen the airwave-plugin is loaded by the VST host, it obtains its absolute path and use it as the key to get the linked VST DLL from the configuration. Then it starts the airwave-host process and passes the path to the linked VST file. The airwave-host loads the VST DLL and works as a fake VST host. Starting from this point, the airwave-plugin and airwave-host act together like a proxy, translating commands between the native VST host and the Windows VST plugin.\n\nKnown issues\n- Due to a bug in wine, there is some hacking involved when embedding the editor window. There is a chance that you get a black window instead of the plugin GUI. Also some areas might not update correctly when increasing the window size. On some hosts (Bitwig Studio for example) this can be solved by closing and re-opening the plugin window.\n"
  },
  {
    "path": "README.md",
    "content": "## About\nAirwave is a [wine](https://www.winehq.org/) based VST bridge, that allows for the use of Windows 32- and 64-bit VST 2.4 audio plugins with Linux VST hosts.\nDue to the use of shared memory, only one extra copying is made for each data transfer. Airwave also uses the XEMBED protocol to correctly embed the plugin editor into the host window.\n\n## Requirements\n- wine, supporting XEMBED protocol (versions greater than 1.7.19 were tested,\nbut earlier versions also may work). To solve the blank window issue you can apply [this patch](https://github.com/phantom-code/airwave/blob/develop/fix-xembed-wine-windows.patch) to wine.\n- libmagic\n- Qt5 for the airwave manager application (GUI)\n\n## Building the source\n1. Install the required packages: multilib-enabled GCC, cmake, git, wine, Qt5, libmagic.\n  * **Arch Linux (x86_64)** example:\n    ```\n    sudo pacman -S gcc-multilib cmake git wine qt5-base\n    ```\n\n  * **Fedora 20 (x86_64)** example:\n    ```\n    sudo yum -y install gcc-c++ git cmake wine wine-devel wine-devel.i686 file file-devel libX11-devel libX11-devel.i686 qt5-devel glibc-devel.i686 glibc-devel\n    ```\n\n  * **Ubuntu 14.04 (x86_64)** example:\n    ```\n    sudo apt-get install git cmake gcc-multilib g++-multilib libx11-dev libx11-dev:i386 qt5-default libmagic-dev\n    sudo add-apt-repository ppa:ubuntu-wine/ppa\n    sudo apt-get update\n    sudo apt-get install wine1.7 wine1.7-dev\n    ```\n2. Get the VST Audio Plugins SDK [from Steinberg](http://www.steinberg.net/en/company/developers.html). I cannot distribute it myself due to the license restrictions.\n\n3. Unpack the VST SDK archive. Further I'll assume that you have unpacked it in your home directory: ${HOME}/VST3\\ SDK.\n\n4. Clone the airwave GIT repository\n  ```\n  git clone https://github.com/phantom-code/airwave.git\n  ```\n\n5. Go to the airwave source directory and execute the following commands:\n  ```\n  mkdir build && cd build\n  cmake -DCMAKE_BUILD_TYPE=\"Release\" -DCMAKE_INSTALL_PREFIX=/opt/airwave -DVSTSDK_PATH=${HOME}/VST3\\ SDK ..\n  make\n  sudo make install\n  ```\n\nOf course, you can change the CMAKE_INSTALL_PREFIX as you like.\n\n## Usage\n1. Run the airwave-manager\n2. Press the \"Create link\" button on the toolbar.\n3. Select desired wine loader and wine prefix in the appropriate combo boxes.\n4. Enter a path to VST plugin DLL file in the \"VST plugin\" field (you can use the \"Browse\" button for convenience). Note, that the path is relative to the selected wine prefix.\n5. Enter a \"Link location\" path (the directory, where your VST host looks for the plugins).\n6. Enter a link name, if you don't like the auto-suggested one.\n7. Select a desired log level for this link. The higher the log level, the more messages you'll receive. The 'default' log level is a special value. It corresponds to the 'Default log level' value from the settings dialog. In most cases, the 'default' log level is the right choice. For maximum performance do not use a higher level than 'trace'.\n7. Press the \"OK\" button. At this point, your VST host should be able to find a new plugin inside of the \"Link location\" directory.\n\n**Note:** After you have created the link you cannot move/rename it with a file manager. All updates have to be done inside the airwave-manager. Also, you should update your links after updating the airwave itself. This could be achived by pressing the \"Update links\" button.\n\n## Under the hood\nThe bridge consists of four components:\n- Plugin endpoint (airwave-plugin.so)\n- Host endpoint (airwave-host-{arch}.exe.so and airwave-host-{arch}.exe launcher script)\n- Configuration file (${XDG_CONFIG_PATH}/airwave/airwave.conf)\n- GUI configurator (airwave-manager)\n\nWhen the airwave-plugin is loaded by the VST host, it obtains its absolute path and use it as the key to get the linked VST DLL from the configuration. Then it starts the airwave-host process and passes the path to the linked VST file. The airwave-host loads the VST DLL and works as a fake VST host. Starting from this point, the airwave-plugin and airwave-host act together like a proxy, translating commands between the native VST host and the Windows VST plugin.\n\n## Known issues\n- Due to a bug in wine, there is some hacking involved when embedding the editor window. There is a chance that you get a black window instead of the plugin GUI. Also some areas might not update correctly when increasing the window size. You can workaround this issue by patching wine with [this patch](https://github.com/phantom-code/airwave/blob/develop/fix-xembed-wine-windows.patch).\n\n## Compatibility\nThe following list is not complete. It contains only plugins, that have been tested by me or by people, who sent me a report.\nPlease note about d2d1.dll mentioned in the list: currently I know that only one version of d2d1.dll is working:  \nversion: 6.1.7601.17514  \nsize: 827904 bytes  \nmd5 hash: 3e0a1bf9e17349a8392455845721f92f  \nIf you will get success with another version, please contact me and I will update this information.\n\n VST-Plugins | works? | Notes |\n------------:|:----------:|:-------|\n AlgoMusic CZynthia | yes |\n Aly James LAB OB-Xtreme | yes |\n Analogic Delay by interrruptor | yes |\n Bionic Delay by interrruptor | yes |\n Blue Cat Audio Oscilloscope Multi | no | doesn't work with wine\n  Cableguys Volume Shaper | yes | you need to install native d2d1.dll and override it in winecfg\n Credland Audio BigKick | yes | you need to install native d2d1.dll and override it in winecfg\n FabFilter plugins | yes | haven't tested them all\n Green Oak Software Crystal | yes |\n Image-Line Harmless | yes |\n Image-Line Sytrus | yes |\n LennarDigital Sylenth1 | yes | you need to override d2d1.dll in winecfg\n LePou Plugins | yes | LeCab2 has slight GUI redrawing issues\n NI Absynth | yes |\n NI FM8 | yes |\n NI Guitar Rig 5 | yes | activation doesn't work\n NI Kontakt 5 | mostly | up to v5.3.1, can import libraries only in Windows XP mode\n NI Massive | yes | only 32-bit\n NI Reaktor 5 | yes |\n Magnus Choir | yes |\n Martin Lüders pg8x | yes |\n Meesha Damatriks | yes |\n Odo Synths Double Six | partly | GUI issues\n Peavey Revalver Mark III.V | yes |\n ReFX Nexus2 | yes |\n ReFX Vanguard | yes |\n Reveal Sound Spire | yes | starting from 1.0.19 you need to override d2d1.dll in winecfg\n Sonic Academy A.N.A. | yes |\n Sonic Academy KICK | yes |\n Sonic Cat LFX-1310 | yes |\n Sonic Charge Cyclone | yes |\n Smartelectronix s(M)exoscope | yes |\n Spectrasonics Omnisphere | yes |\n Spectrasonics Omnisphere 2 | yes | May require copying STEAM dir manually to place on install. Runs too slow with many presets to be usable on a decent laptop.\n SQ8L by Siegfried Kullmann | yes |\n SuperWave P8 | yes |\n Synapse Audio DUNE 2 | yes |\n Synth1 by Ichiro Toda | yes |\n Tone2 FireBird | yes |\n Tone2 Nemesis | yes |\n Tone2 Saurus | yes |\n u-he plugins | yes | Linux version is also available\n Variety of Sound plugins | yes |\n Voxengo plugins | mostly | inter plugin routing doesn't work (architecture issue)\n Xfer Serum | yes | install native GDI+ (run `winetricks gdiplus`)\n EZDrummer2, BFD3, XLN AD2 | yes | host need multi-channel support\n"
  },
  {
    "path": "cmake/FindLibDl.cmake",
    "content": "# - Find libdl\n# Find the native LIBDL includes and library\n#\n#  LIBDL_INCLUDE_DIR - where to find dlfcn.h, etc.\n#  LIBDL_LIBRARIES   - List of libraries when using libdl.\n#  LIBDL_FOUND       - True if libdl found.\n\n\nif(LIBDL_INCLUDE_DIR)\n\t# Already in cache, be silent\n\tset(LIBDL_FIND_QUIETLY TRUE)\nendif(LIBDL_INCLUDE_DIR)\n\nfind_path(LIBDL_INCLUDE_DIR dlfcn.h)\n\nset(LIBDL_NAMES dl libdl ltdl libltdl)\nfind_library(LIBDL_LIBRARY NAMES ${LIBDL_NAMES})\n\n# handle the QUIETLY and REQUIRED arguments and set LIBDL_FOUND to TRUE if\n# all listed variables are TRUE\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(LibDL DEFAULT_MSG LIBDL_LIBRARY LIBDL_INCLUDE_DIR)\n\nif(LIBDL_FOUND)\n\tset(LIBDL_LIBRARIES ${LIBDL_LIBRARY})\nelse(LIBDL_FOUND)\n\tset(LIBDL_LIBRARIES)\nendif(LIBDL_FOUND)\n\nmark_as_advanced(LIBDL_LIBRARY LIBDL_INCLUDE_DIR)\n"
  },
  {
    "path": "cmake/FindLibMagic.cmake",
    "content": "# - Try to find libmagic header and library\n#\n# Usage of this module as follows:\n#\n#     find_package(LibMagic)\n#\n# Variables used by this module, they can change the default behaviour and need\n# to be set before calling find_package:\n#\n#  LIBMAGIC_ROOT_DIR         Set this variable to the root installation of\n#                            libmagic if the module has problems finding the\n#                            proper installation path.\n#\n# Variables defined by this module:\n#\n#  LIBMAGIC_FOUND              System has libmagic, magic.h, and file\n#  LIBMAGIC_FILE_EXE           Path to the 'file' command\n#  LIBMAGIC_VERSION            Version of libmagic\n#  LIBMAGIC_LIBRARY            The libmagic library\n#  LIBMAGIC_INCLUDE_DIR        The location of magic.h\n\nfind_path(LIBMAGIC_ROOT_DIR\n    NAMES include/magic.h\n)\n\nif (${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n    # the static version of the library is preferred on OS X for the\n    # purposes of making packages (libmagic doesn't ship w/ OS X)\n    set(LIBMAGIC_NAMES libmagic.a magic)\nelse ()\n    set(LIBMAGIC_NAMES magic)\nendif ()\n\nfind_file(LIBMAGIC_FILE_EXE\n    NAMES file\n    HINTS ${LIBMAGIC_ROOT_DIR}/bin\n)\n\nfind_library(LIBMAGIC_LIBRARY\n    NAMES ${LIBMAGIC_NAMES}\n    HINTS ${LIBMAGIC_ROOT_DIR}/lib\n)\n\nfind_path(LIBMAGIC_INCLUDE_DIR\n    NAMES magic.h\n    HINTS ${LIBMAGIC_ROOT_DIR}/include\n)\n\nif (LIBMAGIC_FILE_EXE)\n    execute_process(COMMAND \"${LIBMAGIC_FILE_EXE}\" --version\n                    ERROR_VARIABLE  LIBMAGIC_VERSION\n                    OUTPUT_VARIABLE LIBMAGIC_VERSION)\n    string(REGEX REPLACE \"^file-([0-9.]+).*$\" \"\\\\1\"\n           LIBMAGIC_VERSION \"${LIBMAGIC_VERSION}\")\n    message(STATUS \"libmagic version: ${LIBMAGIC_VERSION}\")\nelse ()\n    set(LIBMAGIC_VERSION NOTFOUND)\nendif ()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(LibMagic DEFAULT_MSG\n    LIBMAGIC_LIBRARY\n    LIBMAGIC_INCLUDE_DIR\n    LIBMAGIC_FILE_EXE\n    LIBMAGIC_VERSION\n)\n\nmark_as_advanced(\n    LIBMAGIC_ROOT_DIR\n    LIBMAGIC_FILE_EXE\n    LIBMAGIC_VERSION\n    LIBMAGIC_LIBRARY\n    LIBMAGIC_INCLUDE_DIR\n)\n"
  },
  {
    "path": "config.h.in",
    "content": "// This file has been generated automatically by CMake. Do not edit it manually, as all\n// changes will be overwritten in the future.\n\n#ifndef CORE_CONFIG_H\n#define CORE_CONFIG_H\n\n#define PROJECT_NAME \"@PROJECT_NAME@\"\n\n// Program version\n#define VERSION_MAJOR @VERSION_MAJOR@\n#define VERSION_MINOR @VERSION_MINOR@\n#define VERSION_PATCH @VERSION_PATCH@\n#define VERSION_STRING \"@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@\"\n\n// Various constants\n#cmakedefine PLATFORM_64BIT\n#define INSTALL_PREFIX \"@INSTALL_PREFIX@\"\n#define PLUGIN_BASENAME \"@PLUGIN_BASENAME@\"\n#define HOST_BASENAME \"@HOST_BASENAME@\"\n\n\n#endif // CORE_CONFIG_H\n"
  },
  {
    "path": "fix-xembed-wine-windows.patch",
    "content": "diff -Naurb ./wine-1.7.52/dlls/winex11.drv/event.c ./wine-1.7.52-patched/dlls/winex11.drv/event.c\n--- ./wine-1.7.52/dlls/winex11.drv/event.c\t2015-10-02 17:20:05.000000000 +0300\n+++ ./wine-1.7.52-patched/dlls/winex11.drv/event.c\t2015-10-13 14:25:12.000000000 +0300\n@@ -1036,7 +1036,7 @@\n     if (!data->mapped || data->iconic) goto done;\n     if (data->whole_window && !data->managed) goto done;\n     /* ignore synthetic events on foreign windows */\n-    if (event->send_event && !data->whole_window) goto done;\n+    // if (event->send_event && !data->whole_window) goto done;\n     if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)\n     {\n         TRACE( \"win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\\n\",\ndiff -Naurb ./wine-1.7.52/dlls/winex11.drv/window.c ./wine-1.7.52-patched/dlls/winex11.drv/window.c\n--- ./wine-1.7.52/dlls/winex11.drv/window.c\t2015-10-02 17:20:05.000000000 +0300\n+++ ./wine-1.7.52-patched/dlls/winex11.drv/window.c\t2015-10-13 15:59:29.968686454 +0300\n@@ -1131,7 +1131,11 @@\n             if (data->surface && data->vis.visualid != default_visual.visualid)\n                 data->surface->funcs->flush( data->surface );\n         }\n-        else set_xembed_flags( data, XEMBED_MAPPED );\n+        else {\n+            XMapWindow( data->display, data->whole_window );\n+            XFlush( data->display );\n+            set_xembed_flags( data, XEMBED_MAPPED );\n+        }\n \n         data->mapped = TRUE;\n         data->iconic = (new_style & WS_MINIMIZE) != 0;\n"
  },
  {
    "path": "src/common/dataport.cpp",
    "content": "#include \"dataport.h\"\n\n#include <cstring>\n#include <sys/ipc.h>\n#include <sys/shm.h>\n#include <sys/stat.h>\n#include \"common/logger.h\"\n\n\nnamespace Airwave {\n\n\nDataPort::DataPort() :\n\tid_(-1),\n\tframeSize_(0),\n\tbuffer_(nullptr)\n{\n}\n\n\nDataPort::~DataPort()\n{\n\tdisconnect();\n}\n\n\nbool DataPort::create(size_t frameSize)\n{\n\tif(!isNull()) {\n\t\tERROR(\"Unable to create, port is already created\");\n\t\treturn false;\n\t}\n\n\tsize_t bufferSize = sizeof(ControlBlock) + frameSize;\n\n\tid_ = shmget(IPC_PRIVATE, bufferSize, S_IRUSR | S_IWUSR);\n\tif(id_ < 0) {\n\t\tERROR(\"Unable to allocate %d bytes of shared memory\", bufferSize);\n\t\treturn false;\n\t}\n\n\tbuffer_ = shmat(id_, nullptr, 0);\n\tif(buffer_ == reinterpret_cast<void*>(-1)) {\n\t\tERROR(\"Unable to attach shared memory segment with id %d\", id_);\n\t\tshmctl(id_, IPC_RMID, nullptr);\n\t\tid_ = -1;\n\t\treturn false;\n\t}\n\n\tnew (controlBlock()) ControlBlock;\n\n\tframeSize_ = frameSize;\n\treturn true;\n}\n\n\nbool DataPort::connect(int id)\n{\n\tif(!isNull()) {\n\t\tERROR(\"Unable to connect on already initialized port\");\n\t\treturn false;\n\t}\n\n\tbuffer_ = shmat(id, nullptr, 0);\n\tif(buffer_ == reinterpret_cast<void*>(-1)) {\n\t\tERROR(\"Unable to attach shared memory segment with id %d\", id);\n\t\treturn false;\n\t}\n\n\tshmid_ds info;\n\tif(shmctl(id, IPC_STAT, &info) != 0) {\n\t\tERROR(\"Unable to get info about shared memory segment with id %d\", id);\n\t\tshmdt(buffer_);\n\t\tid_ = -1;\n\t\treturn false;\n\t}\n\n\tsize_t bufferSize = info.shm_segsz;\n\tframeSize_ = bufferSize - sizeof(ControlBlock);\n\n\tid_ = id;\n\treturn true;\n}\n\n\nvoid DataPort::disconnect()\n{\n\tif(!isNull()) {\n\t\tif(!isConnected()) {\n//\t\t\tControlBlock* control = controlBlock();\n//\t\t\tsem_destroy(&control->request);\n//\t\t\tsem_destroy(&control->response);\n\t\t}\n\n\t\tshmdt(buffer_);\n\t\tshmctl(id_, IPC_RMID, nullptr);\n\t\tid_ = -1;\n\t\tbuffer_ = nullptr;\n\t\tframeSize_ = 0;\n\t}\n}\n\n\nbool DataPort::isNull() const\n{\n\treturn id_ < 0;\n}\n\n\nbool DataPort::isConnected() const\n{\n\tshmid_ds info;\n\n\tif(shmctl(id_, IPC_STAT, &info) != 0) {\n\t\tERROR(\"Unable to get shared memory segment (%d) info\", id_);\n\t\treturn false;\n\t}\n\n\treturn info.shm_nattch > 1;\n}\n\n\nint DataPort::id() const\n{\n\treturn id_;\n}\n\n\nsize_t DataPort::frameSize() const\n{\n\treturn frameSize_;\n}\n\n\nvoid* DataPort::frameBuffer()\n{\n\treturn controlBlock() + 1;\n}\n\n\nvoid DataPort::sendRequest()\n{\n\tif(!isNull())\n\t\tcontrolBlock()->request.post();\n}\n\n\nvoid DataPort::sendResponse()\n{\n\tif(!isNull())\n\t\tcontrolBlock()->response.post();\n}\n\n\nbool DataPort::waitRequest(int msecs)\n{\n\treturn controlBlock()->request.wait(msecs);\n}\n\n\nbool DataPort::waitResponse(int msecs)\n{\n\treturn controlBlock()->response.wait(msecs);\n}\n\n\nDataPort::ControlBlock* DataPort::controlBlock()\n{\n\treturn static_cast<ControlBlock*>(buffer_);\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/common/dataport.h",
    "content": "#ifndef COMMON_DATAPORT_H\n#define COMMON_DATAPORT_H\n\n#include \"common/event.h\"\n#include \"common/types.h\"\n\n\nnamespace Airwave {\n\n\nclass DataPort {\npublic:\n\tDataPort();\n\t~DataPort();\n\n\tbool create(size_t frameSize);\n\tbool connect(int id);\n\tvoid disconnect();\n\n\tbool isNull() const;\n\tbool isConnected() const;\n\tint id() const;\n\tsize_t frameSize() const;\n\n\tvoid* frameBuffer();\n\n\ttemplate<typename T>\n\tT* frame();\n\n\tvoid sendRequest();\n\tvoid sendResponse();\n\n\tbool waitRequest(int msecs = -1);\n\tbool waitResponse(int msecs = -1);\n\nprivate:\n\tstruct ControlBlock {\n\t\tEvent request;\n\t\tEvent response;\n\t};\n\n\tint id_;\n\tsize_t frameSize_;\n\tvoid* buffer_;\n\n\tControlBlock* controlBlock();\n};\n\n\ntemplate<typename T>\nT* DataPort::frame()\n{\n\treturn static_cast<T*>(frameBuffer());\n}\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_DATAPORT_H\n"
  },
  {
    "path": "src/common/event.cpp",
    "content": "#include \"event.h\"\n\n#include <errno.h>\n#include <syscall.h>\n#include <time.h>\n#include <unistd.h>\n#include <linux/futex.h>\n\n\n#define futex_wait(futex, count, timeout) \\\n\t\t(syscall(SYS_futex, futex, FUTEX_WAIT, count, timeout, nullptr, 0) == 0)\n\n#define futex_post(futex, count) \\\n\t\t(syscall(SYS_futex, futex, FUTEX_WAKE, count, nullptr, nullptr, 0) == 0)\n\n\nEvent::Event() :\n\tcount_(0)\n{\n}\n\n\nEvent::~Event()\n{\n\t// FIXME The current implementation is not correct as it wakes only the one waiter.\n\tfutex_post(&count_, 1);\n}\n\n\nbool Event::wait(int msecs)\n{\n\ttimespec* timeout = nullptr;\n\ttimespec tm;\n\n\tif(msecs >= 0) {\n\t\tint seconds = msecs / 1000;\n\t\tmsecs %= 1000;\n\n\t\ttm.tv_sec  = seconds;\n\t\ttm.tv_nsec = msecs * 1000000;\n\n\t\ttimeout = &tm;\n\t}\n\n\twhile(count_ == 0) {\n\t\tif(!futex_wait(&count_, 0, timeout) && errno != EWOULDBLOCK)\n\t\t\treturn false;\n\t}\n\n\tcount_--;\n\treturn true;\n}\n\n\nvoid Event::post()\n{\n\tcount_++;\n\tfutex_post(&count_, 1);\n}\n"
  },
  {
    "path": "src/common/event.h",
    "content": "#ifndef COMMON_EVENT_H\n#define COMMON_EVENT_H\n\n#include <atomic>\n\n#ifdef bool\n#undef bool\n#endif\n\n\nclass Event {\npublic:\n\tstatic const int kInfinite = -1;\n\n\tEvent();\n\t~Event();\n\n\tbool wait(int msecs = kInfinite);\n\tvoid post();\n\nprivate:\n\tstd::atomic<int> count_;\n};\n\n\n#endif // COMMON_EVENT_H\n"
  },
  {
    "path": "src/common/filesystem.cpp",
    "content": "#include \"filesystem.h\"\n\n#include <vector>\n#include <pwd.h>\n#include <unistd.h>\n#include <linux/limits.h>\n#include <sys/stat.h>\n\n\nnamespace Airwave {\n\n\nstd::string FileSystem::realPath(const std::string& path)\n{\n\tif(!path.empty()) {\n\t\tstd::string result;\n\n\t\tif(path[0] == '~') {\n\t\t\tstruct passwd* pw = getpwuid(getuid());\n\t\t\tresult = pw->pw_dir;\n\t\t\tresult += path.substr(1);\n\t\t}\n\t\telse {\n\t\t\tresult = path;\n\t\t}\n\n\t\tchar buffer[PATH_MAX];\n\n\t\tif(realpath(result.c_str(), buffer))\n\t\t\treturn std::string(buffer);\n\t}\n\n\treturn std::string();\n}\n\n\nbool FileSystem::isFileExists(const std::string& path)\n{\n\treturn access(path.c_str(), F_OK) != -1;\n}\n\n\nbool FileSystem::isDirExists(const std::string& path)\n{\n\tstruct stat info;\n\treturn stat(path.c_str(), &info) == 0 && S_ISDIR(info.st_mode);\n}\n\n\nbool FileSystem::makePath(const std::string& path)\n{\n\tstd::size_t begin = 0;\n\tstd::size_t pos;\n\tstd::string dir;\n\n\twhile(begin < path.length()) {\n\t\tpos = path.find('/', begin);\n\n\t\tif(pos == std::string::npos) {\n\t\t\tdir = path;\n\t\t\tpos = path.length();\n\t\t}\n\t\telse {\n\t\t\tdir = path.substr(0, pos + 1);\n\t\t}\n\n\t\tif(!isDirExists(dir))\n\t\t\tbreak;\n\n\t\tbegin = pos + 1;\n\t}\n\n\twhile(begin < path.length()) {\n\t\tpos = path.find('/', begin);\n\n\t\tif(pos == std::string::npos) {\n\t\t\tdir = path;\n\t\t\tpos = path.length();\n\t\t}\n\t\telse {\n\t\t\tdir = path.substr(0, pos + 1);\n\t\t}\n\n\t\tif(!makeDir(dir))\n\t\t\treturn false;\n\n\t\tbegin = pos + 1;\n\t}\n\n\treturn true;\n}\n\n\nbool FileSystem::makeDir(const std::string& path)\n{\n\tmode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;\n\n\tif(mkdir(path.c_str(), mode) != 0)\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstd::string FileSystem::fullNameFromPath(const std::string& fileName)\n{\n\tstd::string path = getenv(\"PATH\");\n\tsize_t begin = 0;\n\tsize_t pos = begin;\n\n\twhile(pos < path.length()) {\n\t\tpos = path.find(':', begin);\n\n\t\tstd::string fullName = path.substr(begin, pos - begin);\n\t\tfullName += '/' + fileName;\n\t\tif(isFileExists(fullName))\n\t\t\treturn fullName;\n\n\t\tbegin = pos + 1;\n\t}\n\n\treturn fileName;\n}\n\n\nstd::string FileSystem::baseName(const std::string& path)\n{\n\tsize_t pos = path.rfind('/');\n\tif(pos != std::string::npos)\n\t\treturn path.substr(pos + 1);\n\n\treturn path;\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/common/filesystem.h",
    "content": "#ifndef COMMON_FILESYSTEM_H\n#define COMMON_FILESYSTEM_H\n\n#include <string>\n\n\nnamespace Airwave {\n\n\nclass FileSystem {\npublic:\n\tstatic std::string realPath(const std::string& path);\n\tstatic bool isFileExists(const std::string& path);\n\tstatic bool isDirExists(const std::string &path);\n\tstatic bool makePath(const std::string& path);\n\tstatic bool makeDir(const std::string& path);\n\tstatic std::string fullNameFromPath(const std::string& fileName);\n\tstatic std::string baseName(const std::string& path);\n};\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_FILESYSTEM_H\n"
  },
  {
    "path": "src/common/json.cpp",
    "content": "/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).\n/// It is intended to be used with #include \"json.h\"\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n/*\nThe JsonCpp library's source code, including accompanying documentation, \ntests and demonstration applications, are licensed under the following\nconditions...\n\nThe author (Baptiste Lepilleur) explicitly disclaims copyright in all \njurisdictions which recognize such a disclaimer. In such jurisdictions, \nthis software is released into the Public Domain.\n\nIn jurisdictions which do not recognize Public Domain property (e.g. Germany as of\n2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is\nreleased under the terms of the MIT License (see below).\n\nIn jurisdictions which recognize Public Domain property, the user of this \nsoftware may choose to accept it either as 1) Public Domain, 2) under the \nconditions of the MIT License (see below), or 3) under the terms of dual \nPublic Domain/MIT License conditions described here, as they choose.\n\nThe MIT License is about as close to Public Domain as a license can get, and is\ndescribed in clear, concise terms at:\n\n   http://en.wikipedia.org/wiki/MIT_License\n   \nThe full text of the MIT License follows:\n\n========================================================================\nCopyright (c) 2007-2010 Baptiste Lepilleur\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n========================================================================\n(END LICENSE TEXT)\n\nThe MIT license is compatible with both the GPL and commercial\nsoftware, affording one all of the rights of Public Domain with the\nminor nuisance of being required to keep the above copyright notice\nand license text in the source code. Note also that by accepting the\nPublic Domain \"license\" you can re-license your copy using whatever\nlicense you like.\n\n*/\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n#include \"json.h\"\n\n#ifndef JSON_IS_AMALGAMATION\n#error \"Compile with -I PATH_TO_JSON_DIRECTORY\"\n#endif\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_tool.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n\n/* This header provides common string manipulation support, such as UTF-8,\n * portable conversion from/to string...\n *\n * It is an internal header that must not be exposed.\n */\n\nnamespace Json {\n\n/// Converts a unicode code-point to UTF-8.\nstatic inline std::string codePointToUTF8(unsigned int cp) {\n  std::string result;\n\n  // based on description from http://en.wikipedia.org/wiki/UTF-8\n\n  if (cp <= 0x7f) {\n    result.resize(1);\n    result[0] = static_cast<char>(cp);\n  } else if (cp <= 0x7FF) {\n    result.resize(2);\n    result[1] = static_cast<char>(0x80 | (0x3f & cp));\n    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));\n  } else if (cp <= 0xFFFF) {\n    result.resize(3);\n    result[2] = static_cast<char>(0x80 | (0x3f & cp));\n    result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));\n    result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));\n  } else if (cp <= 0x10FFFF) {\n    result.resize(4);\n    result[3] = static_cast<char>(0x80 | (0x3f & cp));\n    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));\n    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));\n    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));\n  }\n\n  return result;\n}\n\n/// Returns true if ch is a control character (in range [0,32[).\nstatic inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }\n\nenum {\n  /// Constant that specify the size of the buffer that must be passed to\n  /// uintToString.\n  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1\n};\n\n// Defines a char buffer for use with uintToString().\ntypedef char UIntToStringBuffer[uintToStringBufferSize];\n\n/** Converts an unsigned integer to string.\n * @param value Unsigned interger to convert to string\n * @param current Input/Output string buffer.\n *        Must have at least uintToStringBufferSize chars free.\n */\nstatic inline void uintToString(LargestUInt value, char*& current) {\n  *--current = 0;\n  do {\n    *--current = char(value % 10) + '0';\n    value /= 10;\n  } while (value != 0);\n}\n\n/** Change ',' to '.' everywhere in buffer.\n *\n * We had a sophisticated way, but it did not work in WinCE.\n * @see https://github.com/open-target-parsers/jsoncpp/pull/9\n */\nstatic inline void fixNumericLocale(char* begin, char* end) {\n  while (begin < end) {\n    if (*begin == ',') {\n      *begin = '.';\n    }\n    ++begin;\n  }\n}\n\n} // namespace Json {\n\n#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_tool.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_reader.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/assertions.h>\n#include <json/reader.h>\n#include <json/value.h>\n#include \"json_tool.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <utility>\n#include <cstdio>\n#include <cassert>\n#include <cstring>\n#include <istream>\n#include <sstream>\n#include <memory>\n#include <set>\n\n#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below\n#define snprintf _snprintf\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0\n// Disable warning about strdup being deprecated.\n#pragma warning(disable : 4996)\n#endif\n\nstatic int const stackLimit_g = 1000;\nstatic int       stackDepth_g = 0;  // see readValue()\n\nnamespace Json {\n\n#if __cplusplus >= 201103L\ntypedef std::unique_ptr<CharReader> CharReaderPtr;\n#else\ntypedef std::auto_ptr<CharReader>   CharReaderPtr;\n#endif\n\n// Implementation of class Features\n// ////////////////////////////////\n\nFeatures::Features()\n    : allowComments_(true), strictRoot_(false),\n      allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}\n\nFeatures Features::all() { return Features(); }\n\nFeatures Features::strictMode() {\n  Features features;\n  features.allowComments_ = false;\n  features.strictRoot_ = true;\n  features.allowDroppedNullPlaceholders_ = false;\n  features.allowNumericKeys_ = false;\n  return features;\n}\n\n// Implementation of class Reader\n// ////////////////////////////////\n\nstatic bool containsNewLine(Reader::Location begin, Reader::Location end) {\n  for (; begin < end; ++begin)\n    if (*begin == '\\n' || *begin == '\\r')\n      return true;\n  return false;\n}\n\n// Class Reader\n// //////////////////////////////////////////////////////////////////\n\nReader::Reader()\n    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),\n      lastValue_(), commentsBefore_(), features_(Features::all()),\n      collectComments_() {}\n\nReader::Reader(const Features& features)\n    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),\n      lastValue_(), commentsBefore_(), features_(features), collectComments_() {\n}\n\nbool\nReader::parse(const std::string& document, Value& root, bool collectComments) {\n  document_ = document;\n  const char* begin = document_.c_str();\n  const char* end = begin + document_.length();\n  return parse(begin, end, root, collectComments);\n}\n\nbool Reader::parse(std::istream& sin, Value& root, bool collectComments) {\n  // std::istream_iterator<char> begin(sin);\n  // std::istream_iterator<char> end;\n  // Those would allow streamed input from a file, if parse() were a\n  // template function.\n\n  // Since std::string is reference-counted, this at least does not\n  // create an extra copy.\n  std::string doc;\n  std::getline(sin, doc, (char)EOF);\n  return parse(doc, root, collectComments);\n}\n\nbool Reader::parse(const char* beginDoc,\n                   const char* endDoc,\n                   Value& root,\n                   bool collectComments) {\n  if (!features_.allowComments_) {\n    collectComments = false;\n  }\n\n  begin_ = beginDoc;\n  end_ = endDoc;\n  collectComments_ = collectComments;\n  current_ = begin_;\n  lastValueEnd_ = 0;\n  lastValue_ = 0;\n  commentsBefore_ = \"\";\n  errors_.clear();\n  while (!nodes_.empty())\n    nodes_.pop();\n  nodes_.push(&root);\n\n  stackDepth_g = 0;  // Yes, this is bad coding, but options are limited.\n  bool successful = readValue();\n  Token token;\n  skipCommentTokens(token);\n  if (collectComments_ && !commentsBefore_.empty())\n    root.setComment(commentsBefore_, commentAfter);\n  if (features_.strictRoot_) {\n    if (!root.isArray() && !root.isObject()) {\n      // Set error location to start of doc, ideally should be first token found\n      // in doc\n      token.type_ = tokenError;\n      token.start_ = beginDoc;\n      token.end_ = endDoc;\n      addError(\n          \"A valid JSON document must be either an array or an object value.\",\n          token);\n      return false;\n    }\n  }\n  return successful;\n}\n\nbool Reader::readValue() {\n  // This is a non-reentrant way to support a stackLimit. Terrible!\n  // But this deprecated class has a security problem: Bad input can\n  // cause a seg-fault. This seems like a fair, binary-compatible way\n  // to prevent the problem.\n  if (stackDepth_g >= stackLimit_g) throwRuntimeError(\"Exceeded stackLimit in readValue().\");\n  ++stackDepth_g;\n\n  Token token;\n  skipCommentTokens(token);\n  bool successful = true;\n\n  if (collectComments_ && !commentsBefore_.empty()) {\n    currentValue().setComment(commentsBefore_, commentBefore);\n    commentsBefore_ = \"\";\n  }\n\n  switch (token.type_) {\n  case tokenObjectBegin:\n    successful = readObject(token);\n    currentValue().setOffsetLimit(current_ - begin_);\n    break;\n  case tokenArrayBegin:\n    successful = readArray(token);\n    currentValue().setOffsetLimit(current_ - begin_);\n    break;\n  case tokenNumber:\n    successful = decodeNumber(token);\n    break;\n  case tokenString:\n    successful = decodeString(token);\n    break;\n  case tokenTrue:\n    {\n    Value v(true);\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenFalse:\n    {\n    Value v(false);\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenNull:\n    {\n    Value v;\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenArraySeparator:\n  case tokenObjectEnd:\n  case tokenArrayEnd:\n    if (features_.allowDroppedNullPlaceholders_) {\n      // \"Un-read\" the current token and mark the current value as a null\n      // token.\n      current_--;\n      Value v;\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(current_ - begin_ - 1);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    } // Else, fall through...\n  default:\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    return addError(\"Syntax error: value, object or array expected.\", token);\n  }\n\n  if (collectComments_) {\n    lastValueEnd_ = current_;\n    lastValue_ = &currentValue();\n  }\n\n  --stackDepth_g;\n  return successful;\n}\n\nvoid Reader::skipCommentTokens(Token& token) {\n  if (features_.allowComments_) {\n    do {\n      readToken(token);\n    } while (token.type_ == tokenComment);\n  } else {\n    readToken(token);\n  }\n}\n\nbool Reader::readToken(Token& token) {\n  skipSpaces();\n  token.start_ = current_;\n  Char c = getNextChar();\n  bool ok = true;\n  switch (c) {\n  case '{':\n    token.type_ = tokenObjectBegin;\n    break;\n  case '}':\n    token.type_ = tokenObjectEnd;\n    break;\n  case '[':\n    token.type_ = tokenArrayBegin;\n    break;\n  case ']':\n    token.type_ = tokenArrayEnd;\n    break;\n  case '\"':\n    token.type_ = tokenString;\n    ok = readString();\n    break;\n  case '/':\n    token.type_ = tokenComment;\n    ok = readComment();\n    break;\n  case '0':\n  case '1':\n  case '2':\n  case '3':\n  case '4':\n  case '5':\n  case '6':\n  case '7':\n  case '8':\n  case '9':\n  case '-':\n    token.type_ = tokenNumber;\n    readNumber();\n    break;\n  case 't':\n    token.type_ = tokenTrue;\n    ok = match(\"rue\", 3);\n    break;\n  case 'f':\n    token.type_ = tokenFalse;\n    ok = match(\"alse\", 4);\n    break;\n  case 'n':\n    token.type_ = tokenNull;\n    ok = match(\"ull\", 3);\n    break;\n  case ',':\n    token.type_ = tokenArraySeparator;\n    break;\n  case ':':\n    token.type_ = tokenMemberSeparator;\n    break;\n  case 0:\n    token.type_ = tokenEndOfStream;\n    break;\n  default:\n    ok = false;\n    break;\n  }\n  if (!ok)\n    token.type_ = tokenError;\n  token.end_ = current_;\n  return true;\n}\n\nvoid Reader::skipSpaces() {\n  while (current_ != end_) {\n    Char c = *current_;\n    if (c == ' ' || c == '\\t' || c == '\\r' || c == '\\n')\n      ++current_;\n    else\n      break;\n  }\n}\n\nbool Reader::match(Location pattern, int patternLength) {\n  if (end_ - current_ < patternLength)\n    return false;\n  int index = patternLength;\n  while (index--)\n    if (current_[index] != pattern[index])\n      return false;\n  current_ += patternLength;\n  return true;\n}\n\nbool Reader::readComment() {\n  Location commentBegin = current_ - 1;\n  Char c = getNextChar();\n  bool successful = false;\n  if (c == '*')\n    successful = readCStyleComment();\n  else if (c == '/')\n    successful = readCppStyleComment();\n  if (!successful)\n    return false;\n\n  if (collectComments_) {\n    CommentPlacement placement = commentBefore;\n    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {\n      if (c != '*' || !containsNewLine(commentBegin, current_))\n        placement = commentAfterOnSameLine;\n    }\n\n    addComment(commentBegin, current_, placement);\n  }\n  return true;\n}\n\nstatic std::string normalizeEOL(Reader::Location begin, Reader::Location end) {\n  std::string normalized;\n  normalized.reserve(end - begin);\n  Reader::Location current = begin;\n  while (current != end) {\n    char c = *current++;\n    if (c == '\\r') {\n      if (current != end && *current == '\\n')\n         // convert dos EOL\n         ++current;\n      // convert Mac EOL\n      normalized += '\\n';\n    } else {\n      normalized += c;\n    }\n  }\n  return normalized;\n}\n\nvoid\nReader::addComment(Location begin, Location end, CommentPlacement placement) {\n  assert(collectComments_);\n  const std::string& normalized = normalizeEOL(begin, end);\n  if (placement == commentAfterOnSameLine) {\n    assert(lastValue_ != 0);\n    lastValue_->setComment(normalized, placement);\n  } else {\n    commentsBefore_ += normalized;\n  }\n}\n\nbool Reader::readCStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '*' && *current_ == '/')\n      break;\n  }\n  return getNextChar() == '/';\n}\n\nbool Reader::readCppStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '\\n')\n      break;\n    if (c == '\\r') {\n      // Consume DOS EOL. It will be normalized in addComment.\n      if (current_ != end_ && *current_ == '\\n')\n        getNextChar();\n      // Break on Moc OS 9 EOL.\n      break;\n    }\n  }\n  return true;\n}\n\nvoid Reader::readNumber() {\n  const char *p = current_;\n  char c = '0'; // stopgap for already consumed character\n  // integral part\n  while (c >= '0' && c <= '9')\n    c = (current_ = p) < end_ ? *p++ : 0;\n  // fractional part\n  if (c == '.') {\n    c = (current_ = p) < end_ ? *p++ : 0;\n    while (c >= '0' && c <= '9')\n      c = (current_ = p) < end_ ? *p++ : 0;\n  }\n  // exponential part\n  if (c == 'e' || c == 'E') {\n    c = (current_ = p) < end_ ? *p++ : 0;\n    if (c == '+' || c == '-')\n      c = (current_ = p) < end_ ? *p++ : 0;\n    while (c >= '0' && c <= '9')\n      c = (current_ = p) < end_ ? *p++ : 0;\n  }\n}\n\nbool Reader::readString() {\n  Char c = 0;\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\"')\n      break;\n  }\n  return c == '\"';\n}\n\nbool Reader::readObject(Token& tokenStart) {\n  Token tokenName;\n  std::string name;\n  Value init(objectValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  while (readToken(tokenName)) {\n    bool initialTokenOk = true;\n    while (tokenName.type_ == tokenComment && initialTokenOk)\n      initialTokenOk = readToken(tokenName);\n    if (!initialTokenOk)\n      break;\n    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object\n      return true;\n    name = \"\";\n    if (tokenName.type_ == tokenString) {\n      if (!decodeString(tokenName, name))\n        return recoverFromError(tokenObjectEnd);\n    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {\n      Value numberName;\n      if (!decodeNumber(tokenName, numberName))\n        return recoverFromError(tokenObjectEnd);\n      name = numberName.asString();\n    } else {\n      break;\n    }\n\n    Token colon;\n    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {\n      return addErrorAndRecover(\n          \"Missing ':' after object member name\", colon, tokenObjectEnd);\n    }\n    Value& value = currentValue()[name];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok) // error already set\n      return recoverFromError(tokenObjectEnd);\n\n    Token comma;\n    if (!readToken(comma) ||\n        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&\n         comma.type_ != tokenComment)) {\n      return addErrorAndRecover(\n          \"Missing ',' or '}' in object declaration\", comma, tokenObjectEnd);\n    }\n    bool finalizeTokenOk = true;\n    while (comma.type_ == tokenComment && finalizeTokenOk)\n      finalizeTokenOk = readToken(comma);\n    if (comma.type_ == tokenObjectEnd)\n      return true;\n  }\n  return addErrorAndRecover(\n      \"Missing '}' or object member name\", tokenName, tokenObjectEnd);\n}\n\nbool Reader::readArray(Token& tokenStart) {\n  Value init(arrayValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  skipSpaces();\n  if (*current_ == ']') // empty array\n  {\n    Token endArray;\n    readToken(endArray);\n    return true;\n  }\n  int index = 0;\n  for (;;) {\n    Value& value = currentValue()[index++];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok) // error already set\n      return recoverFromError(tokenArrayEnd);\n\n    Token token;\n    // Accept Comment after last item in the array.\n    ok = readToken(token);\n    while (token.type_ == tokenComment && ok) {\n      ok = readToken(token);\n    }\n    bool badTokenType =\n        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);\n    if (!ok || badTokenType) {\n      return addErrorAndRecover(\n          \"Missing ',' or ']' in array declaration\", token, tokenArrayEnd);\n    }\n    if (token.type_ == tokenArrayEnd)\n      break;\n  }\n  return true;\n}\n\nbool Reader::decodeNumber(Token& token) {\n  Value decoded;\n  if (!decodeNumber(token, decoded))\n    return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeNumber(Token& token, Value& decoded) {\n  // Attempts to parse the number as an integer. If the number is\n  // larger than the maximum supported value of an integer then\n  // we decode the number as a double.\n  Location current = token.start_;\n  bool isNegative = *current == '-';\n  if (isNegative)\n    ++current;\n  // TODO: Help the compiler do the div and mod at compile time or get rid of them.\n  Value::LargestUInt maxIntegerValue =\n      isNegative ? Value::LargestUInt(-Value::minLargestInt)\n                 : Value::maxLargestUInt;\n  Value::LargestUInt threshold = maxIntegerValue / 10;\n  Value::LargestUInt value = 0;\n  while (current < token.end_) {\n    Char c = *current++;\n    if (c < '0' || c > '9')\n      return decodeDouble(token, decoded);\n    Value::UInt digit(c - '0');\n    if (value >= threshold) {\n      // We've hit or exceeded the max value divided by 10 (rounded down). If\n      // a) we've only just touched the limit, b) this is the last digit, and\n      // c) it's small enough to fit in that rounding delta, we're okay.\n      // Otherwise treat this number as a double to avoid overflow.\n      if (value > threshold || current != token.end_ ||\n          digit > maxIntegerValue % 10) {\n        return decodeDouble(token, decoded);\n      }\n    }\n    value = value * 10 + digit;\n  }\n  if (isNegative)\n    decoded = -Value::LargestInt(value);\n  else if (value <= Value::LargestUInt(Value::maxInt))\n    decoded = Value::LargestInt(value);\n  else\n    decoded = value;\n  return true;\n}\n\nbool Reader::decodeDouble(Token& token) {\n  Value decoded;\n  if (!decodeDouble(token, decoded))\n    return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeDouble(Token& token, Value& decoded) {\n  double value = 0;\n  const int bufferSize = 32;\n  int count;\n  int length = int(token.end_ - token.start_);\n\n  // Sanity check to avoid buffer overflow exploits.\n  if (length < 0) {\n    return addError(\"Unable to parse token length\", token);\n  }\n\n  // Avoid using a string constant for the format control string given to\n  // sscanf, as this can cause hard to debug crashes on OS X. See here for more\n  // info:\n  //\n  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html\n  char format[] = \"%lf\";\n\n  if (length <= bufferSize) {\n    Char buffer[bufferSize + 1];\n    memcpy(buffer, token.start_, length);\n    buffer[length] = 0;\n    count = sscanf(buffer, format, &value);\n  } else {\n    std::string buffer(token.start_, token.end_);\n    count = sscanf(buffer.c_str(), format, &value);\n  }\n\n  if (count != 1)\n    return addError(\"'\" + std::string(token.start_, token.end_) +\n                        \"' is not a number.\",\n                    token);\n  decoded = value;\n  return true;\n}\n\nbool Reader::decodeString(Token& token) {\n  std::string decoded_string;\n  if (!decodeString(token, decoded_string))\n    return false;\n  Value decoded(decoded_string);\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeString(Token& token, std::string& decoded) {\n  decoded.reserve(token.end_ - token.start_ - 2);\n  Location current = token.start_ + 1; // skip '\"'\n  Location end = token.end_ - 1;       // do not include '\"'\n  while (current != end) {\n    Char c = *current++;\n    if (c == '\"')\n      break;\n    else if (c == '\\\\') {\n      if (current == end)\n        return addError(\"Empty escape sequence in string\", token, current);\n      Char escape = *current++;\n      switch (escape) {\n      case '\"':\n        decoded += '\"';\n        break;\n      case '/':\n        decoded += '/';\n        break;\n      case '\\\\':\n        decoded += '\\\\';\n        break;\n      case 'b':\n        decoded += '\\b';\n        break;\n      case 'f':\n        decoded += '\\f';\n        break;\n      case 'n':\n        decoded += '\\n';\n        break;\n      case 'r':\n        decoded += '\\r';\n        break;\n      case 't':\n        decoded += '\\t';\n        break;\n      case 'u': {\n        unsigned int unicode;\n        if (!decodeUnicodeCodePoint(token, current, end, unicode))\n          return false;\n        decoded += codePointToUTF8(unicode);\n      } break;\n      default:\n        return addError(\"Bad escape sequence in string\", token, current);\n      }\n    } else {\n      decoded += c;\n    }\n  }\n  return true;\n}\n\nbool Reader::decodeUnicodeCodePoint(Token& token,\n                                    Location& current,\n                                    Location end,\n                                    unsigned int& unicode) {\n\n  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))\n    return false;\n  if (unicode >= 0xD800 && unicode <= 0xDBFF) {\n    // surrogate pairs\n    if (end - current < 6)\n      return addError(\n          \"additional six characters expected to parse unicode surrogate pair.\",\n          token,\n          current);\n    unsigned int surrogatePair;\n    if (*(current++) == '\\\\' && *(current++) == 'u') {\n      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {\n        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);\n      } else\n        return false;\n    } else\n      return addError(\"expecting another \\\\u token to begin the second half of \"\n                      \"a unicode surrogate pair\",\n                      token,\n                      current);\n  }\n  return true;\n}\n\nbool Reader::decodeUnicodeEscapeSequence(Token& token,\n                                         Location& current,\n                                         Location end,\n                                         unsigned int& unicode) {\n  if (end - current < 4)\n    return addError(\n        \"Bad unicode escape sequence in string: four digits expected.\",\n        token,\n        current);\n  unicode = 0;\n  for (int index = 0; index < 4; ++index) {\n    Char c = *current++;\n    unicode *= 16;\n    if (c >= '0' && c <= '9')\n      unicode += c - '0';\n    else if (c >= 'a' && c <= 'f')\n      unicode += c - 'a' + 10;\n    else if (c >= 'A' && c <= 'F')\n      unicode += c - 'A' + 10;\n    else\n      return addError(\n          \"Bad unicode escape sequence in string: hexadecimal digit expected.\",\n          token,\n          current);\n  }\n  return true;\n}\n\nbool\nReader::addError(const std::string& message, Token& token, Location extra) {\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = extra;\n  errors_.push_back(info);\n  return false;\n}\n\nbool Reader::recoverFromError(TokenType skipUntilToken) {\n  int errorCount = int(errors_.size());\n  Token skip;\n  for (;;) {\n    if (!readToken(skip))\n      errors_.resize(errorCount); // discard errors caused by recovery\n    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)\n      break;\n  }\n  errors_.resize(errorCount);\n  return false;\n}\n\nbool Reader::addErrorAndRecover(const std::string& message,\n                                Token& token,\n                                TokenType skipUntilToken) {\n  addError(message, token);\n  return recoverFromError(skipUntilToken);\n}\n\nValue& Reader::currentValue() { return *(nodes_.top()); }\n\nReader::Char Reader::getNextChar() {\n  if (current_ == end_)\n    return 0;\n  return *current_++;\n}\n\nvoid Reader::getLocationLineAndColumn(Location location,\n                                      int& line,\n                                      int& column) const {\n  Location current = begin_;\n  Location lastLineStart = current;\n  line = 0;\n  while (current < location && current != end_) {\n    Char c = *current++;\n    if (c == '\\r') {\n      if (*current == '\\n')\n        ++current;\n      lastLineStart = current;\n      ++line;\n    } else if (c == '\\n') {\n      lastLineStart = current;\n      ++line;\n    }\n  }\n  // column & line start at 1\n  column = int(location - lastLineStart) + 1;\n  ++line;\n}\n\nstd::string Reader::getLocationLineAndColumn(Location location) const {\n  int line, column;\n  getLocationLineAndColumn(location, line, column);\n  char buffer[18 + 16 + 16 + 1];\n#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)\n#if defined(WINCE)\n  _snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#else\n  sprintf_s(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#endif\n#else\n  snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#endif\n  return buffer;\n}\n\n// Deprecated. Preserved for backward compatibility\nstd::string Reader::getFormatedErrorMessages() const {\n  return getFormattedErrorMessages();\n}\n\nstd::string Reader::getFormattedErrorMessages() const {\n  std::string formattedMessage;\n  for (Errors::const_iterator itError = errors_.begin();\n       itError != errors_.end();\n       ++itError) {\n    const ErrorInfo& error = *itError;\n    formattedMessage +=\n        \"* \" + getLocationLineAndColumn(error.token_.start_) + \"\\n\";\n    formattedMessage += \"  \" + error.message_ + \"\\n\";\n    if (error.extra_)\n      formattedMessage +=\n          \"See \" + getLocationLineAndColumn(error.extra_) + \" for detail.\\n\";\n  }\n  return formattedMessage;\n}\n\nstd::vector<Reader::StructuredError> Reader::getStructuredErrors() const {\n  std::vector<Reader::StructuredError> allErrors;\n  for (Errors::const_iterator itError = errors_.begin();\n       itError != errors_.end();\n       ++itError) {\n    const ErrorInfo& error = *itError;\n    Reader::StructuredError structured;\n    structured.offset_start = error.token_.start_ - begin_;\n    structured.offset_limit = error.token_.end_ - begin_;\n    structured.message = error.message_;\n    allErrors.push_back(structured);\n  }\n  return allErrors;\n}\n\nbool Reader::pushError(const Value& value, const std::string& message) {\n  size_t length = end_ - begin_;\n  if(value.getOffsetStart() > length\n    || value.getOffsetLimit() > length)\n    return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = end_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = 0;\n  errors_.push_back(info);\n  return true;\n}\n\nbool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {\n  size_t length = end_ - begin_;\n  if(value.getOffsetStart() > length\n    || value.getOffsetLimit() > length\n    || extra.getOffsetLimit() > length)\n    return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = begin_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = begin_ + extra.getOffsetStart();\n  errors_.push_back(info);\n  return true;\n}\n\nbool Reader::good() const {\n  return !errors_.size();\n}\n\n// exact copy of Features\nclass OurFeatures {\npublic:\n  static OurFeatures all();\n  OurFeatures();\n  bool allowComments_;\n  bool strictRoot_;\n  bool allowDroppedNullPlaceholders_;\n  bool allowNumericKeys_;\n  bool allowSingleQuotes_;\n  bool failIfExtra_;\n  bool rejectDupKeys_;\n  int stackLimit_;\n};  // OurFeatures\n\n// exact copy of Implementation of class Features\n// ////////////////////////////////\n\nOurFeatures::OurFeatures()\n    : allowComments_(true), strictRoot_(false)\n    , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)\n    , allowSingleQuotes_(false)\n    , failIfExtra_(false)\n{\n}\n\nOurFeatures OurFeatures::all() { return OurFeatures(); }\n\n// Implementation of class Reader\n// ////////////////////////////////\n\n// exact copy of Reader, renamed to OurReader\nclass OurReader {\npublic:\n  typedef char Char;\n  typedef const Char* Location;\n  struct StructuredError {\n    size_t offset_start;\n    size_t offset_limit;\n    std::string message;\n  };\n\n  OurReader(OurFeatures const& features);\n  bool parse(const char* beginDoc,\n             const char* endDoc,\n             Value& root,\n             bool collectComments = true);\n  std::string getFormattedErrorMessages() const;\n  std::vector<StructuredError> getStructuredErrors() const;\n  bool pushError(const Value& value, const std::string& message);\n  bool pushError(const Value& value, const std::string& message, const Value& extra);\n  bool good() const;\n\nprivate:\n  OurReader(OurReader const&);  // no impl\n  void operator=(OurReader const&);  // no impl\n\n  enum TokenType {\n    tokenEndOfStream = 0,\n    tokenObjectBegin,\n    tokenObjectEnd,\n    tokenArrayBegin,\n    tokenArrayEnd,\n    tokenString,\n    tokenNumber,\n    tokenTrue,\n    tokenFalse,\n    tokenNull,\n    tokenArraySeparator,\n    tokenMemberSeparator,\n    tokenComment,\n    tokenError\n  };\n\n  class Token {\n  public:\n    TokenType type_;\n    Location start_;\n    Location end_;\n  };\n\n  class ErrorInfo {\n  public:\n    Token token_;\n    std::string message_;\n    Location extra_;\n  };\n\n  typedef std::deque<ErrorInfo> Errors;\n\n  bool readToken(Token& token);\n  void skipSpaces();\n  bool match(Location pattern, int patternLength);\n  bool readComment();\n  bool readCStyleComment();\n  bool readCppStyleComment();\n  bool readString();\n  bool readStringSingleQuote();\n  void readNumber();\n  bool readValue();\n  bool readObject(Token& token);\n  bool readArray(Token& token);\n  bool decodeNumber(Token& token);\n  bool decodeNumber(Token& token, Value& decoded);\n  bool decodeString(Token& token);\n  bool decodeString(Token& token, std::string& decoded);\n  bool decodeDouble(Token& token);\n  bool decodeDouble(Token& token, Value& decoded);\n  bool decodeUnicodeCodePoint(Token& token,\n                              Location& current,\n                              Location end,\n                              unsigned int& unicode);\n  bool decodeUnicodeEscapeSequence(Token& token,\n                                   Location& current,\n                                   Location end,\n                                   unsigned int& unicode);\n  bool addError(const std::string& message, Token& token, Location extra = 0);\n  bool recoverFromError(TokenType skipUntilToken);\n  bool addErrorAndRecover(const std::string& message,\n                          Token& token,\n                          TokenType skipUntilToken);\n  void skipUntilSpace();\n  Value& currentValue();\n  Char getNextChar();\n  void\n  getLocationLineAndColumn(Location location, int& line, int& column) const;\n  std::string getLocationLineAndColumn(Location location) const;\n  void addComment(Location begin, Location end, CommentPlacement placement);\n  void skipCommentTokens(Token& token);\n\n  typedef std::stack<Value*> Nodes;\n  Nodes nodes_;\n  Errors errors_;\n  std::string document_;\n  Location begin_;\n  Location end_;\n  Location current_;\n  Location lastValueEnd_;\n  Value* lastValue_;\n  std::string commentsBefore_;\n  int stackDepth_;\n\n  OurFeatures const features_;\n  bool collectComments_;\n};  // OurReader\n\n// complete copy of Read impl, for OurReader\n\nOurReader::OurReader(OurFeatures const& features)\n    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),\n      lastValue_(), commentsBefore_(), features_(features), collectComments_() {\n}\n\nbool OurReader::parse(const char* beginDoc,\n                   const char* endDoc,\n                   Value& root,\n                   bool collectComments) {\n  if (!features_.allowComments_) {\n    collectComments = false;\n  }\n\n  begin_ = beginDoc;\n  end_ = endDoc;\n  collectComments_ = collectComments;\n  current_ = begin_;\n  lastValueEnd_ = 0;\n  lastValue_ = 0;\n  commentsBefore_ = \"\";\n  errors_.clear();\n  while (!nodes_.empty())\n    nodes_.pop();\n  nodes_.push(&root);\n\n  stackDepth_ = 0;\n  bool successful = readValue();\n  Token token;\n  skipCommentTokens(token);\n  if (features_.failIfExtra_) {\n    if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {\n      addError(\"Extra non-whitespace after JSON value.\", token);\n      return false;\n    }\n  }\n  if (collectComments_ && !commentsBefore_.empty())\n    root.setComment(commentsBefore_, commentAfter);\n  if (features_.strictRoot_) {\n    if (!root.isArray() && !root.isObject()) {\n      // Set error location to start of doc, ideally should be first token found\n      // in doc\n      token.type_ = tokenError;\n      token.start_ = beginDoc;\n      token.end_ = endDoc;\n      addError(\n          \"A valid JSON document must be either an array or an object value.\",\n          token);\n      return false;\n    }\n  }\n  return successful;\n}\n\nbool OurReader::readValue() {\n  if (stackDepth_ >= features_.stackLimit_) throwRuntimeError(\"Exceeded stackLimit in readValue().\");\n  ++stackDepth_;\n  Token token;\n  skipCommentTokens(token);\n  bool successful = true;\n\n  if (collectComments_ && !commentsBefore_.empty()) {\n    currentValue().setComment(commentsBefore_, commentBefore);\n    commentsBefore_ = \"\";\n  }\n\n  switch (token.type_) {\n  case tokenObjectBegin:\n    successful = readObject(token);\n    currentValue().setOffsetLimit(current_ - begin_);\n    break;\n  case tokenArrayBegin:\n    successful = readArray(token);\n    currentValue().setOffsetLimit(current_ - begin_);\n    break;\n  case tokenNumber:\n    successful = decodeNumber(token);\n    break;\n  case tokenString:\n    successful = decodeString(token);\n    break;\n  case tokenTrue:\n    {\n    Value v(true);\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenFalse:\n    {\n    Value v(false);\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenNull:\n    {\n    Value v;\n    currentValue().swapPayload(v);\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    }\n    break;\n  case tokenArraySeparator:\n  case tokenObjectEnd:\n  case tokenArrayEnd:\n    if (features_.allowDroppedNullPlaceholders_) {\n      // \"Un-read\" the current token and mark the current value as a null\n      // token.\n      current_--;\n      Value v;\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(current_ - begin_ - 1);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    } // else, fall through ...\n  default:\n    currentValue().setOffsetStart(token.start_ - begin_);\n    currentValue().setOffsetLimit(token.end_ - begin_);\n    return addError(\"Syntax error: value, object or array expected.\", token);\n  }\n\n  if (collectComments_) {\n    lastValueEnd_ = current_;\n    lastValue_ = &currentValue();\n  }\n\n  --stackDepth_;\n  return successful;\n}\n\nvoid OurReader::skipCommentTokens(Token& token) {\n  if (features_.allowComments_) {\n    do {\n      readToken(token);\n    } while (token.type_ == tokenComment);\n  } else {\n    readToken(token);\n  }\n}\n\nbool OurReader::readToken(Token& token) {\n  skipSpaces();\n  token.start_ = current_;\n  Char c = getNextChar();\n  bool ok = true;\n  switch (c) {\n  case '{':\n    token.type_ = tokenObjectBegin;\n    break;\n  case '}':\n    token.type_ = tokenObjectEnd;\n    break;\n  case '[':\n    token.type_ = tokenArrayBegin;\n    break;\n  case ']':\n    token.type_ = tokenArrayEnd;\n    break;\n  case '\"':\n    token.type_ = tokenString;\n    ok = readString();\n    break;\n  case '\\'':\n    if (features_.allowSingleQuotes_) {\n    token.type_ = tokenString;\n    ok = readStringSingleQuote();\n    break;\n    } // else continue\n  case '/':\n    token.type_ = tokenComment;\n    ok = readComment();\n    break;\n  case '0':\n  case '1':\n  case '2':\n  case '3':\n  case '4':\n  case '5':\n  case '6':\n  case '7':\n  case '8':\n  case '9':\n  case '-':\n    token.type_ = tokenNumber;\n    readNumber();\n    break;\n  case 't':\n    token.type_ = tokenTrue;\n    ok = match(\"rue\", 3);\n    break;\n  case 'f':\n    token.type_ = tokenFalse;\n    ok = match(\"alse\", 4);\n    break;\n  case 'n':\n    token.type_ = tokenNull;\n    ok = match(\"ull\", 3);\n    break;\n  case ',':\n    token.type_ = tokenArraySeparator;\n    break;\n  case ':':\n    token.type_ = tokenMemberSeparator;\n    break;\n  case 0:\n    token.type_ = tokenEndOfStream;\n    break;\n  default:\n    ok = false;\n    break;\n  }\n  if (!ok)\n    token.type_ = tokenError;\n  token.end_ = current_;\n  return true;\n}\n\nvoid OurReader::skipSpaces() {\n  while (current_ != end_) {\n    Char c = *current_;\n    if (c == ' ' || c == '\\t' || c == '\\r' || c == '\\n')\n      ++current_;\n    else\n      break;\n  }\n}\n\nbool OurReader::match(Location pattern, int patternLength) {\n  if (end_ - current_ < patternLength)\n    return false;\n  int index = patternLength;\n  while (index--)\n    if (current_[index] != pattern[index])\n      return false;\n  current_ += patternLength;\n  return true;\n}\n\nbool OurReader::readComment() {\n  Location commentBegin = current_ - 1;\n  Char c = getNextChar();\n  bool successful = false;\n  if (c == '*')\n    successful = readCStyleComment();\n  else if (c == '/')\n    successful = readCppStyleComment();\n  if (!successful)\n    return false;\n\n  if (collectComments_) {\n    CommentPlacement placement = commentBefore;\n    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {\n      if (c != '*' || !containsNewLine(commentBegin, current_))\n        placement = commentAfterOnSameLine;\n    }\n\n    addComment(commentBegin, current_, placement);\n  }\n  return true;\n}\n\nvoid\nOurReader::addComment(Location begin, Location end, CommentPlacement placement) {\n  assert(collectComments_);\n  const std::string& normalized = normalizeEOL(begin, end);\n  if (placement == commentAfterOnSameLine) {\n    assert(lastValue_ != 0);\n    lastValue_->setComment(normalized, placement);\n  } else {\n    commentsBefore_ += normalized;\n  }\n}\n\nbool OurReader::readCStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '*' && *current_ == '/')\n      break;\n  }\n  return getNextChar() == '/';\n}\n\nbool OurReader::readCppStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '\\n')\n      break;\n    if (c == '\\r') {\n      // Consume DOS EOL. It will be normalized in addComment.\n      if (current_ != end_ && *current_ == '\\n')\n        getNextChar();\n      // Break on Moc OS 9 EOL.\n      break;\n    }\n  }\n  return true;\n}\n\nvoid OurReader::readNumber() {\n  const char *p = current_;\n  char c = '0'; // stopgap for already consumed character\n  // integral part\n  while (c >= '0' && c <= '9')\n    c = (current_ = p) < end_ ? *p++ : 0;\n  // fractional part\n  if (c == '.') {\n    c = (current_ = p) < end_ ? *p++ : 0;\n    while (c >= '0' && c <= '9')\n      c = (current_ = p) < end_ ? *p++ : 0;\n  }\n  // exponential part\n  if (c == 'e' || c == 'E') {\n    c = (current_ = p) < end_ ? *p++ : 0;\n    if (c == '+' || c == '-')\n      c = (current_ = p) < end_ ? *p++ : 0;\n    while (c >= '0' && c <= '9')\n      c = (current_ = p) < end_ ? *p++ : 0;\n  }\n}\nbool OurReader::readString() {\n  Char c = 0;\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\"')\n      break;\n  }\n  return c == '\"';\n}\n\n\nbool OurReader::readStringSingleQuote() {\n  Char c = 0;\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\\'')\n      break;\n  }\n  return c == '\\'';\n}\n\nbool OurReader::readObject(Token& tokenStart) {\n  Token tokenName;\n  std::string name;\n  Value init(objectValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  while (readToken(tokenName)) {\n    bool initialTokenOk = true;\n    while (tokenName.type_ == tokenComment && initialTokenOk)\n      initialTokenOk = readToken(tokenName);\n    if (!initialTokenOk)\n      break;\n    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object\n      return true;\n    name = \"\";\n    if (tokenName.type_ == tokenString) {\n      if (!decodeString(tokenName, name))\n        return recoverFromError(tokenObjectEnd);\n    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {\n      Value numberName;\n      if (!decodeNumber(tokenName, numberName))\n        return recoverFromError(tokenObjectEnd);\n      name = numberName.asString();\n    } else {\n      break;\n    }\n\n    Token colon;\n    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {\n      return addErrorAndRecover(\n          \"Missing ':' after object member name\", colon, tokenObjectEnd);\n    }\n    if (name.length() >= (1U<<30)) throwRuntimeError(\"keylength >= 2^30\");\n    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {\n      std::string msg = \"Duplicate key: '\" + name + \"'\";\n      return addErrorAndRecover(\n          msg, tokenName, tokenObjectEnd);\n    }\n    Value& value = currentValue()[name];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok) // error already set\n      return recoverFromError(tokenObjectEnd);\n\n    Token comma;\n    if (!readToken(comma) ||\n        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&\n         comma.type_ != tokenComment)) {\n      return addErrorAndRecover(\n          \"Missing ',' or '}' in object declaration\", comma, tokenObjectEnd);\n    }\n    bool finalizeTokenOk = true;\n    while (comma.type_ == tokenComment && finalizeTokenOk)\n      finalizeTokenOk = readToken(comma);\n    if (comma.type_ == tokenObjectEnd)\n      return true;\n  }\n  return addErrorAndRecover(\n      \"Missing '}' or object member name\", tokenName, tokenObjectEnd);\n}\n\nbool OurReader::readArray(Token& tokenStart) {\n  Value init(arrayValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  skipSpaces();\n  if (*current_ == ']') // empty array\n  {\n    Token endArray;\n    readToken(endArray);\n    return true;\n  }\n  int index = 0;\n  for (;;) {\n    Value& value = currentValue()[index++];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok) // error already set\n      return recoverFromError(tokenArrayEnd);\n\n    Token token;\n    // Accept Comment after last item in the array.\n    ok = readToken(token);\n    while (token.type_ == tokenComment && ok) {\n      ok = readToken(token);\n    }\n    bool badTokenType =\n        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);\n    if (!ok || badTokenType) {\n      return addErrorAndRecover(\n          \"Missing ',' or ']' in array declaration\", token, tokenArrayEnd);\n    }\n    if (token.type_ == tokenArrayEnd)\n      break;\n  }\n  return true;\n}\n\nbool OurReader::decodeNumber(Token& token) {\n  Value decoded;\n  if (!decodeNumber(token, decoded))\n    return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeNumber(Token& token, Value& decoded) {\n  // Attempts to parse the number as an integer. If the number is\n  // larger than the maximum supported value of an integer then\n  // we decode the number as a double.\n  Location current = token.start_;\n  bool isNegative = *current == '-';\n  if (isNegative)\n    ++current;\n  // TODO: Help the compiler do the div and mod at compile time or get rid of them.\n  Value::LargestUInt maxIntegerValue =\n      isNegative ? Value::LargestUInt(-Value::minLargestInt)\n                 : Value::maxLargestUInt;\n  Value::LargestUInt threshold = maxIntegerValue / 10;\n  Value::LargestUInt value = 0;\n  while (current < token.end_) {\n    Char c = *current++;\n    if (c < '0' || c > '9')\n      return decodeDouble(token, decoded);\n    Value::UInt digit(c - '0');\n    if (value >= threshold) {\n      // We've hit or exceeded the max value divided by 10 (rounded down). If\n      // a) we've only just touched the limit, b) this is the last digit, and\n      // c) it's small enough to fit in that rounding delta, we're okay.\n      // Otherwise treat this number as a double to avoid overflow.\n      if (value > threshold || current != token.end_ ||\n          digit > maxIntegerValue % 10) {\n        return decodeDouble(token, decoded);\n      }\n    }\n    value = value * 10 + digit;\n  }\n  if (isNegative)\n    decoded = -Value::LargestInt(value);\n  else if (value <= Value::LargestUInt(Value::maxInt))\n    decoded = Value::LargestInt(value);\n  else\n    decoded = value;\n  return true;\n}\n\nbool OurReader::decodeDouble(Token& token) {\n  Value decoded;\n  if (!decodeDouble(token, decoded))\n    return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeDouble(Token& token, Value& decoded) {\n  double value = 0;\n  const int bufferSize = 32;\n  int count;\n  int length = int(token.end_ - token.start_);\n\n  // Sanity check to avoid buffer overflow exploits.\n  if (length < 0) {\n    return addError(\"Unable to parse token length\", token);\n  }\n\n  // Avoid using a string constant for the format control string given to\n  // sscanf, as this can cause hard to debug crashes on OS X. See here for more\n  // info:\n  //\n  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html\n  char format[] = \"%lf\";\n\n  if (length <= bufferSize) {\n    Char buffer[bufferSize + 1];\n    memcpy(buffer, token.start_, length);\n    buffer[length] = 0;\n    count = sscanf(buffer, format, &value);\n  } else {\n    std::string buffer(token.start_, token.end_);\n    count = sscanf(buffer.c_str(), format, &value);\n  }\n\n  if (count != 1)\n    return addError(\"'\" + std::string(token.start_, token.end_) +\n                        \"' is not a number.\",\n                    token);\n  decoded = value;\n  return true;\n}\n\nbool OurReader::decodeString(Token& token) {\n  std::string decoded_string;\n  if (!decodeString(token, decoded_string))\n    return false;\n  Value decoded(decoded_string);\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeString(Token& token, std::string& decoded) {\n  decoded.reserve(token.end_ - token.start_ - 2);\n  Location current = token.start_ + 1; // skip '\"'\n  Location end = token.end_ - 1;       // do not include '\"'\n  while (current != end) {\n    Char c = *current++;\n    if (c == '\"')\n      break;\n    else if (c == '\\\\') {\n      if (current == end)\n        return addError(\"Empty escape sequence in string\", token, current);\n      Char escape = *current++;\n      switch (escape) {\n      case '\"':\n        decoded += '\"';\n        break;\n      case '/':\n        decoded += '/';\n        break;\n      case '\\\\':\n        decoded += '\\\\';\n        break;\n      case 'b':\n        decoded += '\\b';\n        break;\n      case 'f':\n        decoded += '\\f';\n        break;\n      case 'n':\n        decoded += '\\n';\n        break;\n      case 'r':\n        decoded += '\\r';\n        break;\n      case 't':\n        decoded += '\\t';\n        break;\n      case 'u': {\n        unsigned int unicode;\n        if (!decodeUnicodeCodePoint(token, current, end, unicode))\n          return false;\n        decoded += codePointToUTF8(unicode);\n      } break;\n      default:\n        return addError(\"Bad escape sequence in string\", token, current);\n      }\n    } else {\n      decoded += c;\n    }\n  }\n  return true;\n}\n\nbool OurReader::decodeUnicodeCodePoint(Token& token,\n                                    Location& current,\n                                    Location end,\n                                    unsigned int& unicode) {\n\n  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))\n    return false;\n  if (unicode >= 0xD800 && unicode <= 0xDBFF) {\n    // surrogate pairs\n    if (end - current < 6)\n      return addError(\n          \"additional six characters expected to parse unicode surrogate pair.\",\n          token,\n          current);\n    unsigned int surrogatePair;\n    if (*(current++) == '\\\\' && *(current++) == 'u') {\n      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {\n        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);\n      } else\n        return false;\n    } else\n      return addError(\"expecting another \\\\u token to begin the second half of \"\n                      \"a unicode surrogate pair\",\n                      token,\n                      current);\n  }\n  return true;\n}\n\nbool OurReader::decodeUnicodeEscapeSequence(Token& token,\n                                         Location& current,\n                                         Location end,\n                                         unsigned int& unicode) {\n  if (end - current < 4)\n    return addError(\n        \"Bad unicode escape sequence in string: four digits expected.\",\n        token,\n        current);\n  unicode = 0;\n  for (int index = 0; index < 4; ++index) {\n    Char c = *current++;\n    unicode *= 16;\n    if (c >= '0' && c <= '9')\n      unicode += c - '0';\n    else if (c >= 'a' && c <= 'f')\n      unicode += c - 'a' + 10;\n    else if (c >= 'A' && c <= 'F')\n      unicode += c - 'A' + 10;\n    else\n      return addError(\n          \"Bad unicode escape sequence in string: hexadecimal digit expected.\",\n          token,\n          current);\n  }\n  return true;\n}\n\nbool\nOurReader::addError(const std::string& message, Token& token, Location extra) {\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = extra;\n  errors_.push_back(info);\n  return false;\n}\n\nbool OurReader::recoverFromError(TokenType skipUntilToken) {\n  int errorCount = int(errors_.size());\n  Token skip;\n  for (;;) {\n    if (!readToken(skip))\n      errors_.resize(errorCount); // discard errors caused by recovery\n    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)\n      break;\n  }\n  errors_.resize(errorCount);\n  return false;\n}\n\nbool OurReader::addErrorAndRecover(const std::string& message,\n                                Token& token,\n                                TokenType skipUntilToken) {\n  addError(message, token);\n  return recoverFromError(skipUntilToken);\n}\n\nValue& OurReader::currentValue() { return *(nodes_.top()); }\n\nOurReader::Char OurReader::getNextChar() {\n  if (current_ == end_)\n    return 0;\n  return *current_++;\n}\n\nvoid OurReader::getLocationLineAndColumn(Location location,\n                                      int& line,\n                                      int& column) const {\n  Location current = begin_;\n  Location lastLineStart = current;\n  line = 0;\n  while (current < location && current != end_) {\n    Char c = *current++;\n    if (c == '\\r') {\n      if (*current == '\\n')\n        ++current;\n      lastLineStart = current;\n      ++line;\n    } else if (c == '\\n') {\n      lastLineStart = current;\n      ++line;\n    }\n  }\n  // column & line start at 1\n  column = int(location - lastLineStart) + 1;\n  ++line;\n}\n\nstd::string OurReader::getLocationLineAndColumn(Location location) const {\n  int line, column;\n  getLocationLineAndColumn(location, line, column);\n  char buffer[18 + 16 + 16 + 1];\n#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)\n#if defined(WINCE)\n  _snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#else\n  sprintf_s(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#endif\n#else\n  snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n#endif\n  return buffer;\n}\n\nstd::string OurReader::getFormattedErrorMessages() const {\n  std::string formattedMessage;\n  for (Errors::const_iterator itError = errors_.begin();\n       itError != errors_.end();\n       ++itError) {\n    const ErrorInfo& error = *itError;\n    formattedMessage +=\n        \"* \" + getLocationLineAndColumn(error.token_.start_) + \"\\n\";\n    formattedMessage += \"  \" + error.message_ + \"\\n\";\n    if (error.extra_)\n      formattedMessage +=\n          \"See \" + getLocationLineAndColumn(error.extra_) + \" for detail.\\n\";\n  }\n  return formattedMessage;\n}\n\nstd::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {\n  std::vector<OurReader::StructuredError> allErrors;\n  for (Errors::const_iterator itError = errors_.begin();\n       itError != errors_.end();\n       ++itError) {\n    const ErrorInfo& error = *itError;\n    OurReader::StructuredError structured;\n    structured.offset_start = error.token_.start_ - begin_;\n    structured.offset_limit = error.token_.end_ - begin_;\n    structured.message = error.message_;\n    allErrors.push_back(structured);\n  }\n  return allErrors;\n}\n\nbool OurReader::pushError(const Value& value, const std::string& message) {\n  size_t length = end_ - begin_;\n  if(value.getOffsetStart() > length\n    || value.getOffsetLimit() > length)\n    return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = end_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = 0;\n  errors_.push_back(info);\n  return true;\n}\n\nbool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) {\n  size_t length = end_ - begin_;\n  if(value.getOffsetStart() > length\n    || value.getOffsetLimit() > length\n    || extra.getOffsetLimit() > length)\n    return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = begin_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = begin_ + extra.getOffsetStart();\n  errors_.push_back(info);\n  return true;\n}\n\nbool OurReader::good() const {\n  return !errors_.size();\n}\n\n\nclass OurCharReader : public CharReader {\n  bool const collectComments_;\n  OurReader reader_;\npublic:\n  OurCharReader(\n    bool collectComments,\n    OurFeatures const& features)\n  : collectComments_(collectComments)\n  , reader_(features)\n  {}\n  virtual bool parse(\n      char const* beginDoc, char const* endDoc,\n      Value* root, std::string* errs) {\n    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);\n    if (errs) {\n      *errs = reader_.getFormattedErrorMessages();\n    }\n    return ok;\n  }\n};\n\nCharReaderBuilder::CharReaderBuilder()\n{\n  setDefaults(&settings_);\n}\nCharReaderBuilder::~CharReaderBuilder()\n{}\nCharReader* CharReaderBuilder::newCharReader() const\n{\n  bool collectComments = settings_[\"collectComments\"].asBool();\n  OurFeatures features = OurFeatures::all();\n  features.allowComments_ = settings_[\"allowComments\"].asBool();\n  features.strictRoot_ = settings_[\"strictRoot\"].asBool();\n  features.allowDroppedNullPlaceholders_ = settings_[\"allowDroppedNullPlaceholders\"].asBool();\n  features.allowNumericKeys_ = settings_[\"allowNumericKeys\"].asBool();\n  features.allowSingleQuotes_ = settings_[\"allowSingleQuotes\"].asBool();\n  features.stackLimit_ = settings_[\"stackLimit\"].asInt();\n  features.failIfExtra_ = settings_[\"failIfExtra\"].asBool();\n  features.rejectDupKeys_ = settings_[\"rejectDupKeys\"].asBool();\n  return new OurCharReader(collectComments, features);\n}\nstatic void getValidReaderKeys(std::set<std::string>* valid_keys)\n{\n  valid_keys->clear();\n  valid_keys->insert(\"collectComments\");\n  valid_keys->insert(\"allowComments\");\n  valid_keys->insert(\"strictRoot\");\n  valid_keys->insert(\"allowDroppedNullPlaceholders\");\n  valid_keys->insert(\"allowNumericKeys\");\n  valid_keys->insert(\"allowSingleQuotes\");\n  valid_keys->insert(\"stackLimit\");\n  valid_keys->insert(\"failIfExtra\");\n  valid_keys->insert(\"rejectDupKeys\");\n}\nbool CharReaderBuilder::validate(Json::Value* invalid) const\n{\n  Json::Value my_invalid;\n  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL\n  Json::Value& inv = *invalid;\n  std::set<std::string> valid_keys;\n  getValidReaderKeys(&valid_keys);\n  Value::Members keys = settings_.getMemberNames();\n  size_t n = keys.size();\n  for (size_t i = 0; i < n; ++i) {\n    std::string const& key = keys[i];\n    if (valid_keys.find(key) == valid_keys.end()) {\n      inv[key] = settings_[key];\n    }\n  }\n  return 0u == inv.size();\n}\nValue& CharReaderBuilder::operator[](std::string key)\n{\n  return settings_[key];\n}\n// static\nvoid CharReaderBuilder::strictMode(Json::Value* settings)\n{\n//! [CharReaderBuilderStrictMode]\n  (*settings)[\"allowComments\"] = false;\n  (*settings)[\"strictRoot\"] = true;\n  (*settings)[\"allowDroppedNullPlaceholders\"] = false;\n  (*settings)[\"allowNumericKeys\"] = false;\n  (*settings)[\"allowSingleQuotes\"] = false;\n  (*settings)[\"failIfExtra\"] = true;\n  (*settings)[\"rejectDupKeys\"] = true;\n//! [CharReaderBuilderStrictMode]\n}\n// static\nvoid CharReaderBuilder::setDefaults(Json::Value* settings)\n{\n//! [CharReaderBuilderDefaults]\n  (*settings)[\"collectComments\"] = true;\n  (*settings)[\"allowComments\"] = true;\n  (*settings)[\"strictRoot\"] = false;\n  (*settings)[\"allowDroppedNullPlaceholders\"] = false;\n  (*settings)[\"allowNumericKeys\"] = false;\n  (*settings)[\"allowSingleQuotes\"] = false;\n  (*settings)[\"stackLimit\"] = 1000;\n  (*settings)[\"failIfExtra\"] = false;\n  (*settings)[\"rejectDupKeys\"] = false;\n//! [CharReaderBuilderDefaults]\n}\n\n//////////////////////////////////\n// global functions\n\nbool parseFromStream(\n    CharReader::Factory const& fact, std::istream& sin,\n    Value* root, std::string* errs)\n{\n  std::ostringstream ssin;\n  ssin << sin.rdbuf();\n  std::string doc = ssin.str();\n  char const* begin = doc.data();\n  char const* end = begin + doc.size();\n  // Note that we do not actually need a null-terminator.\n  CharReaderPtr const reader(fact.newCharReader());\n  return reader->parse(begin, end, root, errs);\n}\n\nstd::istream& operator>>(std::istream& sin, Value& root) {\n  CharReaderBuilder b;\n  std::string errs;\n  bool ok = parseFromStream(b, sin, &root, &errs);\n  if (!ok) {\n    fprintf(stderr,\n            \"Error from reader: %s\",\n            errs.c_str());\n\n    throwRuntimeError(\"reader error\");\n  }\n  return sin;\n}\n\n} // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_reader.cpp\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_valueiterator.inl\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n// included by json_value.cpp\n\nnamespace Json {\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueIteratorBase\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueIteratorBase::ValueIteratorBase()\n    : current_(), isNull_(true) {\n}\n\nValueIteratorBase::ValueIteratorBase(\n    const Value::ObjectValues::iterator& current)\n    : current_(current), isNull_(false) {}\n\nValue& ValueIteratorBase::deref() const {\n  return current_->second;\n}\n\nvoid ValueIteratorBase::increment() {\n  ++current_;\n}\n\nvoid ValueIteratorBase::decrement() {\n  --current_;\n}\n\nValueIteratorBase::difference_type\nValueIteratorBase::computeDistance(const SelfType& other) const {\n#ifdef JSON_USE_CPPTL_SMALLMAP\n  return other.current_ - current_;\n#else\n  // Iterator for null value are initialized using the default\n  // constructor, which initialize current_ to the default\n  // std::map::iterator. As begin() and end() are two instance\n  // of the default std::map::iterator, they can not be compared.\n  // To allow this, we handle this comparison specifically.\n  if (isNull_ && other.isNull_) {\n    return 0;\n  }\n\n  // Usage of std::distance is not portable (does not compile with Sun Studio 12\n  // RogueWave STL,\n  // which is the one used by default).\n  // Using a portable hand-made version for non random iterator instead:\n  //   return difference_type( std::distance( current_, other.current_ ) );\n  difference_type myDistance = 0;\n  for (Value::ObjectValues::iterator it = current_; it != other.current_;\n       ++it) {\n    ++myDistance;\n  }\n  return myDistance;\n#endif\n}\n\nbool ValueIteratorBase::isEqual(const SelfType& other) const {\n  if (isNull_) {\n    return other.isNull_;\n  }\n  return current_ == other.current_;\n}\n\nvoid ValueIteratorBase::copy(const SelfType& other) {\n  current_ = other.current_;\n  isNull_ = other.isNull_;\n}\n\nValue ValueIteratorBase::key() const {\n  const Value::CZString czstring = (*current_).first;\n  if (czstring.data()) {\n    if (czstring.isStaticString())\n      return Value(StaticString(czstring.data()));\n    return Value(czstring.data(), czstring.data() + czstring.length());\n  }\n  return Value(czstring.index());\n}\n\nUInt ValueIteratorBase::index() const {\n  const Value::CZString czstring = (*current_).first;\n  if (!czstring.data())\n    return czstring.index();\n  return Value::UInt(-1);\n}\n\nstd::string ValueIteratorBase::name() const {\n  char const* key;\n  char const* end;\n  key = memberName(&end);\n  if (!key) return std::string();\n  return std::string(key, end);\n}\n\nchar const* ValueIteratorBase::memberName() const {\n  const char* name = (*current_).first.data();\n  return name ? name : \"\";\n}\n\nchar const* ValueIteratorBase::memberName(char const** end) const {\n  const char* name = (*current_).first.data();\n  if (!name) {\n    *end = NULL;\n    return NULL;\n  }\n  *end = name + (*current_).first.length();\n  return name;\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueConstIterator\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueConstIterator::ValueConstIterator() {}\n\nValueConstIterator::ValueConstIterator(\n    const Value::ObjectValues::iterator& current)\n    : ValueIteratorBase(current) {}\n\nValueConstIterator& ValueConstIterator::\noperator=(const ValueIteratorBase& other) {\n  copy(other);\n  return *this;\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueIterator\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueIterator::ValueIterator() {}\n\nValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)\n    : ValueIteratorBase(current) {}\n\nValueIterator::ValueIterator(const ValueConstIterator& other)\n    : ValueIteratorBase(other) {}\n\nValueIterator::ValueIterator(const ValueIterator& other)\n    : ValueIteratorBase(other) {}\n\nValueIterator& ValueIterator::operator=(const SelfType& other) {\n  copy(other);\n  return *this;\n}\n\n} // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_valueiterator.inl\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_value.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/assertions.h>\n#include <json/value.h>\n#include <json/writer.h>\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <math.h>\n#include <sstream>\n#include <utility>\n#include <cstring>\n#include <cassert>\n#ifdef JSON_USE_CPPTL\n#include <cpptl/conststring.h>\n#endif\n#include <cstddef> // size_t\n#include <algorithm> // min()\n\n#define JSON_ASSERT_UNREACHABLE assert(false)\n\nnamespace Json {\n\n// This is a walkaround to avoid the static initialization of Value::null.\n// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of\n// 8 (instead of 4) as a bit of future-proofing.\n#if defined(__ARMEL__)\n#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))\n#else\n#define ALIGNAS(byte_alignment)\n#endif\nstatic const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };\nconst unsigned char& kNullRef = kNull[0];\nconst Value& Value::null = reinterpret_cast<const Value&>(kNullRef);\nconst Value& Value::nullRef = null;\n\nconst Int Value::minInt = Int(~(UInt(-1) / 2));\nconst Int Value::maxInt = Int(UInt(-1) / 2);\nconst UInt Value::maxUInt = UInt(-1);\n#if defined(JSON_HAS_INT64)\nconst Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));\nconst Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);\nconst UInt64 Value::maxUInt64 = UInt64(-1);\n// The constant is hard-coded because some compiler have trouble\n// converting Value::maxUInt64 to a double correctly (AIX/xlC).\n// Assumes that UInt64 is a 64 bits integer.\nstatic const double maxUInt64AsDouble = 18446744073709551615.0;\n#endif // defined(JSON_HAS_INT64)\nconst LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));\nconst LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);\nconst LargestUInt Value::maxLargestUInt = LargestUInt(-1);\n\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\ntemplate <typename T, typename U>\nstatic inline bool InRange(double d, T min, U max) {\n  return d >= min && d <= max;\n}\n#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\nstatic inline double integerToDouble(Json::UInt64 value) {\n  return static_cast<double>(Int64(value / 2)) * 2.0 + Int64(value & 1);\n}\n\ntemplate <typename T> static inline double integerToDouble(T value) {\n  return static_cast<double>(value);\n}\n\ntemplate <typename T, typename U>\nstatic inline bool InRange(double d, T min, U max) {\n  return d >= integerToDouble(min) && d <= integerToDouble(max);\n}\n#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n\n/** Duplicates the specified string value.\n * @param value Pointer to the string to duplicate. Must be zero-terminated if\n *              length is \"unknown\".\n * @param length Length of the value. if equals to unknown, then it will be\n *               computed using strlen(value).\n * @return Pointer on the duplicate instance of string.\n */\nstatic inline char* duplicateStringValue(const char* value,\n                                         size_t length) {\n  // Avoid an integer overflow in the call to malloc below by limiting length\n  // to a sane value.\n  if (length >= (size_t)Value::maxInt)\n    length = Value::maxInt - 1;\n\n  char* newString = static_cast<char*>(malloc(length + 1));\n  if (newString == NULL) {\n    throwRuntimeError(\n        \"in Json::Value::duplicateStringValue(): \"\n        \"Failed to allocate string value buffer\");\n  }\n  memcpy(newString, value, length);\n  newString[length] = 0;\n  return newString;\n}\n\n/* Record the length as a prefix.\n */\nstatic inline char* duplicateAndPrefixStringValue(\n    const char* value,\n    unsigned int length)\n{\n  // Avoid an integer overflow in the call to malloc below by limiting length\n  // to a sane value.\n  JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U,\n                      \"in Json::Value::duplicateAndPrefixStringValue(): \"\n                      \"length too big for prefixing\");\n  unsigned actualLength = length + sizeof(unsigned) + 1U;\n  char* newString = static_cast<char*>(malloc(actualLength));\n  if (newString == 0) {\n    throwRuntimeError(\n        \"in Json::Value::duplicateAndPrefixStringValue(): \"\n        \"Failed to allocate string value buffer\");\n  }\n  *reinterpret_cast<unsigned*>(newString) = length;\n  memcpy(newString + sizeof(unsigned), value, length);\n  newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later\n  return newString;\n}\ninline static void decodePrefixedString(\n    bool isPrefixed, char const* prefixed,\n    unsigned* length, char const** value)\n{\n  if (!isPrefixed) {\n    *length = strlen(prefixed);\n    *value = prefixed;\n  } else {\n    *length = *reinterpret_cast<unsigned const*>(prefixed);\n    *value = prefixed + sizeof(unsigned);\n  }\n}\n/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().\n */\nstatic inline void releaseStringValue(char* value) { free(value); }\n\n} // namespace Json\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// ValueInternals...\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n#if !defined(JSON_IS_AMALGAMATION)\n\n#include \"json_valueiterator.inl\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\nclass JSON_API Exception : public std::exception {\npublic:\n  Exception(std::string const& msg);\n  virtual ~Exception() throw();\n  virtual char const* what() const throw();\nprotected:\n  std::string const msg_;\n};\nclass JSON_API RuntimeError : public Exception {\npublic:\n  RuntimeError(std::string const& msg);\n};\nclass JSON_API LogicError : public Exception {\npublic:\n  LogicError(std::string const& msg);\n};\n\nException::Exception(std::string const& msg)\n  : msg_(msg)\n{}\nException::~Exception() throw()\n{}\nchar const* Exception::what() const throw()\n{\n  return msg_.c_str();\n}\nRuntimeError::RuntimeError(std::string const& msg)\n  : Exception(msg)\n{}\nLogicError::LogicError(std::string const& msg)\n  : Exception(msg)\n{}\nvoid throwRuntimeError(std::string const& msg)\n{\n  throw RuntimeError(msg);\n}\nvoid throwLogicError(std::string const& msg)\n{\n  throw LogicError(msg);\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::CommentInfo\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValue::CommentInfo::CommentInfo() : comment_(0) {}\n\nValue::CommentInfo::~CommentInfo() {\n  if (comment_)\n    releaseStringValue(comment_);\n}\n\nvoid Value::CommentInfo::setComment(const char* text, size_t len) {\n  if (comment_) {\n    releaseStringValue(comment_);\n    comment_ = 0;\n  }\n  JSON_ASSERT(text != 0);\n  JSON_ASSERT_MESSAGE(\n      text[0] == '\\0' || text[0] == '/',\n      \"in Json::Value::setComment(): Comments must start with /\");\n  // It seems that /**/ style comments are acceptable as well.\n  comment_ = duplicateStringValue(text, len);\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::CZString\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\n// Notes: policy_ indicates if the string was allocated when\n// a string is stored.\n\nValue::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}\n\nValue::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate)\n    : cstr_(str)\n{\n  // allocate != duplicate\n  storage_.policy_ = allocate;\n  storage_.length_ = length;\n}\n\nValue::CZString::CZString(const CZString& other)\n    : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0\n                ? duplicateStringValue(other.cstr_, other.storage_.length_)\n                : other.cstr_)\n{\n  storage_.policy_ = (other.cstr_\n                 ? (other.storage_.policy_ == noDuplication\n                     ? noDuplication : duplicate)\n                 : other.storage_.policy_);\n  storage_.length_ = other.storage_.length_;\n}\n\nValue::CZString::~CZString() {\n  if (cstr_ && storage_.policy_ == duplicate)\n    releaseStringValue(const_cast<char*>(cstr_));\n}\n\nvoid Value::CZString::swap(CZString& other) {\n  std::swap(cstr_, other.cstr_);\n  std::swap(index_, other.index_);\n}\n\nValue::CZString& Value::CZString::operator=(CZString other) {\n  swap(other);\n  return *this;\n}\n\nbool Value::CZString::operator<(const CZString& other) const {\n  if (!cstr_) return index_ < other.index_;\n  //return strcmp(cstr_, other.cstr_) < 0;\n  // Assume both are strings.\n  unsigned this_len = this->storage_.length_;\n  unsigned other_len = other.storage_.length_;\n  unsigned min_len = std::min(this_len, other_len);\n  int comp = memcmp(this->cstr_, other.cstr_, min_len);\n  if (comp < 0) return true;\n  if (comp > 0) return false;\n  return (this_len < other_len);\n}\n\nbool Value::CZString::operator==(const CZString& other) const {\n  if (!cstr_) return index_ == other.index_;\n  //return strcmp(cstr_, other.cstr_) == 0;\n  // Assume both are strings.\n  unsigned this_len = this->storage_.length_;\n  unsigned other_len = other.storage_.length_;\n  if (this_len != other_len) return false;\n  int comp = memcmp(this->cstr_, other.cstr_, this_len);\n  return comp == 0;\n}\n\nArrayIndex Value::CZString::index() const { return index_; }\n\n//const char* Value::CZString::c_str() const { return cstr_; }\nconst char* Value::CZString::data() const { return cstr_; }\nunsigned Value::CZString::length() const { return storage_.length_; }\nbool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::Value\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\n/*! \\internal Default constructor initialization must be equivalent to:\n * memset( this, 0, sizeof(Value) )\n * This optimization is used in ValueInternalMap fast allocator.\n */\nValue::Value(ValueType type) {\n  initBasic(type);\n  switch (type) {\n  case nullValue:\n    break;\n  case intValue:\n  case uintValue:\n    value_.int_ = 0;\n    break;\n  case realValue:\n    value_.real_ = 0.0;\n    break;\n  case stringValue:\n    value_.string_ = 0;\n    break;\n  case arrayValue:\n  case objectValue:\n    value_.map_ = new ObjectValues();\n    break;\n  case booleanValue:\n    value_.bool_ = false;\n    break;\n  default:\n    JSON_ASSERT_UNREACHABLE;\n  }\n}\n\nValue::Value(Int value) {\n  initBasic(intValue);\n  value_.int_ = value;\n}\n\nValue::Value(UInt value) {\n  initBasic(uintValue);\n  value_.uint_ = value;\n}\n#if defined(JSON_HAS_INT64)\nValue::Value(Int64 value) {\n  initBasic(intValue);\n  value_.int_ = value;\n}\nValue::Value(UInt64 value) {\n  initBasic(uintValue);\n  value_.uint_ = value;\n}\n#endif // defined(JSON_HAS_INT64)\n\nValue::Value(double value) {\n  initBasic(realValue);\n  value_.real_ = value;\n}\n\nValue::Value(const char* value) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));\n}\n\nValue::Value(const char* beginValue, const char* endValue) {\n  initBasic(stringValue, true);\n  value_.string_ =\n      duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));\n}\n\nValue::Value(const std::string& value) {\n  initBasic(stringValue, true);\n  value_.string_ =\n      duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));\n}\n\nValue::Value(const StaticString& value) {\n  initBasic(stringValue);\n  value_.string_ = const_cast<char*>(value.c_str());\n}\n\n#ifdef JSON_USE_CPPTL\nValue::Value(const CppTL::ConstString& value) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));\n}\n#endif\n\nValue::Value(bool value) {\n  initBasic(booleanValue);\n  value_.bool_ = value;\n}\n\nValue::Value(Value const& other)\n    : type_(other.type_), allocated_(false)\n      ,\n      comments_(0), start_(other.start_), limit_(other.limit_)\n{\n  switch (type_) {\n  case nullValue:\n  case intValue:\n  case uintValue:\n  case realValue:\n  case booleanValue:\n    value_ = other.value_;\n    break;\n  case stringValue:\n    if (other.value_.string_ && other.allocated_) {\n      unsigned len;\n      char const* str;\n      decodePrefixedString(other.allocated_, other.value_.string_,\n          &len, &str);\n      value_.string_ = duplicateAndPrefixStringValue(str, len);\n      allocated_ = true;\n    } else {\n      value_.string_ = other.value_.string_;\n      allocated_ = false;\n    }\n    break;\n  case arrayValue:\n  case objectValue:\n    value_.map_ = new ObjectValues(*other.value_.map_);\n    break;\n  default:\n    JSON_ASSERT_UNREACHABLE;\n  }\n  if (other.comments_) {\n    comments_ = new CommentInfo[numberOfCommentPlacement];\n    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {\n      const CommentInfo& otherComment = other.comments_[comment];\n      if (otherComment.comment_)\n        comments_[comment].setComment(\n            otherComment.comment_, strlen(otherComment.comment_));\n    }\n  }\n}\n\nValue::~Value() {\n  switch (type_) {\n  case nullValue:\n  case intValue:\n  case uintValue:\n  case realValue:\n  case booleanValue:\n    break;\n  case stringValue:\n    if (allocated_)\n      releaseStringValue(value_.string_);\n    break;\n  case arrayValue:\n  case objectValue:\n    delete value_.map_;\n    break;\n  default:\n    JSON_ASSERT_UNREACHABLE;\n  }\n\n  if (comments_)\n    delete[] comments_;\n}\n\nValue& Value::operator=(Value other) {\n  swap(other);\n  return *this;\n}\n\nvoid Value::swapPayload(Value& other) {\n  ValueType temp = type_;\n  type_ = other.type_;\n  other.type_ = temp;\n  std::swap(value_, other.value_);\n  int temp2 = allocated_;\n  allocated_ = other.allocated_;\n  other.allocated_ = temp2;\n}\n\nvoid Value::swap(Value& other) {\n  swapPayload(other);\n  std::swap(comments_, other.comments_);\n  std::swap(start_, other.start_);\n  std::swap(limit_, other.limit_);\n}\n\nValueType Value::type() const { return type_; }\n\nint Value::compare(const Value& other) const {\n  if (*this < other)\n    return -1;\n  if (*this > other)\n    return 1;\n  return 0;\n}\n\nbool Value::operator<(const Value& other) const {\n  int typeDelta = type_ - other.type_;\n  if (typeDelta)\n    return typeDelta < 0 ? true : false;\n  switch (type_) {\n  case nullValue:\n    return false;\n  case intValue:\n    return value_.int_ < other.value_.int_;\n  case uintValue:\n    return value_.uint_ < other.value_.uint_;\n  case realValue:\n    return value_.real_ < other.value_.real_;\n  case booleanValue:\n    return value_.bool_ < other.value_.bool_;\n  case stringValue:\n  {\n    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {\n      if (other.value_.string_) return true;\n      else return false;\n    }\n    unsigned this_len;\n    unsigned other_len;\n    char const* this_str;\n    char const* other_str;\n    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);\n    unsigned min_len = std::min(this_len, other_len);\n    int comp = memcmp(this_str, other_str, min_len);\n    if (comp < 0) return true;\n    if (comp > 0) return false;\n    return (this_len < other_len);\n  }\n  case arrayValue:\n  case objectValue: {\n    int delta = int(value_.map_->size() - other.value_.map_->size());\n    if (delta)\n      return delta < 0;\n    return (*value_.map_) < (*other.value_.map_);\n  }\n  default:\n    JSON_ASSERT_UNREACHABLE;\n  }\n  return false; // unreachable\n}\n\nbool Value::operator<=(const Value& other) const { return !(other < *this); }\n\nbool Value::operator>=(const Value& other) const { return !(*this < other); }\n\nbool Value::operator>(const Value& other) const { return other < *this; }\n\nbool Value::operator==(const Value& other) const {\n  // if ( type_ != other.type_ )\n  // GCC 2.95.3 says:\n  // attempt to take address of bit-field structure member `Json::Value::type_'\n  // Beats me, but a temp solves the problem.\n  int temp = other.type_;\n  if (type_ != temp)\n    return false;\n  switch (type_) {\n  case nullValue:\n    return true;\n  case intValue:\n    return value_.int_ == other.value_.int_;\n  case uintValue:\n    return value_.uint_ == other.value_.uint_;\n  case realValue:\n    return value_.real_ == other.value_.real_;\n  case booleanValue:\n    return value_.bool_ == other.value_.bool_;\n  case stringValue:\n  {\n    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {\n      return (value_.string_ == other.value_.string_);\n    }\n    unsigned this_len;\n    unsigned other_len;\n    char const* this_str;\n    char const* other_str;\n    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);\n    if (this_len != other_len) return false;\n    int comp = memcmp(this_str, other_str, this_len);\n    return comp == 0;\n  }\n  case arrayValue:\n  case objectValue:\n    return value_.map_->size() == other.value_.map_->size() &&\n           (*value_.map_) == (*other.value_.map_);\n  default:\n    JSON_ASSERT_UNREACHABLE;\n  }\n  return false; // unreachable\n}\n\nbool Value::operator!=(const Value& other) const { return !(*this == other); }\n\nconst char* Value::asCString() const {\n  JSON_ASSERT_MESSAGE(type_ == stringValue,\n                      \"in Json::Value::asCString(): requires stringValue\");\n  if (value_.string_ == 0) return 0;\n  unsigned this_len;\n  char const* this_str;\n  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n  return this_str;\n}\n\nbool Value::getString(char const** str, char const** end) const {\n  if (type_ != stringValue) return false;\n  if (value_.string_ == 0) return false;\n  unsigned length;\n  decodePrefixedString(this->allocated_, this->value_.string_, &length, str);\n  *end = *str + length;\n  return true;\n}\n\nstd::string Value::asString() const {\n  switch (type_) {\n  case nullValue:\n    return \"\";\n  case stringValue:\n  {\n    if (value_.string_ == 0) return \"\";\n    unsigned this_len;\n    char const* this_str;\n    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n    return std::string(this_str, this_len);\n  }\n  case booleanValue:\n    return value_.bool_ ? \"true\" : \"false\";\n  case intValue:\n    return valueToString(value_.int_);\n  case uintValue:\n    return valueToString(value_.uint_);\n  case realValue:\n    return valueToString(value_.real_);\n  default:\n    JSON_FAIL_MESSAGE(\"Type is not convertible to string\");\n  }\n}\n\n#ifdef JSON_USE_CPPTL\nCppTL::ConstString Value::asConstString() const {\n  unsigned len;\n  char const* str;\n  decodePrefixedString(allocated_, value_.string_,\n      &len, &str);\n  return CppTL::ConstString(str, len);\n}\n#endif\n\nValue::Int Value::asInt() const {\n  switch (type_) {\n  case intValue:\n    JSON_ASSERT_MESSAGE(isInt(), \"LargestInt out of Int range\");\n    return Int(value_.int_);\n  case uintValue:\n    JSON_ASSERT_MESSAGE(isInt(), \"LargestUInt out of Int range\");\n    return Int(value_.uint_);\n  case realValue:\n    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),\n                        \"double out of Int range\");\n    return Int(value_.real_);\n  case nullValue:\n    return 0;\n  case booleanValue:\n    return value_.bool_ ? 1 : 0;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to Int.\");\n}\n\nValue::UInt Value::asUInt() const {\n  switch (type_) {\n  case intValue:\n    JSON_ASSERT_MESSAGE(isUInt(), \"LargestInt out of UInt range\");\n    return UInt(value_.int_);\n  case uintValue:\n    JSON_ASSERT_MESSAGE(isUInt(), \"LargestUInt out of UInt range\");\n    return UInt(value_.uint_);\n  case realValue:\n    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),\n                        \"double out of UInt range\");\n    return UInt(value_.real_);\n  case nullValue:\n    return 0;\n  case booleanValue:\n    return value_.bool_ ? 1 : 0;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to UInt.\");\n}\n\n#if defined(JSON_HAS_INT64)\n\nValue::Int64 Value::asInt64() const {\n  switch (type_) {\n  case intValue:\n    return Int64(value_.int_);\n  case uintValue:\n    JSON_ASSERT_MESSAGE(isInt64(), \"LargestUInt out of Int64 range\");\n    return Int64(value_.uint_);\n  case realValue:\n    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),\n                        \"double out of Int64 range\");\n    return Int64(value_.real_);\n  case nullValue:\n    return 0;\n  case booleanValue:\n    return value_.bool_ ? 1 : 0;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to Int64.\");\n}\n\nValue::UInt64 Value::asUInt64() const {\n  switch (type_) {\n  case intValue:\n    JSON_ASSERT_MESSAGE(isUInt64(), \"LargestInt out of UInt64 range\");\n    return UInt64(value_.int_);\n  case uintValue:\n    return UInt64(value_.uint_);\n  case realValue:\n    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),\n                        \"double out of UInt64 range\");\n    return UInt64(value_.real_);\n  case nullValue:\n    return 0;\n  case booleanValue:\n    return value_.bool_ ? 1 : 0;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to UInt64.\");\n}\n#endif // if defined(JSON_HAS_INT64)\n\nLargestInt Value::asLargestInt() const {\n#if defined(JSON_NO_INT64)\n  return asInt();\n#else\n  return asInt64();\n#endif\n}\n\nLargestUInt Value::asLargestUInt() const {\n#if defined(JSON_NO_INT64)\n  return asUInt();\n#else\n  return asUInt64();\n#endif\n}\n\ndouble Value::asDouble() const {\n  switch (type_) {\n  case intValue:\n    return static_cast<double>(value_.int_);\n  case uintValue:\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    return static_cast<double>(value_.uint_);\n#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    return integerToDouble(value_.uint_);\n#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n  case realValue:\n    return value_.real_;\n  case nullValue:\n    return 0.0;\n  case booleanValue:\n    return value_.bool_ ? 1.0 : 0.0;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to double.\");\n}\n\nfloat Value::asFloat() const {\n  switch (type_) {\n  case intValue:\n    return static_cast<float>(value_.int_);\n  case uintValue:\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    return static_cast<float>(value_.uint_);\n#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    return integerToDouble(value_.uint_);\n#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n  case realValue:\n    return static_cast<float>(value_.real_);\n  case nullValue:\n    return 0.0;\n  case booleanValue:\n    return value_.bool_ ? 1.0f : 0.0f;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to float.\");\n}\n\nbool Value::asBool() const {\n  switch (type_) {\n  case booleanValue:\n    return value_.bool_;\n  case nullValue:\n    return false;\n  case intValue:\n    return value_.int_ ? true : false;\n  case uintValue:\n    return value_.uint_ ? true : false;\n  case realValue:\n    return value_.real_ ? true : false;\n  default:\n    break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to bool.\");\n}\n\nbool Value::isConvertibleTo(ValueType other) const {\n  switch (other) {\n  case nullValue:\n    return (isNumeric() && asDouble() == 0.0) ||\n           (type_ == booleanValue && value_.bool_ == false) ||\n           (type_ == stringValue && asString() == \"\") ||\n           (type_ == arrayValue && value_.map_->size() == 0) ||\n           (type_ == objectValue && value_.map_->size() == 0) ||\n           type_ == nullValue;\n  case intValue:\n    return isInt() ||\n           (type_ == realValue && InRange(value_.real_, minInt, maxInt)) ||\n           type_ == booleanValue || type_ == nullValue;\n  case uintValue:\n    return isUInt() ||\n           (type_ == realValue && InRange(value_.real_, 0, maxUInt)) ||\n           type_ == booleanValue || type_ == nullValue;\n  case realValue:\n    return isNumeric() || type_ == booleanValue || type_ == nullValue;\n  case booleanValue:\n    return isNumeric() || type_ == booleanValue || type_ == nullValue;\n  case stringValue:\n    return isNumeric() || type_ == booleanValue || type_ == stringValue ||\n           type_ == nullValue;\n  case arrayValue:\n    return type_ == arrayValue || type_ == nullValue;\n  case objectValue:\n    return type_ == objectValue || type_ == nullValue;\n  }\n  JSON_ASSERT_UNREACHABLE;\n  return false;\n}\n\n/// Number of values in array or object\nArrayIndex Value::size() const {\n  switch (type_) {\n  case nullValue:\n  case intValue:\n  case uintValue:\n  case realValue:\n  case booleanValue:\n  case stringValue:\n    return 0;\n  case arrayValue: // size of the array is highest index + 1\n    if (!value_.map_->empty()) {\n      ObjectValues::const_iterator itLast = value_.map_->end();\n      --itLast;\n      return (*itLast).first.index() + 1;\n    }\n    return 0;\n  case objectValue:\n    return ArrayIndex(value_.map_->size());\n  }\n  JSON_ASSERT_UNREACHABLE;\n  return 0; // unreachable;\n}\n\nbool Value::empty() const {\n  if (isNull() || isArray() || isObject())\n    return size() == 0u;\n  else\n    return false;\n}\n\nbool Value::operator!() const { return isNull(); }\n\nvoid Value::clear() {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||\n                          type_ == objectValue,\n                      \"in Json::Value::clear(): requires complex value\");\n  start_ = 0;\n  limit_ = 0;\n  switch (type_) {\n  case arrayValue:\n  case objectValue:\n    value_.map_->clear();\n    break;\n  default:\n    break;\n  }\n}\n\nvoid Value::resize(ArrayIndex newSize) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,\n                      \"in Json::Value::resize(): requires arrayValue\");\n  if (type_ == nullValue)\n    *this = Value(arrayValue);\n  ArrayIndex oldSize = size();\n  if (newSize == 0)\n    clear();\n  else if (newSize > oldSize)\n    (*this)[newSize - 1];\n  else {\n    for (ArrayIndex index = newSize; index < oldSize; ++index) {\n      value_.map_->erase(index);\n    }\n    assert(size() == newSize);\n  }\n}\n\nValue& Value::operator[](ArrayIndex index) {\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == arrayValue,\n      \"in Json::Value::operator[](ArrayIndex): requires arrayValue\");\n  if (type_ == nullValue)\n    *this = Value(arrayValue);\n  CZString key(index);\n  ObjectValues::iterator it = value_.map_->lower_bound(key);\n  if (it != value_.map_->end() && (*it).first == key)\n    return (*it).second;\n\n  ObjectValues::value_type defaultValue(key, nullRef);\n  it = value_.map_->insert(it, defaultValue);\n  return (*it).second;\n}\n\nValue& Value::operator[](int index) {\n  JSON_ASSERT_MESSAGE(\n      index >= 0,\n      \"in Json::Value::operator[](int index): index cannot be negative\");\n  return (*this)[ArrayIndex(index)];\n}\n\nconst Value& Value::operator[](ArrayIndex index) const {\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == arrayValue,\n      \"in Json::Value::operator[](ArrayIndex)const: requires arrayValue\");\n  if (type_ == nullValue)\n    return nullRef;\n  CZString key(index);\n  ObjectValues::const_iterator it = value_.map_->find(key);\n  if (it == value_.map_->end())\n    return nullRef;\n  return (*it).second;\n}\n\nconst Value& Value::operator[](int index) const {\n  JSON_ASSERT_MESSAGE(\n      index >= 0,\n      \"in Json::Value::operator[](int index) const: index cannot be negative\");\n  return (*this)[ArrayIndex(index)];\n}\n\nvoid Value::initBasic(ValueType type, bool allocated) {\n  type_ = type;\n  allocated_ = allocated;\n  comments_ = 0;\n  start_ = 0;\n  limit_ = 0;\n}\n\n// Access an object value by name, create a null member if it does not exist.\n// @pre Type of '*this' is object or null.\n// @param key is null-terminated.\nValue& Value::resolveReference(const char* key) {\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == objectValue,\n      \"in Json::Value::resolveReference(): requires objectValue\");\n  if (type_ == nullValue)\n    *this = Value(objectValue);\n  CZString actualKey(\n      key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE!\n  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);\n  if (it != value_.map_->end() && (*it).first == actualKey)\n    return (*it).second;\n\n  ObjectValues::value_type defaultValue(actualKey, nullRef);\n  it = value_.map_->insert(it, defaultValue);\n  Value& value = (*it).second;\n  return value;\n}\n\n// @param key is not null-terminated.\nValue& Value::resolveReference(char const* key, char const* end)\n{\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == objectValue,\n      \"in Json::Value::resolveReference(key, end): requires objectValue\");\n  if (type_ == nullValue)\n    *this = Value(objectValue);\n  CZString actualKey(\n      key, static_cast<unsigned>(end-key), CZString::duplicateOnCopy);\n  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);\n  if (it != value_.map_->end() && (*it).first == actualKey)\n    return (*it).second;\n\n  ObjectValues::value_type defaultValue(actualKey, nullRef);\n  it = value_.map_->insert(it, defaultValue);\n  Value& value = (*it).second;\n  return value;\n}\n\nValue Value::get(ArrayIndex index, const Value& defaultValue) const {\n  const Value* value = &((*this)[index]);\n  return value == &nullRef ? defaultValue : *value;\n}\n\nbool Value::isValidIndex(ArrayIndex index) const { return index < size(); }\n\nValue const* Value::find(char const* key, char const* end) const\n{\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == objectValue,\n      \"in Json::Value::find(key, end, found): requires objectValue or nullValue\");\n  if (type_ == nullValue) return NULL;\n  CZString actualKey(key, static_cast<unsigned>(end-key), CZString::noDuplication);\n  ObjectValues::const_iterator it = value_.map_->find(actualKey);\n  if (it == value_.map_->end()) return NULL;\n  return &(*it).second;\n}\nconst Value& Value::operator[](const char* key) const\n{\n  Value const* found = find(key, key + strlen(key));\n  if (!found) return nullRef;\n  return *found;\n}\nValue const& Value::operator[](std::string const& key) const\n{\n  Value const* found = find(key.data(), key.data() + key.length());\n  if (!found) return nullRef;\n  return *found;\n}\n\nValue& Value::operator[](const char* key) {\n  return resolveReference(key, key + strlen(key));\n}\n\nValue& Value::operator[](const std::string& key) {\n  return resolveReference(key.data(), key.data() + key.length());\n}\n\nValue& Value::operator[](const StaticString& key) {\n  return resolveReference(key.c_str());\n}\n\n#ifdef JSON_USE_CPPTL\nValue& Value::operator[](const CppTL::ConstString& key) {\n  return resolveReference(key.c_str(), key.end_c_str());\n}\nValue const& Value::operator[](CppTL::ConstString const& key) const\n{\n  Value const* found = find(key.c_str(), key.end_c_str());\n  if (!found) return nullRef;\n  return *found;\n}\n#endif\n\nValue& Value::append(const Value& value) { return (*this)[size()] = value; }\n\nValue Value::get(char const* key, char const* end, Value const& defaultValue) const\n{\n  Value const* found = find(key, end);\n  return !found ? defaultValue : *found;\n}\nValue Value::get(char const* key, Value const& defaultValue) const\n{\n  return get(key, key + strlen(key), defaultValue);\n}\nValue Value::get(std::string const& key, Value const& defaultValue) const\n{\n  return get(key.data(), key.data() + key.length(), defaultValue);\n}\n\n\nbool Value::removeMember(const char* key, const char* end, Value* removed)\n{\n  if (type_ != objectValue) {\n    return false;\n  }\n  CZString actualKey(key, static_cast<unsigned>(end-key), CZString::noDuplication);\n  ObjectValues::iterator it = value_.map_->find(actualKey);\n  if (it == value_.map_->end())\n    return false;\n  *removed = it->second;\n  value_.map_->erase(it);\n  return true;\n}\nbool Value::removeMember(const char* key, Value* removed)\n{\n  return removeMember(key, key + strlen(key), removed);\n}\nbool Value::removeMember(std::string const& key, Value* removed)\n{\n  return removeMember(key.data(), key.data() + key.length(), removed);\n}\nValue Value::removeMember(const char* key)\n{\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,\n                      \"in Json::Value::removeMember(): requires objectValue\");\n  if (type_ == nullValue)\n    return nullRef;\n\n  Value removed;  // null\n  removeMember(key, key + strlen(key), &removed);\n  return removed; // still null if removeMember() did nothing\n}\nValue Value::removeMember(const std::string& key)\n{\n  return removeMember(key.c_str());\n}\n\nbool Value::removeIndex(ArrayIndex index, Value* removed) {\n  if (type_ != arrayValue) {\n    return false;\n  }\n  CZString key(index);\n  ObjectValues::iterator it = value_.map_->find(key);\n  if (it == value_.map_->end()) {\n    return false;\n  }\n  *removed = it->second;\n  ArrayIndex oldSize = size();\n  // shift left all items left, into the place of the \"removed\"\n  for (ArrayIndex i = index; i < (oldSize - 1); ++i){\n    CZString key(i);\n    (*value_.map_)[key] = (*this)[i + 1];\n  }\n  // erase the last one (\"leftover\")\n  CZString keyLast(oldSize - 1);\n  ObjectValues::iterator itLast = value_.map_->find(keyLast);\n  value_.map_->erase(itLast);\n  return true;\n}\n\n#ifdef JSON_USE_CPPTL\nValue Value::get(const CppTL::ConstString& key,\n                 const Value& defaultValue) const {\n  return get(key.c_str(), key.end_c_str(), defaultValue);\n}\n#endif\n\nbool Value::isMember(char const* key, char const* end) const\n{\n  Value const* value = find(key, end);\n  return NULL != value;\n}\nbool Value::isMember(char const* key) const\n{\n  return isMember(key, key + strlen(key));\n}\nbool Value::isMember(std::string const& key) const\n{\n  return isMember(key.data(), key.data() + key.length());\n}\n\n#ifdef JSON_USE_CPPTL\nbool Value::isMember(const CppTL::ConstString& key) const {\n  return isMember(key.c_str(), key.end_c_str());\n}\n#endif\n\nValue::Members Value::getMemberNames() const {\n  JSON_ASSERT_MESSAGE(\n      type_ == nullValue || type_ == objectValue,\n      \"in Json::Value::getMemberNames(), value must be objectValue\");\n  if (type_ == nullValue)\n    return Value::Members();\n  Members members;\n  members.reserve(value_.map_->size());\n  ObjectValues::const_iterator it = value_.map_->begin();\n  ObjectValues::const_iterator itEnd = value_.map_->end();\n  for (; it != itEnd; ++it) {\n    members.push_back(std::string((*it).first.data(),\n                                  (*it).first.length()));\n  }\n  return members;\n}\n//\n//# ifdef JSON_USE_CPPTL\n// EnumMemberNames\n// Value::enumMemberNames() const\n//{\n//   if ( type_ == objectValue )\n//   {\n//      return CppTL::Enum::any(  CppTL::Enum::transform(\n//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),\n//         MemberNamesTransform() ) );\n//   }\n//   return EnumMemberNames();\n//}\n//\n//\n// EnumValues\n// Value::enumValues() const\n//{\n//   if ( type_ == objectValue  ||  type_ == arrayValue )\n//      return CppTL::Enum::anyValues( *(value_.map_),\n//                                     CppTL::Type<const Value &>() );\n//   return EnumValues();\n//}\n//\n//# endif\n\nstatic bool IsIntegral(double d) {\n  double integral_part;\n  return modf(d, &integral_part) == 0.0;\n}\n\nbool Value::isNull() const { return type_ == nullValue; }\n\nbool Value::isBool() const { return type_ == booleanValue; }\n\nbool Value::isInt() const {\n  switch (type_) {\n  case intValue:\n    return value_.int_ >= minInt && value_.int_ <= maxInt;\n  case uintValue:\n    return value_.uint_ <= UInt(maxInt);\n  case realValue:\n    return value_.real_ >= minInt && value_.real_ <= maxInt &&\n           IsIntegral(value_.real_);\n  default:\n    break;\n  }\n  return false;\n}\n\nbool Value::isUInt() const {\n  switch (type_) {\n  case intValue:\n    return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);\n  case uintValue:\n    return value_.uint_ <= maxUInt;\n  case realValue:\n    return value_.real_ >= 0 && value_.real_ <= maxUInt &&\n           IsIntegral(value_.real_);\n  default:\n    break;\n  }\n  return false;\n}\n\nbool Value::isInt64() const {\n#if defined(JSON_HAS_INT64)\n  switch (type_) {\n  case intValue:\n    return true;\n  case uintValue:\n    return value_.uint_ <= UInt64(maxInt64);\n  case realValue:\n    // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a\n    // double, so double(maxInt64) will be rounded up to 2^63. Therefore we\n    // require the value to be strictly less than the limit.\n    return value_.real_ >= double(minInt64) &&\n           value_.real_ < double(maxInt64) && IsIntegral(value_.real_);\n  default:\n    break;\n  }\n#endif // JSON_HAS_INT64\n  return false;\n}\n\nbool Value::isUInt64() const {\n#if defined(JSON_HAS_INT64)\n  switch (type_) {\n  case intValue:\n    return value_.int_ >= 0;\n  case uintValue:\n    return true;\n  case realValue:\n    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a\n    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we\n    // require the value to be strictly less than the limit.\n    return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&\n           IsIntegral(value_.real_);\n  default:\n    break;\n  }\n#endif // JSON_HAS_INT64\n  return false;\n}\n\nbool Value::isIntegral() const {\n#if defined(JSON_HAS_INT64)\n  return isInt64() || isUInt64();\n#else\n  return isInt() || isUInt();\n#endif\n}\n\nbool Value::isDouble() const { return type_ == realValue || isIntegral(); }\n\nbool Value::isNumeric() const { return isIntegral() || isDouble(); }\n\nbool Value::isString() const { return type_ == stringValue; }\n\nbool Value::isArray() const { return type_ == arrayValue; }\n\nbool Value::isObject() const { return type_ == objectValue; }\n\nvoid Value::setComment(const char* comment, size_t len, CommentPlacement placement) {\n  if (!comments_)\n    comments_ = new CommentInfo[numberOfCommentPlacement];\n  if ((len > 0) && (comment[len-1] == '\\n')) {\n    // Always discard trailing newline, to aid indentation.\n    len -= 1;\n  }\n  comments_[placement].setComment(comment, len);\n}\n\nvoid Value::setComment(const char* comment, CommentPlacement placement) {\n  setComment(comment, strlen(comment), placement);\n}\n\nvoid Value::setComment(const std::string& comment, CommentPlacement placement) {\n  setComment(comment.c_str(), comment.length(), placement);\n}\n\nbool Value::hasComment(CommentPlacement placement) const {\n  return comments_ != 0 && comments_[placement].comment_ != 0;\n}\n\nstd::string Value::getComment(CommentPlacement placement) const {\n  if (hasComment(placement))\n    return comments_[placement].comment_;\n  return \"\";\n}\n\nvoid Value::setOffsetStart(size_t start) { start_ = start; }\n\nvoid Value::setOffsetLimit(size_t limit) { limit_ = limit; }\n\nsize_t Value::getOffsetStart() const { return start_; }\n\nsize_t Value::getOffsetLimit() const { return limit_; }\n\nstd::string Value::toStyledString() const {\n  StyledWriter writer;\n  return writer.write(*this);\n}\n\nValue::const_iterator Value::begin() const {\n  switch (type_) {\n  case arrayValue:\n  case objectValue:\n    if (value_.map_)\n      return const_iterator(value_.map_->begin());\n    break;\n  default:\n    break;\n  }\n  return const_iterator();\n}\n\nValue::const_iterator Value::end() const {\n  switch (type_) {\n  case arrayValue:\n  case objectValue:\n    if (value_.map_)\n      return const_iterator(value_.map_->end());\n    break;\n  default:\n    break;\n  }\n  return const_iterator();\n}\n\nValue::iterator Value::begin() {\n  switch (type_) {\n  case arrayValue:\n  case objectValue:\n    if (value_.map_)\n      return iterator(value_.map_->begin());\n    break;\n  default:\n    break;\n  }\n  return iterator();\n}\n\nValue::iterator Value::end() {\n  switch (type_) {\n  case arrayValue:\n  case objectValue:\n    if (value_.map_)\n      return iterator(value_.map_->end());\n    break;\n  default:\n    break;\n  }\n  return iterator();\n}\n\n// class PathArgument\n// //////////////////////////////////////////////////////////////////\n\nPathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}\n\nPathArgument::PathArgument(ArrayIndex index)\n    : key_(), index_(index), kind_(kindIndex) {}\n\nPathArgument::PathArgument(const char* key)\n    : key_(key), index_(), kind_(kindKey) {}\n\nPathArgument::PathArgument(const std::string& key)\n    : key_(key.c_str()), index_(), kind_(kindKey) {}\n\n// class Path\n// //////////////////////////////////////////////////////////////////\n\nPath::Path(const std::string& path,\n           const PathArgument& a1,\n           const PathArgument& a2,\n           const PathArgument& a3,\n           const PathArgument& a4,\n           const PathArgument& a5) {\n  InArgs in;\n  in.push_back(&a1);\n  in.push_back(&a2);\n  in.push_back(&a3);\n  in.push_back(&a4);\n  in.push_back(&a5);\n  makePath(path, in);\n}\n\nvoid Path::makePath(const std::string& path, const InArgs& in) {\n  const char* current = path.c_str();\n  const char* end = current + path.length();\n  InArgs::const_iterator itInArg = in.begin();\n  while (current != end) {\n    if (*current == '[') {\n      ++current;\n      if (*current == '%')\n        addPathInArg(path, in, itInArg, PathArgument::kindIndex);\n      else {\n        ArrayIndex index = 0;\n        for (; current != end && *current >= '0' && *current <= '9'; ++current)\n          index = index * 10 + ArrayIndex(*current - '0');\n        args_.push_back(index);\n      }\n      if (current == end || *current++ != ']')\n        invalidPath(path, int(current - path.c_str()));\n    } else if (*current == '%') {\n      addPathInArg(path, in, itInArg, PathArgument::kindKey);\n      ++current;\n    } else if (*current == '.') {\n      ++current;\n    } else {\n      const char* beginName = current;\n      while (current != end && !strchr(\"[.\", *current))\n        ++current;\n      args_.push_back(std::string(beginName, current));\n    }\n  }\n}\n\nvoid Path::addPathInArg(const std::string& /*path*/,\n                        const InArgs& in,\n                        InArgs::const_iterator& itInArg,\n                        PathArgument::Kind kind) {\n  if (itInArg == in.end()) {\n    // Error: missing argument %d\n  } else if ((*itInArg)->kind_ != kind) {\n    // Error: bad argument type\n  } else {\n    args_.push_back(**itInArg);\n  }\n}\n\nvoid Path::invalidPath(const std::string& /*path*/, int /*location*/) {\n  // Error: invalid path.\n}\n\nconst Value& Path::resolve(const Value& root) const {\n  const Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray() || !node->isValidIndex(arg.index_)) {\n        // Error: unable to resolve path (array value expected at position...\n      }\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject()) {\n        // Error: unable to resolve path (object value expected at position...)\n      }\n      node = &((*node)[arg.key_]);\n      if (node == &Value::nullRef) {\n        // Error: unable to resolve path (object has no member named '' at\n        // position...)\n      }\n    }\n  }\n  return *node;\n}\n\nValue Path::resolve(const Value& root, const Value& defaultValue) const {\n  const Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray() || !node->isValidIndex(arg.index_))\n        return defaultValue;\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject())\n        return defaultValue;\n      node = &((*node)[arg.key_]);\n      if (node == &Value::nullRef)\n        return defaultValue;\n    }\n  }\n  return *node;\n}\n\nValue& Path::make(Value& root) const {\n  Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray()) {\n        // Error: node is not an array at position ...\n      }\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject()) {\n        // Error: node is not an object at position...\n      }\n      node = &((*node)[arg.key_]);\n    }\n  }\n  return *node;\n}\n\n} // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_value.cpp\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_writer.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/writer.h>\n#include \"json_tool.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <iomanip>\n#include <memory>\n#include <sstream>\n#include <utility>\n#include <set>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n\n#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0\n#include <float.h>\n#define isfinite _finite\n#elif defined(__sun) && defined(__SVR4) //Solaris\n#include <ieeefp.h>\n#define isfinite finite\n#else\n#include <cmath>\n#define isfinite std::isfinite\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below\n#define snprintf _snprintf\n#else\n#define snprintf std::snprintf\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0\n// Disable warning about strdup being deprecated.\n#pragma warning(disable : 4996)\n#endif\n\nnamespace Json {\n\n#if __cplusplus >= 201103L\ntypedef std::unique_ptr<StreamWriter> StreamWriterPtr;\n#else\ntypedef std::auto_ptr<StreamWriter>   StreamWriterPtr;\n#endif\n\nstatic bool containsControlCharacter(const char* str) {\n  while (*str) {\n    if (isControlCharacter(*(str++)))\n      return true;\n  }\n  return false;\n}\n\nstatic bool containsControlCharacter0(const char* str, unsigned len) {\n  char const* end = str + len;\n  while (end != str) {\n    if (isControlCharacter(*str) || 0==*str)\n      return true;\n    ++str;\n  }\n  return false;\n}\n\nstd::string valueToString(LargestInt value) {\n  UIntToStringBuffer buffer;\n  char* current = buffer + sizeof(buffer);\n  bool isNegative = value < 0;\n  if (isNegative)\n    value = -value;\n  uintToString(LargestUInt(value), current);\n  if (isNegative)\n    *--current = '-';\n  assert(current >= buffer);\n  return current;\n}\n\nstd::string valueToString(LargestUInt value) {\n  UIntToStringBuffer buffer;\n  char* current = buffer + sizeof(buffer);\n  uintToString(value, current);\n  assert(current >= buffer);\n  return current;\n}\n\n#if defined(JSON_HAS_INT64)\n\nstd::string valueToString(Int value) {\n  return valueToString(LargestInt(value));\n}\n\nstd::string valueToString(UInt value) {\n  return valueToString(LargestUInt(value));\n}\n\n#endif // # if defined(JSON_HAS_INT64)\n\nstd::string valueToString(double value) {\n  // Allocate a buffer that is more than large enough to store the 16 digits of\n  // precision requested below.\n  char buffer[32];\n  int len = -1;\n\n// Print into the buffer. We need not request the alternative representation\n// that always has a decimal point because JSON doesn't distingish the\n// concepts of reals and integers.\n#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with\n                                                      // visual studio 2005 to\n                                                      // avoid warning.\n#if defined(WINCE)\n  len = _snprintf(buffer, sizeof(buffer), \"%.17g\", value);\n#else\n  len = sprintf_s(buffer, sizeof(buffer), \"%.17g\", value);\n#endif\n#else\n  if (isfinite(value)) {\n    len = snprintf(buffer, sizeof(buffer), \"%.17g\", value);\n  } else {\n    // IEEE standard states that NaN values will not compare to themselves\n    if (value != value) {\n      len = snprintf(buffer, sizeof(buffer), \"null\");\n    } else if (value < 0) {\n      len = snprintf(buffer, sizeof(buffer), \"-1e+9999\");\n    } else {\n      len = snprintf(buffer, sizeof(buffer), \"1e+9999\");\n    }\n    // For those, we do not need to call fixNumLoc, but it is fast.\n  }\n#endif\n  assert(len >= 0);\n  fixNumericLocale(buffer, buffer + len);\n  return buffer;\n}\n\nstd::string valueToString(bool value) { return value ? \"true\" : \"false\"; }\n\nstd::string valueToQuotedString(const char* value) {\n  if (value == NULL)\n    return \"\";\n  // Not sure how to handle unicode...\n  if (strpbrk(value, \"\\\"\\\\\\b\\f\\n\\r\\t\") == NULL &&\n      !containsControlCharacter(value))\n    return std::string(\"\\\"\") + value + \"\\\"\";\n  // We have to walk value and escape any special characters.\n  // Appending to std::string is not efficient, but this should be rare.\n  // (Note: forward slashes are *not* rare, but I am not escaping them.)\n  std::string::size_type maxsize =\n      strlen(value) * 2 + 3; // allescaped+quotes+NULL\n  std::string result;\n  result.reserve(maxsize); // to avoid lots of mallocs\n  result += \"\\\"\";\n  for (const char* c = value; *c != 0; ++c) {\n    switch (*c) {\n    case '\\\"':\n      result += \"\\\\\\\"\";\n      break;\n    case '\\\\':\n      result += \"\\\\\\\\\";\n      break;\n    case '\\b':\n      result += \"\\\\b\";\n      break;\n    case '\\f':\n      result += \"\\\\f\";\n      break;\n    case '\\n':\n      result += \"\\\\n\";\n      break;\n    case '\\r':\n      result += \"\\\\r\";\n      break;\n    case '\\t':\n      result += \"\\\\t\";\n      break;\n    // case '/':\n    // Even though \\/ is considered a legal escape in JSON, a bare\n    // slash is also legal, so I see no reason to escape it.\n    // (I hope I am not misunderstanding something.\n    // blep notes: actually escaping \\/ may be useful in javascript to avoid </\n    // sequence.\n    // Should add a flag to allow this compatibility mode and prevent this\n    // sequence from occurring.\n    default:\n      if (isControlCharacter(*c)) {\n        std::ostringstream oss;\n        oss << \"\\\\u\" << std::hex << std::uppercase << std::setfill('0')\n            << std::setw(4) << static_cast<int>(*c);\n        result += oss.str();\n      } else {\n        result += *c;\n      }\n      break;\n    }\n  }\n  result += \"\\\"\";\n  return result;\n}\n\n// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp\nstatic char const* strnpbrk(char const* s, char const* accept, size_t n) {\n  assert((s || !n) && accept);\n\n  char const* const end = s + n;\n  for (char const* cur = s; cur < end; ++cur) {\n    int const c = *cur;\n    for (char const* a = accept; *a; ++a) {\n      if (*a == c) {\n        return cur;\n      }\n    }\n  }\n  return NULL;\n}\nstatic std::string valueToQuotedStringN(const char* value, unsigned length) {\n  if (value == NULL)\n    return \"\";\n  // Not sure how to handle unicode...\n  if (strnpbrk(value, \"\\\"\\\\\\b\\f\\n\\r\\t\", length) == NULL &&\n      !containsControlCharacter0(value, length))\n    return std::string(\"\\\"\") + value + \"\\\"\";\n  // We have to walk value and escape any special characters.\n  // Appending to std::string is not efficient, but this should be rare.\n  // (Note: forward slashes are *not* rare, but I am not escaping them.)\n  std::string::size_type maxsize =\n      length * 2 + 3; // allescaped+quotes+NULL\n  std::string result;\n  result.reserve(maxsize); // to avoid lots of mallocs\n  result += \"\\\"\";\n  char const* end = value + length;\n  for (const char* c = value; c != end; ++c) {\n    switch (*c) {\n    case '\\\"':\n      result += \"\\\\\\\"\";\n      break;\n    case '\\\\':\n      result += \"\\\\\\\\\";\n      break;\n    case '\\b':\n      result += \"\\\\b\";\n      break;\n    case '\\f':\n      result += \"\\\\f\";\n      break;\n    case '\\n':\n      result += \"\\\\n\";\n      break;\n    case '\\r':\n      result += \"\\\\r\";\n      break;\n    case '\\t':\n      result += \"\\\\t\";\n      break;\n    // case '/':\n    // Even though \\/ is considered a legal escape in JSON, a bare\n    // slash is also legal, so I see no reason to escape it.\n    // (I hope I am not misunderstanding something.)\n    // blep notes: actually escaping \\/ may be useful in javascript to avoid </\n    // sequence.\n    // Should add a flag to allow this compatibility mode and prevent this\n    // sequence from occurring.\n    default:\n      if ((isControlCharacter(*c)) || (*c == 0)) {\n        std::ostringstream oss;\n        oss << \"\\\\u\" << std::hex << std::uppercase << std::setfill('0')\n            << std::setw(4) << static_cast<int>(*c);\n        result += oss.str();\n      } else {\n        result += *c;\n      }\n      break;\n    }\n  }\n  result += \"\\\"\";\n  return result;\n}\n\n// Class Writer\n// //////////////////////////////////////////////////////////////////\nWriter::~Writer() {}\n\n// Class FastWriter\n// //////////////////////////////////////////////////////////////////\n\nFastWriter::FastWriter()\n    : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),\n      omitEndingLineFeed_(false) {}\n\nvoid FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }\n\nvoid FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }\n\nvoid FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }\n\nstd::string FastWriter::write(const Value& root) {\n  document_ = \"\";\n  writeValue(root);\n  if (!omitEndingLineFeed_)\n    document_ += \"\\n\";\n  return document_;\n}\n\nvoid FastWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n  case nullValue:\n    if (!dropNullPlaceholders_)\n      document_ += \"null\";\n    break;\n  case intValue:\n    document_ += valueToString(value.asLargestInt());\n    break;\n  case uintValue:\n    document_ += valueToString(value.asLargestUInt());\n    break;\n  case realValue:\n    document_ += valueToString(value.asDouble());\n    break;\n  case stringValue:\n    document_ += valueToQuotedString(value.asCString());\n    break;\n  case booleanValue:\n    document_ += valueToString(value.asBool());\n    break;\n  case arrayValue: {\n    document_ += '[';\n    int size = value.size();\n    for (int index = 0; index < size; ++index) {\n      if (index > 0)\n        document_ += ',';\n      writeValue(value[index]);\n    }\n    document_ += ']';\n  } break;\n  case objectValue: {\n    Value::Members members(value.getMemberNames());\n    document_ += '{';\n    for (Value::Members::iterator it = members.begin(); it != members.end();\n         ++it) {\n      const std::string& name = *it;\n      if (it != members.begin())\n        document_ += ',';\n      document_ += valueToQuotedStringN(name.data(), name.length());\n      document_ += yamlCompatiblityEnabled_ ? \": \" : \":\";\n      writeValue(value[name]);\n    }\n    document_ += '}';\n  } break;\n  }\n}\n\n// Class StyledWriter\n// //////////////////////////////////////////////////////////////////\n\nStyledWriter::StyledWriter()\n    : rightMargin_(74), indentSize_(3), addChildValues_() {}\n\nstd::string StyledWriter::write(const Value& root) {\n  document_ = \"\";\n  addChildValues_ = false;\n  indentString_ = \"\";\n  writeCommentBeforeValue(root);\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  document_ += \"\\n\";\n  return document_;\n}\n\nvoid StyledWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n  case nullValue:\n    pushValue(\"null\");\n    break;\n  case intValue:\n    pushValue(valueToString(value.asLargestInt()));\n    break;\n  case uintValue:\n    pushValue(valueToString(value.asLargestUInt()));\n    break;\n  case realValue:\n    pushValue(valueToString(value.asDouble()));\n    break;\n  case stringValue:\n  {\n    // Is NULL is possible for value.string_?\n    char const* str;\n    char const* end;\n    bool ok = value.getString(&str, &end);\n    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));\n    else pushValue(\"\");\n    break;\n  }\n  case booleanValue:\n    pushValue(valueToString(value.asBool()));\n    break;\n  case arrayValue:\n    writeArrayValue(value);\n    break;\n  case objectValue: {\n    Value::Members members(value.getMemberNames());\n    if (members.empty())\n      pushValue(\"{}\");\n    else {\n      writeWithIndent(\"{\");\n      indent();\n      Value::Members::iterator it = members.begin();\n      for (;;) {\n        const std::string& name = *it;\n        const Value& childValue = value[name];\n        writeCommentBeforeValue(childValue);\n        writeWithIndent(valueToQuotedString(name.c_str()));\n        document_ += \" : \";\n        writeValue(childValue);\n        if (++it == members.end()) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        document_ += ',';\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"}\");\n    }\n  } break;\n  }\n}\n\nvoid StyledWriter::writeArrayValue(const Value& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isArrayMultiLine = isMultineArray(value);\n    if (isArrayMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        const Value& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          writeIndent();\n          writeValue(childValue);\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        document_ += ',';\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else // output on a single line\n    {\n      assert(childValues_.size() == size);\n      document_ += \"[ \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0)\n          document_ += \", \";\n        document_ += childValues_[index];\n      }\n      document_ += \" ]\";\n    }\n  }\n}\n\nbool StyledWriter::isMultineArray(const Value& value) {\n  int size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (int index = 0; index < size && !isMultiLine; ++index) {\n    const Value& childValue = value[index];\n    isMultiLine =\n        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&\n                        childValue.size() > 0);\n  }\n  if (!isMultiLine) // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'\n    for (int index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += int(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid StyledWriter::pushValue(const std::string& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    document_ += value;\n}\n\nvoid StyledWriter::writeIndent() {\n  if (!document_.empty()) {\n    char last = document_[document_.length() - 1];\n    if (last == ' ') // already indented\n      return;\n    if (last != '\\n') // Comments may add new-line\n      document_ += '\\n';\n  }\n  document_ += indentString_;\n}\n\nvoid StyledWriter::writeWithIndent(const std::string& value) {\n  writeIndent();\n  document_ += value;\n}\n\nvoid StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }\n\nvoid StyledWriter::unindent() {\n  assert(int(indentString_.size()) >= indentSize_);\n  indentString_.resize(indentString_.size() - indentSize_);\n}\n\nvoid StyledWriter::writeCommentBeforeValue(const Value& root) {\n  if (!root.hasComment(commentBefore))\n    return;\n\n  document_ += \"\\n\";\n  writeIndent();\n  const std::string& comment = root.getComment(commentBefore);\n  std::string::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    document_ += *iter;\n    if (*iter == '\\n' &&\n       (iter != comment.end() && *(iter + 1) == '/'))\n      writeIndent();\n    ++iter;\n  }\n\n  // Comments are stripped of trailing newlines, so add one here\n  document_ += \"\\n\";\n}\n\nvoid StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {\n  if (root.hasComment(commentAfterOnSameLine))\n    document_ += \" \" + root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    document_ += \"\\n\";\n    document_ += root.getComment(commentAfter);\n    document_ += \"\\n\";\n  }\n}\n\nbool StyledWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) ||\n         value.hasComment(commentAfterOnSameLine) ||\n         value.hasComment(commentAfter);\n}\n\n// Class StyledStreamWriter\n// //////////////////////////////////////////////////////////////////\n\nStyledStreamWriter::StyledStreamWriter(std::string indentation)\n    : document_(NULL), rightMargin_(74), indentation_(indentation),\n      addChildValues_() {}\n\nvoid StyledStreamWriter::write(std::ostream& out, const Value& root) {\n  document_ = &out;\n  addChildValues_ = false;\n  indentString_ = \"\";\n  indented_ = true;\n  writeCommentBeforeValue(root);\n  if (!indented_) writeIndent();\n  indented_ = true;\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  *document_ << \"\\n\";\n  document_ = NULL; // Forget the stream, for safety.\n}\n\nvoid StyledStreamWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n  case nullValue:\n    pushValue(\"null\");\n    break;\n  case intValue:\n    pushValue(valueToString(value.asLargestInt()));\n    break;\n  case uintValue:\n    pushValue(valueToString(value.asLargestUInt()));\n    break;\n  case realValue:\n    pushValue(valueToString(value.asDouble()));\n    break;\n  case stringValue:\n    pushValue(valueToQuotedString(value.asCString()));\n    break;\n  case booleanValue:\n    pushValue(valueToString(value.asBool()));\n    break;\n  case arrayValue:\n    writeArrayValue(value);\n    break;\n  case objectValue: {\n    Value::Members members(value.getMemberNames());\n    if (members.empty())\n      pushValue(\"{}\");\n    else {\n      writeWithIndent(\"{\");\n      indent();\n      Value::Members::iterator it = members.begin();\n      for (;;) {\n        const std::string& name = *it;\n        const Value& childValue = value[name];\n        writeCommentBeforeValue(childValue);\n        writeWithIndent(valueToQuotedString(name.c_str()));\n        *document_ << \" : \";\n        writeValue(childValue);\n        if (++it == members.end()) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *document_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"}\");\n    }\n  } break;\n  }\n}\n\nvoid StyledStreamWriter::writeArrayValue(const Value& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isArrayMultiLine = isMultineArray(value);\n    if (isArrayMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        const Value& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          if (!indented_) writeIndent();\n          indented_ = true;\n          writeValue(childValue);\n          indented_ = false;\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *document_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else // output on a single line\n    {\n      assert(childValues_.size() == size);\n      *document_ << \"[ \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0)\n          *document_ << \", \";\n        *document_ << childValues_[index];\n      }\n      *document_ << \" ]\";\n    }\n  }\n}\n\nbool StyledStreamWriter::isMultineArray(const Value& value) {\n  int size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (int index = 0; index < size && !isMultiLine; ++index) {\n    const Value& childValue = value[index];\n    isMultiLine =\n        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&\n                        childValue.size() > 0);\n  }\n  if (!isMultiLine) // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'\n    for (int index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += int(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid StyledStreamWriter::pushValue(const std::string& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    *document_ << value;\n}\n\nvoid StyledStreamWriter::writeIndent() {\n  // blep intended this to look at the so-far-written string\n  // to determine whether we are already indented, but\n  // with a stream we cannot do that. So we rely on some saved state.\n  // The caller checks indented_.\n  *document_ << '\\n' << indentString_;\n}\n\nvoid StyledStreamWriter::writeWithIndent(const std::string& value) {\n  if (!indented_) writeIndent();\n  *document_ << value;\n  indented_ = false;\n}\n\nvoid StyledStreamWriter::indent() { indentString_ += indentation_; }\n\nvoid StyledStreamWriter::unindent() {\n  assert(indentString_.size() >= indentation_.size());\n  indentString_.resize(indentString_.size() - indentation_.size());\n}\n\nvoid StyledStreamWriter::writeCommentBeforeValue(const Value& root) {\n  if (!root.hasComment(commentBefore))\n    return;\n\n  if (!indented_) writeIndent();\n  const std::string& comment = root.getComment(commentBefore);\n  std::string::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    *document_ << *iter;\n    if (*iter == '\\n' &&\n       (iter != comment.end() && *(iter + 1) == '/'))\n      // writeIndent();  // would include newline\n      *document_ << indentString_;\n    ++iter;\n  }\n  indented_ = false;\n}\n\nvoid StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {\n  if (root.hasComment(commentAfterOnSameLine))\n    *document_ << ' ' << root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    writeIndent();\n    *document_ << root.getComment(commentAfter);\n  }\n  indented_ = false;\n}\n\nbool StyledStreamWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) ||\n         value.hasComment(commentAfterOnSameLine) ||\n         value.hasComment(commentAfter);\n}\n\n//////////////////////////\n// BuiltStyledStreamWriter\n\n/// Scoped enums are not available until C++11.\nstruct CommentStyle {\n  /// Decide whether to write comments.\n  enum Enum {\n    None,  ///< Drop all comments.\n    Most,  ///< Recover odd behavior of previous versions (not implemented yet).\n    All  ///< Keep all comments.\n  };\n};\n\nstruct BuiltStyledStreamWriter : public StreamWriter\n{\n  BuiltStyledStreamWriter(\n      std::string const& indentation,\n      CommentStyle::Enum cs,\n      std::string const& colonSymbol,\n      std::string const& nullSymbol,\n      std::string const& endingLineFeedSymbol);\n  virtual int write(Value const& root, std::ostream* sout);\nprivate:\n  void writeValue(Value const& value);\n  void writeArrayValue(Value const& value);\n  bool isMultineArray(Value const& value);\n  void pushValue(std::string const& value);\n  void writeIndent();\n  void writeWithIndent(std::string const& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(Value const& root);\n  void writeCommentAfterValueOnSameLine(Value const& root);\n  static bool hasCommentForValue(const Value& value);\n\n  typedef std::vector<std::string> ChildValues;\n\n  ChildValues childValues_;\n  std::string indentString_;\n  int rightMargin_;\n  std::string indentation_;\n  CommentStyle::Enum cs_;\n  std::string colonSymbol_;\n  std::string nullSymbol_;\n  std::string endingLineFeedSymbol_;\n  bool addChildValues_ : 1;\n  bool indented_ : 1;\n};\nBuiltStyledStreamWriter::BuiltStyledStreamWriter(\n      std::string const& indentation,\n      CommentStyle::Enum cs,\n      std::string const& colonSymbol,\n      std::string const& nullSymbol,\n      std::string const& endingLineFeedSymbol)\n  : rightMargin_(74)\n  , indentation_(indentation)\n  , cs_(cs)\n  , colonSymbol_(colonSymbol)\n  , nullSymbol_(nullSymbol)\n  , endingLineFeedSymbol_(endingLineFeedSymbol)\n  , addChildValues_(false)\n  , indented_(false)\n{\n}\nint BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)\n{\n  sout_ = sout;\n  addChildValues_ = false;\n  indented_ = true;\n  indentString_ = \"\";\n  writeCommentBeforeValue(root);\n  if (!indented_) writeIndent();\n  indented_ = true;\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  *sout_ << endingLineFeedSymbol_;\n  sout_ = NULL;\n  return 0;\n}\nvoid BuiltStyledStreamWriter::writeValue(Value const& value) {\n  switch (value.type()) {\n  case nullValue:\n    pushValue(nullSymbol_);\n    break;\n  case intValue:\n    pushValue(valueToString(value.asLargestInt()));\n    break;\n  case uintValue:\n    pushValue(valueToString(value.asLargestUInt()));\n    break;\n  case realValue:\n    pushValue(valueToString(value.asDouble()));\n    break;\n  case stringValue:\n  {\n    // Is NULL is possible for value.string_?\n    char const* str;\n    char const* end;\n    bool ok = value.getString(&str, &end);\n    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));\n    else pushValue(\"\");\n    break;\n  }\n  case booleanValue:\n    pushValue(valueToString(value.asBool()));\n    break;\n  case arrayValue:\n    writeArrayValue(value);\n    break;\n  case objectValue: {\n    Value::Members members(value.getMemberNames());\n    if (members.empty())\n      pushValue(\"{}\");\n    else {\n      writeWithIndent(\"{\");\n      indent();\n      Value::Members::iterator it = members.begin();\n      for (;;) {\n        std::string const& name = *it;\n        Value const& childValue = value[name];\n        writeCommentBeforeValue(childValue);\n        writeWithIndent(valueToQuotedStringN(name.data(), name.length()));\n        *sout_ << colonSymbol_;\n        writeValue(childValue);\n        if (++it == members.end()) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *sout_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"}\");\n    }\n  } break;\n  }\n}\n\nvoid BuiltStyledStreamWriter::writeArrayValue(Value const& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);\n    if (isMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        Value const& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          if (!indented_) writeIndent();\n          indented_ = true;\n          writeValue(childValue);\n          indented_ = false;\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *sout_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else // output on a single line\n    {\n      assert(childValues_.size() == size);\n      *sout_ << \"[\";\n      if (!indentation_.empty()) *sout_ << \" \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0)\n          *sout_ << \", \";\n        *sout_ << childValues_[index];\n      }\n      if (!indentation_.empty()) *sout_ << \" \";\n      *sout_ << \"]\";\n    }\n  }\n}\n\nbool BuiltStyledStreamWriter::isMultineArray(Value const& value) {\n  int size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (int index = 0; index < size && !isMultiLine; ++index) {\n    Value const& childValue = value[index];\n    isMultiLine =\n        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&\n                        childValue.size() > 0);\n  }\n  if (!isMultiLine) // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'\n    for (int index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += int(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid BuiltStyledStreamWriter::pushValue(std::string const& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    *sout_ << value;\n}\n\nvoid BuiltStyledStreamWriter::writeIndent() {\n  // blep intended this to look at the so-far-written string\n  // to determine whether we are already indented, but\n  // with a stream we cannot do that. So we rely on some saved state.\n  // The caller checks indented_.\n\n  if (!indentation_.empty()) {\n    // In this case, drop newlines too.\n    *sout_ << '\\n' << indentString_;\n  }\n}\n\nvoid BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {\n  if (!indented_) writeIndent();\n  *sout_ << value;\n  indented_ = false;\n}\n\nvoid BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }\n\nvoid BuiltStyledStreamWriter::unindent() {\n  assert(indentString_.size() >= indentation_.size());\n  indentString_.resize(indentString_.size() - indentation_.size());\n}\n\nvoid BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {\n  if (cs_ == CommentStyle::None) return;\n  if (!root.hasComment(commentBefore))\n    return;\n\n  if (!indented_) writeIndent();\n  const std::string& comment = root.getComment(commentBefore);\n  std::string::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    *sout_ << *iter;\n    if (*iter == '\\n' &&\n       (iter != comment.end() && *(iter + 1) == '/'))\n      // writeIndent();  // would write extra newline\n      *sout_ << indentString_;\n    ++iter;\n  }\n  indented_ = false;\n}\n\nvoid BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {\n  if (cs_ == CommentStyle::None) return;\n  if (root.hasComment(commentAfterOnSameLine))\n    *sout_ << \" \" + root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    writeIndent();\n    *sout_ << root.getComment(commentAfter);\n  }\n}\n\n// static\nbool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) ||\n         value.hasComment(commentAfterOnSameLine) ||\n         value.hasComment(commentAfter);\n}\n\n///////////////\n// StreamWriter\n\nStreamWriter::StreamWriter()\n    : sout_(NULL)\n{\n}\nStreamWriter::~StreamWriter()\n{\n}\nStreamWriter::Factory::~Factory()\n{}\nStreamWriterBuilder::StreamWriterBuilder()\n{\n  setDefaults(&settings_);\n}\nStreamWriterBuilder::~StreamWriterBuilder()\n{}\nStreamWriter* StreamWriterBuilder::newStreamWriter() const\n{\n  std::string indentation = settings_[\"indentation\"].asString();\n  std::string cs_str = settings_[\"commentStyle\"].asString();\n  bool eyc = settings_[\"enableYAMLCompatibility\"].asBool();\n  bool dnp = settings_[\"dropNullPlaceholders\"].asBool();\n  CommentStyle::Enum cs = CommentStyle::All;\n  if (cs_str == \"All\") {\n    cs = CommentStyle::All;\n  } else if (cs_str == \"None\") {\n    cs = CommentStyle::None;\n  } else {\n    throwRuntimeError(\"commentStyle must be 'All' or 'None'\");\n  }\n  std::string colonSymbol = \" : \";\n  if (eyc) {\n    colonSymbol = \": \";\n  } else if (indentation.empty()) {\n    colonSymbol = \":\";\n  }\n  std::string nullSymbol = \"null\";\n  if (dnp) {\n    nullSymbol = \"\";\n  }\n  std::string endingLineFeedSymbol = \"\";\n  return new BuiltStyledStreamWriter(\n      indentation, cs,\n      colonSymbol, nullSymbol, endingLineFeedSymbol);\n}\nstatic void getValidWriterKeys(std::set<std::string>* valid_keys)\n{\n  valid_keys->clear();\n  valid_keys->insert(\"indentation\");\n  valid_keys->insert(\"commentStyle\");\n  valid_keys->insert(\"enableYAMLCompatibility\");\n  valid_keys->insert(\"dropNullPlaceholders\");\n}\nbool StreamWriterBuilder::validate(Json::Value* invalid) const\n{\n  Json::Value my_invalid;\n  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL\n  Json::Value& inv = *invalid;\n  std::set<std::string> valid_keys;\n  getValidWriterKeys(&valid_keys);\n  Value::Members keys = settings_.getMemberNames();\n  size_t n = keys.size();\n  for (size_t i = 0; i < n; ++i) {\n    std::string const& key = keys[i];\n    if (valid_keys.find(key) == valid_keys.end()) {\n      inv[key] = settings_[key];\n    }\n  }\n  return 0u == inv.size();\n}\nValue& StreamWriterBuilder::operator[](std::string key)\n{\n  return settings_[key];\n}\n// static\nvoid StreamWriterBuilder::setDefaults(Json::Value* settings)\n{\n  //! [StreamWriterBuilderDefaults]\n  (*settings)[\"commentStyle\"] = \"All\";\n  (*settings)[\"indentation\"] = \"\\t\";\n  (*settings)[\"enableYAMLCompatibility\"] = false;\n  (*settings)[\"dropNullPlaceholders\"] = false;\n  //! [StreamWriterBuilderDefaults]\n}\n\nstd::string writeString(StreamWriter::Factory const& builder, Value const& root) {\n  std::ostringstream sout;\n  StreamWriterPtr const writer(builder.newStreamWriter());\n  writer->write(root, &sout);\n  return sout.str();\n}\n\nstd::ostream& operator<<(std::ostream& sout, Value const& root) {\n  StreamWriterBuilder builder;\n  StreamWriterPtr const writer(builder.newStreamWriter());\n  writer->write(root, &sout);\n  return sout;\n}\n\n} // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_writer.cpp\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n"
  },
  {
    "path": "src/common/json.h",
    "content": "/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).\n/// It is intended to be used with #include \"json.h\"\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n/*\nThe JsonCpp library's source code, including accompanying documentation, \ntests and demonstration applications, are licensed under the following\nconditions...\n\nThe author (Baptiste Lepilleur) explicitly disclaims copyright in all \njurisdictions which recognize such a disclaimer. In such jurisdictions, \nthis software is released into the Public Domain.\n\nIn jurisdictions which do not recognize Public Domain property (e.g. Germany as of\n2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is\nreleased under the terms of the MIT License (see below).\n\nIn jurisdictions which recognize Public Domain property, the user of this \nsoftware may choose to accept it either as 1) Public Domain, 2) under the \nconditions of the MIT License (see below), or 3) under the terms of dual \nPublic Domain/MIT License conditions described here, as they choose.\n\nThe MIT License is about as close to Public Domain as a license can get, and is\ndescribed in clear, concise terms at:\n\n   http://en.wikipedia.org/wiki/MIT_License\n   \nThe full text of the MIT License follows:\n\n========================================================================\nCopyright (c) 2007-2010 Baptiste Lepilleur\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n========================================================================\n(END LICENSE TEXT)\n\nThe MIT license is compatible with both the GPL and commercial\nsoftware, affording one all of the rights of Public Domain with the\nminor nuisance of being required to keep the above copyright notice\nand license text in the source code. Note also that by accepting the\nPublic Domain \"license\" you can re-license your copy using whatever\nlicense you like.\n\n*/\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n#ifndef JSON_AMALGATED_H_INCLUDED\n# define JSON_AMALGATED_H_INCLUDED\n/// If defined, indicates that the source file is amalgated\n/// to prevent private header inclusion.\n#define JSON_IS_AMALGAMATION\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/version.h\n// //////////////////////////////////////////////////////////////////////\n\n// DO NOT EDIT. This file is generated by CMake from  \"version\"\n// and \"version.h.in\" files.\n// Run CMake configure step to update it.\n#ifndef JSON_VERSION_H_INCLUDED\n# define JSON_VERSION_H_INCLUDED\n\n# define JSONCPP_VERSION_STRING \"1.6.0\"\n# define JSONCPP_VERSION_MAJOR 1\n# define JSONCPP_VERSION_MINOR 6\n# define JSONCPP_VERSION_PATCH 0\n# define JSONCPP_VERSION_QUALIFIER\n# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))\n\n#endif // JSON_VERSION_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/version.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_CONFIG_H_INCLUDED\n#define JSON_CONFIG_H_INCLUDED\n\n/// If defined, indicates that json library is embedded in CppTL library.\n//# define JSON_IN_CPPTL 1\n\n/// If defined, indicates that json may leverage CppTL library\n//#  define JSON_USE_CPPTL 1\n/// If defined, indicates that cpptl vector based map should be used instead of\n/// std::map\n/// as Value container.\n//#  define JSON_USE_CPPTL_SMALLMAP 1\n\n// If non-zero, the library uses exceptions to report bad input instead of C\n// assertion macros. The default is to use exceptions.\n#ifndef JSON_USE_EXCEPTION\n#define JSON_USE_EXCEPTION 1\n#endif\n\n/// If defined, indicates that the target file is amalgated\n/// to prevent private header inclusion.\n/// Remarks: it is automatically defined in the generated amalgated header.\n// #define JSON_IS_AMALGAMATION\n\n#ifdef JSON_IN_CPPTL\n#include <cpptl/config.h>\n#ifndef JSON_USE_CPPTL\n#define JSON_USE_CPPTL 1\n#endif\n#endif\n\n#ifdef JSON_IN_CPPTL\n#define JSON_API CPPTL_API\n#elif defined(JSON_DLL_BUILD)\n#if defined(_MSC_VER)\n#define JSON_API __declspec(dllexport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif // if defined(_MSC_VER)\n#elif defined(JSON_DLL)\n#if defined(_MSC_VER)\n#define JSON_API __declspec(dllimport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif // if defined(_MSC_VER)\n#endif // ifdef JSON_IN_CPPTL\n#if !defined(JSON_API)\n#define JSON_API\n#endif\n\n// If JSON_NO_INT64 is defined, then Json only support C++ \"int\" type for\n// integer\n// Storages, and 64 bits integer support is disabled.\n// #define JSON_NO_INT64 1\n\n#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6\n// Microsoft Visual Studio 6 only support conversion from __int64 to double\n// (no conversion from unsigned __int64).\n#define JSON_USE_INT64_DOUBLE_CONVERSION 1\n// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'\n// characters in the debug information)\n// All projects I've ever seen with VS6 were using this globally (not bothering\n// with pragma push/pop).\n#pragma warning(disable : 4786)\n#endif // if defined(_MSC_VER)  &&  _MSC_VER < 1200 // MSVC 6\n\n#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008\n/// Indicates that the following function is deprecated.\n#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))\n#elif defined(__clang__) && defined(__has_feature)\n#if __has_feature(attribute_deprecated_with_message)\n#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))\n#endif\n#elif defined(__GNUC__) &&  (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))\n#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))\n#elif defined(__GNUC__) &&  (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n#define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))\n#endif\n\n#if !defined(JSONCPP_DEPRECATED)\n#define JSONCPP_DEPRECATED(message)\n#endif // if !defined(JSONCPP_DEPRECATED)\n\nnamespace Json {\ntypedef int Int;\ntypedef unsigned int UInt;\n#if defined(JSON_NO_INT64)\ntypedef int LargestInt;\ntypedef unsigned int LargestUInt;\n#undef JSON_HAS_INT64\n#else                 // if defined(JSON_NO_INT64)\n// For Microsoft Visual use specific types as long long is not supported\n#if defined(_MSC_VER) // Microsoft Visual Studio\ntypedef __int64 Int64;\ntypedef unsigned __int64 UInt64;\n#else                 // if defined(_MSC_VER) // Other platforms, use long long\ntypedef long long int Int64;\ntypedef unsigned long long int UInt64;\n#endif // if defined(_MSC_VER)\ntypedef Int64 LargestInt;\ntypedef UInt64 LargestUInt;\n#define JSON_HAS_INT64\n#endif // if defined(JSON_NO_INT64)\n} // end namespace Json\n\n#endif // JSON_CONFIG_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_FORWARDS_H_INCLUDED\n#define JSON_FORWARDS_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"config.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\n// writer.h\nclass FastWriter;\nclass StyledWriter;\n\n// reader.h\nclass Reader;\n\n// features.h\nclass Features;\n\n// value.h\ntypedef unsigned int ArrayIndex;\nclass StaticString;\nclass Path;\nclass PathArgument;\nclass Value;\nclass ValueIteratorBase;\nclass ValueIterator;\nclass ValueConstIterator;\n\n} // namespace Json\n\n#endif // JSON_FORWARDS_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/features.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_FEATURES_H_INCLUDED\n#define CPPTL_JSON_FEATURES_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"forwards.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\n/** \\brief Configuration passed to reader and writer.\n * This configuration object can be used to force the Reader or Writer\n * to behave in a standard conforming way.\n */\nclass JSON_API Features {\npublic:\n  /** \\brief A configuration that allows all features and assumes all strings\n   * are UTF-8.\n   * - C & C++ comments are allowed\n   * - Root object can be any JSON value\n   * - Assumes Value strings are encoded in UTF-8\n   */\n  static Features all();\n\n  /** \\brief A configuration that is strictly compatible with the JSON\n   * specification.\n   * - Comments are forbidden.\n   * - Root object must be either an array or an object value.\n   * - Assumes Value strings are encoded in UTF-8\n   */\n  static Features strictMode();\n\n  /** \\brief Initialize the configuration like JsonConfig::allFeatures;\n   */\n  Features();\n\n  /// \\c true if comments are allowed. Default: \\c true.\n  bool allowComments_;\n\n  /// \\c true if root must be either an array or an object value. Default: \\c\n  /// false.\n  bool strictRoot_;\n\n  /// \\c true if dropped null placeholders are allowed. Default: \\c false.\n  bool allowDroppedNullPlaceholders_;\n\n  /// \\c true if numeric object key are allowed. Default: \\c false.\n  bool allowNumericKeys_;\n};\n\n} // namespace Json\n\n#endif // CPPTL_JSON_FEATURES_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/features.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/value.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_H_INCLUDED\n#define CPPTL_JSON_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"forwards.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <string>\n#include <vector>\n#include <exception>\n\n#ifndef JSON_USE_CPPTL_SMALLMAP\n#include <map>\n#else\n#include <cpptl/smallmap.h>\n#endif\n#ifdef JSON_USE_CPPTL\n#include <cpptl/forwards.h>\n#endif\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n/** \\brief JSON (JavaScript Object Notation).\n */\nnamespace Json {\n\n/** Base class for all exceptions we throw.\n *\n * We use nothing but these internally. Of course, STL can throw others.\n */\nclass JSON_API Exception;\n/** Exceptions which the user cannot easily avoid.\n *\n * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input\n * \n * \\remark derived from Json::Exception\n */\nclass JSON_API RuntimeError;\n/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.\n *\n * These are precondition-violations (user bugs) and internal errors (our bugs).\n * \n * \\remark derived from Json::Exception\n */\nclass JSON_API LogicError;\n\n/// used internally\nvoid throwRuntimeError(std::string const& msg);\n/// used internally\nvoid throwLogicError(std::string const& msg);\n\n/** \\brief Type of the value held by a Value object.\n */\nenum ValueType {\n  nullValue = 0, ///< 'null' value\n  intValue,      ///< signed integer value\n  uintValue,     ///< unsigned integer value\n  realValue,     ///< double value\n  stringValue,   ///< UTF-8 string value\n  booleanValue,  ///< bool value\n  arrayValue,    ///< array value (ordered list)\n  objectValue    ///< object value (collection of name/value pairs).\n};\n\nenum CommentPlacement {\n  commentBefore = 0,      ///< a comment placed on the line before a value\n  commentAfterOnSameLine, ///< a comment just after a value on the same line\n  commentAfter, ///< a comment on the line after a value (only make sense for\n  /// root value)\n  numberOfCommentPlacement\n};\n\n//# ifdef JSON_USE_CPPTL\n//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;\n//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;\n//# endif\n\n/** \\brief Lightweight wrapper to tag static string.\n *\n * Value constructor and objectValue member assignement takes advantage of the\n * StaticString and avoid the cost of string duplication when storing the\n * string or the member name.\n *\n * Example of usage:\n * \\code\n * Json::Value aValue( StaticString(\"some text\") );\n * Json::Value object;\n * static const StaticString code(\"code\");\n * object[code] = 1234;\n * \\endcode\n */\nclass JSON_API StaticString {\npublic:\n  explicit StaticString(const char* czstring) : c_str_(czstring) {}\n\n  operator const char*() const { return c_str_; }\n\n  const char* c_str() const { return c_str_; }\n\nprivate:\n  const char* c_str_;\n};\n\n/** \\brief Represents a <a HREF=\"http://www.json.org\">JSON</a> value.\n *\n * This class is a discriminated union wrapper that can represents a:\n * - signed integer [range: Value::minInt - Value::maxInt]\n * - unsigned integer (range: 0 - Value::maxUInt)\n * - double\n * - UTF-8 string\n * - boolean\n * - 'null'\n * - an ordered list of Value\n * - collection of name/value pairs (javascript object)\n *\n * The type of the held value is represented by a #ValueType and\n * can be obtained using type().\n *\n * Values of an #objectValue or #arrayValue can be accessed using operator[]()\n * methods.\n * Non-const methods will automatically create the a #nullValue element\n * if it does not exist.\n * The sequence of an #arrayValue will be automatically resized and initialized\n * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.\n *\n * The get() methods can be used to obtain default value in the case the\n * required element does not exist.\n *\n * It is possible to iterate over the list of a #objectValue values using\n * the getMemberNames() method.\n *\n * \\note #Value string-length fit in size_t, but keys must be < 2^30.\n * (The reason is an implementation detail.) A #CharReader will raise an\n * exception if a bound is exceeded to avoid security holes in your app,\n * but the Value API does *not* check bounds. That is the responsibility\n * of the caller.\n */\nclass JSON_API Value {\n  friend class ValueIteratorBase;\npublic:\n  typedef std::vector<std::string> Members;\n  typedef ValueIterator iterator;\n  typedef ValueConstIterator const_iterator;\n  typedef Json::UInt UInt;\n  typedef Json::Int Int;\n#if defined(JSON_HAS_INT64)\n  typedef Json::UInt64 UInt64;\n  typedef Json::Int64 Int64;\n#endif // defined(JSON_HAS_INT64)\n  typedef Json::LargestInt LargestInt;\n  typedef Json::LargestUInt LargestUInt;\n  typedef Json::ArrayIndex ArrayIndex;\n\n  static const Value& null;  ///< We regret this reference to a global instance; prefer the simpler Value().\n  static const Value& nullRef;  ///< just a kludge for binary-compatibility; same as null\n  /// Minimum signed integer value that can be stored in a Json::Value.\n  static const LargestInt minLargestInt;\n  /// Maximum signed integer value that can be stored in a Json::Value.\n  static const LargestInt maxLargestInt;\n  /// Maximum unsigned integer value that can be stored in a Json::Value.\n  static const LargestUInt maxLargestUInt;\n\n  /// Minimum signed int value that can be stored in a Json::Value.\n  static const Int minInt;\n  /// Maximum signed int value that can be stored in a Json::Value.\n  static const Int maxInt;\n  /// Maximum unsigned int value that can be stored in a Json::Value.\n  static const UInt maxUInt;\n\n#if defined(JSON_HAS_INT64)\n  /// Minimum signed 64 bits int value that can be stored in a Json::Value.\n  static const Int64 minInt64;\n  /// Maximum signed 64 bits int value that can be stored in a Json::Value.\n  static const Int64 maxInt64;\n  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.\n  static const UInt64 maxUInt64;\n#endif // defined(JSON_HAS_INT64)\n\nprivate:\n#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION\n  class CZString {\n  public:\n    enum DuplicationPolicy {\n      noDuplication = 0,\n      duplicate,\n      duplicateOnCopy\n    };\n    CZString(ArrayIndex index);\n    CZString(char const* str, unsigned length, DuplicationPolicy allocate);\n    CZString(CZString const& other);\n    ~CZString();\n    CZString& operator=(CZString other);\n    bool operator<(CZString const& other) const;\n    bool operator==(CZString const& other) const;\n    ArrayIndex index() const;\n    //const char* c_str() const; ///< \\deprecated\n    char const* data() const;\n    unsigned length() const;\n    bool isStaticString() const;\n\n  private:\n    void swap(CZString& other);\n\n    struct StringStorage {\n      DuplicationPolicy policy_: 2;\n      unsigned length_: 30; // 1GB max\n    };\n\n    char const* cstr_;  // actually, a prefixed string, unless policy is noDup\n    union {\n      ArrayIndex index_;\n      StringStorage storage_;\n    };\n  };\n\npublic:\n#ifndef JSON_USE_CPPTL_SMALLMAP\n  typedef std::map<CZString, Value> ObjectValues;\n#else\n  typedef CppTL::SmallMap<CZString, Value> ObjectValues;\n#endif // ifndef JSON_USE_CPPTL_SMALLMAP\n#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION\n\npublic:\n  /** \\brief Create a default Value of the given type.\n\n    This is a very useful constructor.\n    To create an empty array, pass arrayValue.\n    To create an empty object, pass objectValue.\n    Another Value can then be set to this one by assignment.\nThis is useful since clear() and resize() will not alter types.\n\n    Examples:\n\\code\nJson::Value null_value; // null\nJson::Value arr_value(Json::arrayValue); // []\nJson::Value obj_value(Json::objectValue); // {}\n\\endcode\n  */\n  Value(ValueType type = nullValue);\n  Value(Int value);\n  Value(UInt value);\n#if defined(JSON_HAS_INT64)\n  Value(Int64 value);\n  Value(UInt64 value);\n#endif // if defined(JSON_HAS_INT64)\n  Value(double value);\n  Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)\n  Value(const char* beginValue, const char* endValue); ///< Copy all, incl zeroes.\n  /** \\brief Constructs a value from a static string.\n\n   * Like other value string constructor but do not duplicate the string for\n   * internal storage. The given string must remain alive after the call to this\n   * constructor.\n   * \\note This works only for null-terminated strings. (We cannot change the\n   *   size of this class, so we have nowhere to store the length,\n   *   which might be computed later for various operations.)\n   *\n   * Example of usage:\n   * \\code\n   * static StaticString foo(\"some text\");\n   * Json::Value aValue(foo);\n   * \\endcode\n   */\n  Value(const StaticString& value);\n  Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too.\n#ifdef JSON_USE_CPPTL\n  Value(const CppTL::ConstString& value);\n#endif\n  Value(bool value);\n  /// Deep copy.\n  Value(const Value& other);\n  ~Value();\n\n  /// Deep copy, then swap(other).\n  /// \\note Over-write existing comments. To preserve comments, use #swapPayload().\n  Value& operator=(Value other);\n  /// Swap everything.\n  void swap(Value& other);\n  /// Swap values but leave comments and target offsets in place.\n  void swapPayload(Value& other);\n\n  ValueType type() const;\n\n  /// Compare payload only, not comments etc.\n  bool operator<(const Value& other) const;\n  bool operator<=(const Value& other) const;\n  bool operator>=(const Value& other) const;\n  bool operator>(const Value& other) const;\n  bool operator==(const Value& other) const;\n  bool operator!=(const Value& other) const;\n  int compare(const Value& other) const;\n\n  const char* asCString() const; ///< Embedded zeroes could cause you trouble!\n  std::string asString() const; ///< Embedded zeroes are possible.\n  /** Get raw char* of string-value.\n   *  \\return false if !string. (Seg-fault if str or end are NULL.)\n   */\n  bool getString(\n      char const** str, char const** end) const;\n#ifdef JSON_USE_CPPTL\n  CppTL::ConstString asConstString() const;\n#endif\n  Int asInt() const;\n  UInt asUInt() const;\n#if defined(JSON_HAS_INT64)\n  Int64 asInt64() const;\n  UInt64 asUInt64() const;\n#endif // if defined(JSON_HAS_INT64)\n  LargestInt asLargestInt() const;\n  LargestUInt asLargestUInt() const;\n  float asFloat() const;\n  double asDouble() const;\n  bool asBool() const;\n\n  bool isNull() const;\n  bool isBool() const;\n  bool isInt() const;\n  bool isInt64() const;\n  bool isUInt() const;\n  bool isUInt64() const;\n  bool isIntegral() const;\n  bool isDouble() const;\n  bool isNumeric() const;\n  bool isString() const;\n  bool isArray() const;\n  bool isObject() const;\n\n  bool isConvertibleTo(ValueType other) const;\n\n  /// Number of values in array or object\n  ArrayIndex size() const;\n\n  /// \\brief Return true if empty array, empty object, or null;\n  /// otherwise, false.\n  bool empty() const;\n\n  /// Return isNull()\n  bool operator!() const;\n\n  /// Remove all object members and array elements.\n  /// \\pre type() is arrayValue, objectValue, or nullValue\n  /// \\post type() is unchanged\n  void clear();\n\n  /// Resize the array to size elements.\n  /// New elements are initialized to null.\n  /// May only be called on nullValue or arrayValue.\n  /// \\pre type() is arrayValue or nullValue\n  /// \\post type() is arrayValue\n  void resize(ArrayIndex size);\n\n  /// Access an array element (zero based index ).\n  /// If the array contains less than index element, then null value are\n  /// inserted\n  /// in the array so that its size is index+1.\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  Value& operator[](ArrayIndex index);\n\n  /// Access an array element (zero based index ).\n  /// If the array contains less than index element, then null value are\n  /// inserted\n  /// in the array so that its size is index+1.\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  Value& operator[](int index);\n\n  /// Access an array element (zero based index )\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  const Value& operator[](ArrayIndex index) const;\n\n  /// Access an array element (zero based index )\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  const Value& operator[](int index) const;\n\n  /// If the array contains at least index+1 elements, returns the element\n  /// value,\n  /// otherwise returns defaultValue.\n  Value get(ArrayIndex index, const Value& defaultValue) const;\n  /// Return true if index < size().\n  bool isValidIndex(ArrayIndex index) const;\n  /// \\brief Append value to array at the end.\n  ///\n  /// Equivalent to jsonvalue[jsonvalue.size()] = value;\n  Value& append(const Value& value);\n\n  /// Access an object value by name, create a null member if it does not exist.\n  /// \\note Because of our implementation, keys are limited to 2^30 -1 chars.\n  ///  Exceeding that will cause an exception.\n  Value& operator[](const char* key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  const Value& operator[](const char* key) const;\n  /// Access an object value by name, create a null member if it does not exist.\n  /// \\param key may contain embedded nulls.\n  Value& operator[](const std::string& key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  /// \\param key may contain embedded nulls.\n  const Value& operator[](const std::string& key) const;\n  /** \\brief Access an object value by name, create a null member if it does not\n   exist.\n\n   * If the object has no entry for that name, then the member name used to store\n   * the new entry is not duplicated.\n   * Example of use:\n   * \\code\n   * Json::Value object;\n   * static const StaticString code(\"code\");\n   * object[code] = 1234;\n   * \\endcode\n   */\n  Value& operator[](const StaticString& key);\n#ifdef JSON_USE_CPPTL\n  /// Access an object value by name, create a null member if it does not exist.\n  Value& operator[](const CppTL::ConstString& key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  const Value& operator[](const CppTL::ConstString& key) const;\n#endif\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  Value get(const char* key, const Value& defaultValue) const;\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  /// \\param key may contain embedded nulls.\n  Value get(const char* key, const char* end, const Value& defaultValue) const;\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  /// \\param key may contain embedded nulls.\n  Value get(const std::string& key, const Value& defaultValue) const;\n#ifdef JSON_USE_CPPTL\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;\n#endif\n  /// Most general and efficient version of isMember()const, get()const,\n  /// and operator[]const\n  /// \\note As stated elsewhere, behavior is undefined if (end-key) >= 2^30\n  Value const* find(char const* key, char const* end) const;\n  /// Most general and efficient version of object-mutators.\n  /// \\note As stated elsewhere, behavior is undefined if (end-key) >= 2^30\n  /// \\return non-zero, but JSON_ASSERT if this is neither object nor nullValue.\n  Value const* demand(char const* key, char const* end);\n  /// \\brief Remove and return the named member.\n  ///\n  /// Do nothing if it did not exist.\n  /// \\return the removed Value, or null.\n  /// \\pre type() is objectValue or nullValue\n  /// \\post type() is unchanged\n  /// \\deprecated\n  Value removeMember(const char* key);\n  /// Same as removeMember(const char*)\n  /// \\param key may contain embedded nulls.\n  /// \\deprecated\n  Value removeMember(const std::string& key);\n  /// Same as removeMember(const char* key, const char* end, Value* removed),\n  /// but 'key' is null-terminated.\n  bool removeMember(const char* key, Value* removed);\n  /** \\brief Remove the named map member.\n\n      Update 'removed' iff removed.\n      \\param key may contain embedded nulls.\n      \\return true iff removed (no exceptions)\n  */\n  bool removeMember(std::string const& key, Value* removed);\n  /// Same as removeMember(std::string const& key, Value* removed)\n  bool removeMember(const char* key, const char* end, Value* removed);\n  /** \\brief Remove the indexed array element.\n\n      O(n) expensive operations.\n      Update 'removed' iff removed.\n      \\return true iff removed (no exceptions)\n  */\n  bool removeIndex(ArrayIndex i, Value* removed);\n\n  /// Return true if the object has a member named key.\n  /// \\note 'key' must be null-terminated.\n  bool isMember(const char* key) const;\n  /// Return true if the object has a member named key.\n  /// \\param key may contain embedded nulls.\n  bool isMember(const std::string& key) const;\n  /// Same as isMember(std::string const& key)const\n  bool isMember(const char* key, const char* end) const;\n#ifdef JSON_USE_CPPTL\n  /// Return true if the object has a member named key.\n  bool isMember(const CppTL::ConstString& key) const;\n#endif\n\n  /// \\brief Return a list of the member names.\n  ///\n  /// If null, return an empty list.\n  /// \\pre type() is objectValue or nullValue\n  /// \\post if type() was nullValue, it remains nullValue\n  Members getMemberNames() const;\n\n  //# ifdef JSON_USE_CPPTL\n  //      EnumMemberNames enumMemberNames() const;\n  //      EnumValues enumValues() const;\n  //# endif\n\n  /// \\deprecated Always pass len.\n  void setComment(const char* comment, CommentPlacement placement);\n  /// Comments must be //... or /* ... */\n  void setComment(const char* comment, size_t len, CommentPlacement placement);\n  /// Comments must be //... or /* ... */\n  void setComment(const std::string& comment, CommentPlacement placement);\n  bool hasComment(CommentPlacement placement) const;\n  /// Include delimiters and embedded newlines.\n  std::string getComment(CommentPlacement placement) const;\n\n  std::string toStyledString() const;\n\n  const_iterator begin() const;\n  const_iterator end() const;\n\n  iterator begin();\n  iterator end();\n\n  // Accessors for the [start, limit) range of bytes within the JSON text from\n  // which this value was parsed, if any.\n  void setOffsetStart(size_t start);\n  void setOffsetLimit(size_t limit);\n  size_t getOffsetStart() const;\n  size_t getOffsetLimit() const;\n\nprivate:\n  void initBasic(ValueType type, bool allocated = false);\n\n  Value& resolveReference(const char* key);\n  Value& resolveReference(const char* key, const char* end);\n\n  struct CommentInfo {\n    CommentInfo();\n    ~CommentInfo();\n\n    void setComment(const char* text, size_t len);\n\n    char* comment_;\n  };\n\n  // struct MemberNamesTransform\n  //{\n  //   typedef const char *result_type;\n  //   const char *operator()( const CZString &name ) const\n  //   {\n  //      return name.c_str();\n  //   }\n  //};\n\n  union ValueHolder {\n    LargestInt int_;\n    LargestUInt uint_;\n    double real_;\n    bool bool_;\n    char* string_;  // actually ptr to unsigned, followed by str, unless !allocated_\n    ObjectValues* map_;\n  } value_;\n  ValueType type_ : 8;\n  unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.\n                               // If not allocated_, string_ must be null-terminated.\n  CommentInfo* comments_;\n\n  // [start, limit) byte offsets in the target JSON text from which this Value\n  // was extracted.\n  size_t start_;\n  size_t limit_;\n};\n\n/** \\brief Experimental and untested: represents an element of the \"path\" to\n * access a node.\n */\nclass JSON_API PathArgument {\npublic:\n  friend class Path;\n\n  PathArgument();\n  PathArgument(ArrayIndex index);\n  PathArgument(const char* key);\n  PathArgument(const std::string& key);\n\nprivate:\n  enum Kind {\n    kindNone = 0,\n    kindIndex,\n    kindKey\n  };\n  std::string key_;\n  ArrayIndex index_;\n  Kind kind_;\n};\n\n/** \\brief Experimental and untested: represents a \"path\" to access a node.\n *\n * Syntax:\n * - \".\" => root node\n * - \".[n]\" => elements at index 'n' of root node (an array value)\n * - \".name\" => member named 'name' of root node (an object value)\n * - \".name1.name2.name3\"\n * - \".[0][1][2].name1[3]\"\n * - \".%\" => member name is provided as parameter\n * - \".[%]\" => index is provied as parameter\n */\nclass JSON_API Path {\npublic:\n  Path(const std::string& path,\n       const PathArgument& a1 = PathArgument(),\n       const PathArgument& a2 = PathArgument(),\n       const PathArgument& a3 = PathArgument(),\n       const PathArgument& a4 = PathArgument(),\n       const PathArgument& a5 = PathArgument());\n\n  const Value& resolve(const Value& root) const;\n  Value resolve(const Value& root, const Value& defaultValue) const;\n  /// Creates the \"path\" to access the specified node and returns a reference on\n  /// the node.\n  Value& make(Value& root) const;\n\nprivate:\n  typedef std::vector<const PathArgument*> InArgs;\n  typedef std::vector<PathArgument> Args;\n\n  void makePath(const std::string& path, const InArgs& in);\n  void addPathInArg(const std::string& path,\n                    const InArgs& in,\n                    InArgs::const_iterator& itInArg,\n                    PathArgument::Kind kind);\n  void invalidPath(const std::string& path, int location);\n\n  Args args_;\n};\n\n/** \\brief base class for Value iterators.\n *\n */\nclass JSON_API ValueIteratorBase {\npublic:\n  typedef std::bidirectional_iterator_tag iterator_category;\n  typedef unsigned int size_t;\n  typedef int difference_type;\n  typedef ValueIteratorBase SelfType;\n\n  ValueIteratorBase();\n  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);\n\n  bool operator==(const SelfType& other) const { return isEqual(other); }\n\n  bool operator!=(const SelfType& other) const { return !isEqual(other); }\n\n  difference_type operator-(const SelfType& other) const {\n    return other.computeDistance(*this);\n  }\n\n  /// Return either the index or the member name of the referenced value as a\n  /// Value.\n  Value key() const;\n\n  /// Return the index of the referenced Value, or -1 if it is not an arrayValue.\n  UInt index() const;\n\n  /// Return the member name of the referenced Value, or \"\" if it is not an\n  /// objectValue.\n  /// \\note Avoid `c_str()` on result, as embedded zeroes are possible.\n  std::string name() const;\n\n  /// Return the member name of the referenced Value. \"\" if it is not an\n  /// objectValue.\n  /// \\deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.\n  JSONCPP_DEPRECATED(\"Use `key = name();` instead.\")\n  char const* memberName() const;\n  /// Return the member name of the referenced Value, or NULL if it is not an\n  /// objectValue.\n  /// \\note Better version than memberName(). Allows embedded nulls.\n  char const* memberName(char const** end) const;\n\nprotected:\n  Value& deref() const;\n\n  void increment();\n\n  void decrement();\n\n  difference_type computeDistance(const SelfType& other) const;\n\n  bool isEqual(const SelfType& other) const;\n\n  void copy(const SelfType& other);\n\nprivate:\n  Value::ObjectValues::iterator current_;\n  // Indicates that iterator is for a null value.\n  bool isNull_;\n};\n\n/** \\brief const iterator for object and array value.\n *\n */\nclass JSON_API ValueConstIterator : public ValueIteratorBase {\n  friend class Value;\n\npublic:\n  typedef const Value value_type;\n  //typedef unsigned int size_t;\n  //typedef int difference_type;\n  typedef const Value& reference;\n  typedef const Value* pointer;\n  typedef ValueConstIterator SelfType;\n\n  ValueConstIterator();\n\nprivate:\n/*! \\internal Use by Value to create an iterator.\n */\n  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);\npublic:\n  SelfType& operator=(const ValueIteratorBase& other);\n\n  SelfType operator++(int) {\n    SelfType temp(*this);\n    ++*this;\n    return temp;\n  }\n\n  SelfType operator--(int) {\n    SelfType temp(*this);\n    --*this;\n    return temp;\n  }\n\n  SelfType& operator--() {\n    decrement();\n    return *this;\n  }\n\n  SelfType& operator++() {\n    increment();\n    return *this;\n  }\n\n  reference operator*() const { return deref(); }\n\n  pointer operator->() const { return &deref(); }\n};\n\n/** \\brief Iterator for object and array value.\n */\nclass JSON_API ValueIterator : public ValueIteratorBase {\n  friend class Value;\n\npublic:\n  typedef Value value_type;\n  typedef unsigned int size_t;\n  typedef int difference_type;\n  typedef Value& reference;\n  typedef Value* pointer;\n  typedef ValueIterator SelfType;\n\n  ValueIterator();\n  ValueIterator(const ValueConstIterator& other);\n  ValueIterator(const ValueIterator& other);\n\nprivate:\n/*! \\internal Use by Value to create an iterator.\n */\n  explicit ValueIterator(const Value::ObjectValues::iterator& current);\npublic:\n  SelfType& operator=(const SelfType& other);\n\n  SelfType operator++(int) {\n    SelfType temp(*this);\n    ++*this;\n    return temp;\n  }\n\n  SelfType operator--(int) {\n    SelfType temp(*this);\n    --*this;\n    return temp;\n  }\n\n  SelfType& operator--() {\n    decrement();\n    return *this;\n  }\n\n  SelfType& operator++() {\n    increment();\n    return *this;\n  }\n\n  reference operator*() const { return deref(); }\n\n  pointer operator->() const { return &deref(); }\n};\n\n} // namespace Json\n\n\nnamespace std {\n/// Specialize std::swap() for Json::Value.\ntemplate<>\ninline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }\n}\n\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif // CPPTL_JSON_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/value.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/reader.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_READER_H_INCLUDED\n#define CPPTL_JSON_READER_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"features.h\"\n#include \"value.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <deque>\n#include <iosfwd>\n#include <stack>\n#include <string>\n#include <istream>\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\nnamespace Json {\n\n/** \\brief Unserialize a <a HREF=\"http://www.json.org\">JSON</a> document into a\n *Value.\n *\n * \\deprecated Use CharReader and CharReaderBuilder.\n */\nclass JSON_API Reader {\npublic:\n  typedef char Char;\n  typedef const Char* Location;\n\n  /** \\brief An error tagged with where in the JSON text it was encountered.\n   *\n   * The offsets give the [start, limit) range of bytes within the text. Note\n   * that this is bytes, not codepoints.\n   *\n   */\n  struct StructuredError {\n    size_t offset_start;\n    size_t offset_limit;\n    std::string message;\n  };\n\n  /** \\brief Constructs a Reader allowing all features\n   * for parsing.\n   */\n  Reader();\n\n  /** \\brief Constructs a Reader allowing the specified feature set\n   * for parsing.\n   */\n  Reader(const Features& features);\n\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   * document.\n   * \\param document UTF-8 encoded string containing the document to read.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param collectComments \\c true to collect comment and allow writing them\n   * back during\n   *                        serialization, \\c false to discard comments.\n   *                        This parameter is ignored if\n   * Features::allowComments_\n   *                        is \\c false.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   * error occurred.\n   */\n  bool\n  parse(const std::string& document, Value& root, bool collectComments = true);\n\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   document.\n   * \\param beginDoc Pointer on the beginning of the UTF-8 encoded string of the\n   document to read.\n   * \\param endDoc Pointer on the end of the UTF-8 encoded string of the\n   document to read.\n   *               Must be >= beginDoc.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param collectComments \\c true to collect comment and allow writing them\n   back during\n   *                        serialization, \\c false to discard comments.\n   *                        This parameter is ignored if\n   Features::allowComments_\n   *                        is \\c false.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   error occurred.\n   */\n  bool parse(const char* beginDoc,\n             const char* endDoc,\n             Value& root,\n             bool collectComments = true);\n\n  /// \\brief Parse from input stream.\n  /// \\see Json::operator>>(std::istream&, Json::Value&).\n  bool parse(std::istream& is, Value& root, bool collectComments = true);\n\n  /** \\brief Returns a user friendly string that list errors in the parsed\n   * document.\n   * \\return Formatted error message with the list of errors with their location\n   * in\n   *         the parsed document. An empty string is returned if no error\n   * occurred\n   *         during parsing.\n   * \\deprecated Use getFormattedErrorMessages() instead (typo fix).\n   */\n  JSONCPP_DEPRECATED(\"Use getFormattedErrorMessages() instead.\")\n  std::string getFormatedErrorMessages() const;\n\n  /** \\brief Returns a user friendly string that list errors in the parsed\n   * document.\n   * \\return Formatted error message with the list of errors with their location\n   * in\n   *         the parsed document. An empty string is returned if no error\n   * occurred\n   *         during parsing.\n   */\n  std::string getFormattedErrorMessages() const;\n\n  /** \\brief Returns a vector of structured erros encounted while parsing.\n   * \\return A (possibly empty) vector of StructuredError objects. Currently\n   *         only one error can be returned, but the caller should tolerate\n   * multiple\n   *         errors.  This can occur if the parser recovers from a non-fatal\n   *         parse error and then encounters additional errors.\n   */\n  std::vector<StructuredError> getStructuredErrors() const;\n\n  /** \\brief Add a semantic error message.\n   * \\param value JSON Value location associated with the error\n   * \\param message The error message.\n   * \\return \\c true if the error was successfully added, \\c false if the\n   * Value offset exceeds the document size.\n   */\n  bool pushError(const Value& value, const std::string& message);\n\n  /** \\brief Add a semantic error message with extra context.\n   * \\param value JSON Value location associated with the error\n   * \\param message The error message.\n   * \\param extra Additional JSON Value location to contextualize the error\n   * \\return \\c true if the error was successfully added, \\c false if either\n   * Value offset exceeds the document size.\n   */\n  bool pushError(const Value& value, const std::string& message, const Value& extra);\n\n  /** \\brief Return whether there are any errors.\n   * \\return \\c true if there are no errors to report \\c false if\n   * errors have occurred.\n   */\n  bool good() const;\n\nprivate:\n  enum TokenType {\n    tokenEndOfStream = 0,\n    tokenObjectBegin,\n    tokenObjectEnd,\n    tokenArrayBegin,\n    tokenArrayEnd,\n    tokenString,\n    tokenNumber,\n    tokenTrue,\n    tokenFalse,\n    tokenNull,\n    tokenArraySeparator,\n    tokenMemberSeparator,\n    tokenComment,\n    tokenError\n  };\n\n  class Token {\n  public:\n    TokenType type_;\n    Location start_;\n    Location end_;\n  };\n\n  class ErrorInfo {\n  public:\n    Token token_;\n    std::string message_;\n    Location extra_;\n  };\n\n  typedef std::deque<ErrorInfo> Errors;\n\n  bool readToken(Token& token);\n  void skipSpaces();\n  bool match(Location pattern, int patternLength);\n  bool readComment();\n  bool readCStyleComment();\n  bool readCppStyleComment();\n  bool readString();\n  void readNumber();\n  bool readValue();\n  bool readObject(Token& token);\n  bool readArray(Token& token);\n  bool decodeNumber(Token& token);\n  bool decodeNumber(Token& token, Value& decoded);\n  bool decodeString(Token& token);\n  bool decodeString(Token& token, std::string& decoded);\n  bool decodeDouble(Token& token);\n  bool decodeDouble(Token& token, Value& decoded);\n  bool decodeUnicodeCodePoint(Token& token,\n                              Location& current,\n                              Location end,\n                              unsigned int& unicode);\n  bool decodeUnicodeEscapeSequence(Token& token,\n                                   Location& current,\n                                   Location end,\n                                   unsigned int& unicode);\n  bool addError(const std::string& message, Token& token, Location extra = 0);\n  bool recoverFromError(TokenType skipUntilToken);\n  bool addErrorAndRecover(const std::string& message,\n                          Token& token,\n                          TokenType skipUntilToken);\n  void skipUntilSpace();\n  Value& currentValue();\n  Char getNextChar();\n  void\n  getLocationLineAndColumn(Location location, int& line, int& column) const;\n  std::string getLocationLineAndColumn(Location location) const;\n  void addComment(Location begin, Location end, CommentPlacement placement);\n  void skipCommentTokens(Token& token);\n\n  typedef std::stack<Value*> Nodes;\n  Nodes nodes_;\n  Errors errors_;\n  std::string document_;\n  Location begin_;\n  Location end_;\n  Location current_;\n  Location lastValueEnd_;\n  Value* lastValue_;\n  std::string commentsBefore_;\n  Features features_;\n  bool collectComments_;\n};  // Reader\n\n/** Interface for reading JSON from a char array.\n */\nclass JSON_API CharReader {\npublic:\n  virtual ~CharReader() {}\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   document.\n   * The document must be a UTF-8 encoded string containing the document to read.\n   *\n   * \\param beginDoc Pointer on the beginning of the UTF-8 encoded string of the\n   document to read.\n   * \\param endDoc Pointer on the end of the UTF-8 encoded string of the\n   document to read.\n   *        Must be >= beginDoc.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param errs [out] Formatted error messages (if not NULL)\n   *        a user friendly string that lists errors in the parsed\n   * document.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   error occurred.\n   */\n  virtual bool parse(\n      char const* beginDoc, char const* endDoc,\n      Value* root, std::string* errs) = 0;\n\n  class Factory {\n  public:\n    virtual ~Factory() {}\n    /** \\brief Allocate a CharReader via operator new().\n     * \\throw std::exception if something goes wrong (e.g. invalid settings)\n     */\n    virtual CharReader* newCharReader() const = 0;\n  };  // Factory\n};  // CharReader\n\n/** \\brief Build a CharReader implementation.\n\nUsage:\n\\code\n  using namespace Json;\n  CharReaderBuilder builder;\n  builder[\"collectComments\"] = false;\n  Value value;\n  std::string errs;\n  bool ok = parseFromStream(builder, std::cin, &value, &errs);\n\\endcode\n*/\nclass JSON_API CharReaderBuilder : public CharReader::Factory {\npublic:\n  // Note: We use a Json::Value so that we can add data-members to this class\n  // without a major version bump.\n  /** Configuration of this builder.\n    These are case-sensitive.\n    Available settings (case-sensitive):\n    - `\"collectComments\": false or true`\n      - true to collect comment and allow writing them\n        back during serialization, false to discard comments.\n        This parameter is ignored if allowComments is false.\n    - `\"allowComments\": false or true`\n      - true if comments are allowed.\n    - `\"strictRoot\": false or true`\n      - true if root must be either an array or an object value\n    - `\"allowDroppedNullPlaceholders\": false or true`\n      - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)\n    - `\"allowNumericKeys\": false or true`\n      - true if numeric object keys are allowed.\n    - `\"allowSingleQuotes\": false or true`\n      - true if '' are allowed for strings (both keys and values)\n    - `\"stackLimit\": integer`\n      - Exceeding stackLimit (recursive depth of `readValue()`) will\n        cause an exception.\n      - This is a security issue (seg-faults caused by deeply nested JSON),\n        so the default is low.\n    - `\"failIfExtra\": false or true`\n      - If true, `parse()` returns false when extra non-whitespace trails\n        the JSON value in the input string.\n    - `\"rejectDupKeys\": false or true`\n      - If true, `parse()` returns false when a key is duplicated within an object.\n\n    You can examine 'settings_` yourself\n    to see the defaults. You can also write and read them just like any\n    JSON Value.\n    \\sa setDefaults()\n    */\n  Json::Value settings_;\n\n  CharReaderBuilder();\n  virtual ~CharReaderBuilder();\n\n  virtual CharReader* newCharReader() const;\n\n  /** \\return true if 'settings' are legal and consistent;\n   *   otherwise, indicate bad settings via 'invalid'.\n   */\n  bool validate(Json::Value* invalid) const;\n\n  /** A simple way to update a specific setting.\n   */\n  Value& operator[](std::string key);\n\n  /** Called by ctor, but you can use this to reset settings_.\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode\n   */\n  static void setDefaults(Json::Value* settings);\n  /** Same as old Features::strictMode().\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults\n   */\n  static void strictMode(Json::Value* settings);\n};\n\n/** Consume entire stream and use its begin/end.\n  * Someday we might have a real StreamReader, but for now this\n  * is convenient.\n  */\nbool JSON_API parseFromStream(\n    CharReader::Factory const&,\n    std::istream&,\n    Value* root, std::string* errs);\n\n/** \\brief Read from 'sin' into 'root'.\n\n Always keep comments from the input JSON.\n\n This can be used to read a file into a particular sub-object.\n For example:\n \\code\n Json::Value root;\n cin >> root[\"dir\"][\"file\"];\n cout << root;\n \\endcode\n Result:\n \\verbatim\n {\n \"dir\": {\n     \"file\": {\n     // The input stream JSON would be nested here.\n     }\n }\n }\n \\endverbatim\n \\throw std::exception on parse error.\n \\see Json::operator<<()\n*/\nJSON_API std::istream& operator>>(std::istream&, Value&);\n\n} // namespace Json\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif // CPPTL_JSON_READER_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/reader.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/writer.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_WRITER_H_INCLUDED\n#define JSON_WRITER_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"value.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n#include <vector>\n#include <string>\n#include <ostream>\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\nnamespace Json {\n\nclass Value;\n\n/**\n\nUsage:\n\\code\n  using namespace Json;\n  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {\n    std::unique_ptr<StreamWriter> const writer(\n      factory.newStreamWriter());\n    writer->write(value, &std::cout);\n    std::cout << std::endl;  // add lf and flush\n  }\n\\endcode\n*/\nclass JSON_API StreamWriter {\nprotected:\n  std::ostream* sout_;  // not owned; will not delete\npublic:\n  StreamWriter();\n  virtual ~StreamWriter();\n  /** Write Value into document as configured in sub-class.\n      Do not take ownership of sout, but maintain a reference during function.\n      \\pre sout != NULL\n      \\return zero on success (For now, we always return zero, so check the stream instead.)\n      \\throw std::exception possibly, depending on configuration\n   */\n  virtual int write(Value const& root, std::ostream* sout) = 0;\n\n  /** \\brief A simple abstract factory.\n   */\n  class JSON_API Factory {\n  public:\n    virtual ~Factory();\n    /** \\brief Allocate a CharReader via operator new().\n     * \\throw std::exception if something goes wrong (e.g. invalid settings)\n     */\n    virtual StreamWriter* newStreamWriter() const = 0;\n  };  // Factory\n};  // StreamWriter\n\n/** \\brief Write into stringstream, then return string, for convenience.\n * A StreamWriter will be created from the factory, used, and then deleted.\n */\nstd::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);\n\n\n/** \\brief Build a StreamWriter implementation.\n\nUsage:\n\\code\n  using namespace Json;\n  Value value = ...;\n  StreamWriterBuilder builder;\n  builder[\"commentStyle\"] = \"None\";\n  builder[\"indentation\"] = \"   \";  // or whatever you like\n  std::unique_ptr<Json::StreamWriter> writer(\n      builder.newStreamWriter());\n  writer->write(value, &std::cout);\n  std::cout << std::endl;  // add lf and flush\n\\endcode\n*/\nclass JSON_API StreamWriterBuilder : public StreamWriter::Factory {\npublic:\n  // Note: We use a Json::Value so that we can add data-members to this class\n  // without a major version bump.\n  /** Configuration of this builder.\n    Available settings (case-sensitive):\n    - \"commentStyle\": \"None\" or \"All\"\n    - \"indentation\":  \"<anything>\"\n    - \"enableYAMLCompatibility\": false or true\n      - slightly change the whitespace around colons\n    - \"dropNullPlaceholders\": false or true\n      - Drop the \"null\" string from the writer's output for nullValues.\n        Strictly speaking, this is not valid JSON. But when the output is being\n        fed to a browser's Javascript, it makes for smaller output and the\n        browser can handle the output just fine.\n\n    You can examine 'settings_` yourself\n    to see the defaults. You can also write and read them just like any\n    JSON Value.\n    \\sa setDefaults()\n    */\n  Json::Value settings_;\n\n  StreamWriterBuilder();\n  virtual ~StreamWriterBuilder();\n\n  /**\n   * \\throw std::exception if something goes wrong (e.g. invalid settings)\n   */\n  virtual StreamWriter* newStreamWriter() const;\n\n  /** \\return true if 'settings' are legal and consistent;\n   *   otherwise, indicate bad settings via 'invalid'.\n   */\n  bool validate(Json::Value* invalid) const;\n  /** A simple way to update a specific setting.\n   */\n  Value& operator[](std::string key);\n\n  /** Called by ctor, but you can use this to reset settings_.\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults\n   */\n  static void setDefaults(Json::Value* settings);\n};\n\n/** \\brief Abstract class for writers.\n * \\deprecated Use StreamWriter. (And really, this is an implementation detail.)\n */\nclass JSON_API Writer {\npublic:\n  virtual ~Writer();\n\n  virtual std::string write(const Value& root) = 0;\n};\n\n/** \\brief Outputs a Value in <a HREF=\"http://www.json.org\">JSON</a> format\n *without formatting (not human friendly).\n *\n * The JSON document is written in a single line. It is not intended for 'human'\n *consumption,\n * but may be usefull to support feature such as RPC where bandwith is limited.\n * \\sa Reader, Value\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API FastWriter : public Writer {\n\npublic:\n  FastWriter();\n  virtual ~FastWriter() {}\n\n  void enableYAMLCompatibility();\n\n  /** \\brief Drop the \"null\" string from the writer's output for nullValues.\n   * Strictly speaking, this is not valid JSON. But when the output is being\n   * fed to a browser's Javascript, it makes for smaller output and the\n   * browser can handle the output just fine.\n   */\n  void dropNullPlaceholders();\n\n  void omitEndingLineFeed();\n\npublic: // overridden from Writer\n  virtual std::string write(const Value& root);\n\nprivate:\n  void writeValue(const Value& value);\n\n  std::string document_;\n  bool yamlCompatiblityEnabled_;\n  bool dropNullPlaceholders_;\n  bool omitEndingLineFeed_;\n};\n\n/** \\brief Writes a Value in <a HREF=\"http://www.json.org\">JSON</a> format in a\n *human friendly way.\n *\n * The rules for line break and indent are as follow:\n * - Object value:\n *     - if empty then print {} without indent and line break\n *     - if not empty the print '{', line break & indent, print one value per\n *line\n *       and then unindent and line break and print '}'.\n * - Array value:\n *     - if empty then print [] without indent and line break\n *     - if the array contains no object value, empty array or some other value\n *types,\n *       and all the values fit on one lines, then print the array on a single\n *line.\n *     - otherwise, it the values do not fit on one line, or the array contains\n *       object or non empty array, then print one value per line.\n *\n * If the Value have comments then they are outputed according to their\n *#CommentPlacement.\n *\n * \\sa Reader, Value, Value::setComment()\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API StyledWriter : public Writer {\npublic:\n  StyledWriter();\n  virtual ~StyledWriter() {}\n\npublic: // overridden from Writer\n  /** \\brief Serialize a Value in <a HREF=\"http://www.json.org\">JSON</a> format.\n   * \\param root Value to serialize.\n   * \\return String containing the JSON document that represents the root value.\n   */\n  virtual std::string write(const Value& root);\n\nprivate:\n  void writeValue(const Value& value);\n  void writeArrayValue(const Value& value);\n  bool isMultineArray(const Value& value);\n  void pushValue(const std::string& value);\n  void writeIndent();\n  void writeWithIndent(const std::string& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(const Value& root);\n  void writeCommentAfterValueOnSameLine(const Value& root);\n  bool hasCommentForValue(const Value& value);\n  static std::string normalizeEOL(const std::string& text);\n\n  typedef std::vector<std::string> ChildValues;\n\n  ChildValues childValues_;\n  std::string document_;\n  std::string indentString_;\n  int rightMargin_;\n  int indentSize_;\n  bool addChildValues_;\n};\n\n/** \\brief Writes a Value in <a HREF=\"http://www.json.org\">JSON</a> format in a\n human friendly way,\n     to a stream rather than to a string.\n *\n * The rules for line break and indent are as follow:\n * - Object value:\n *     - if empty then print {} without indent and line break\n *     - if not empty the print '{', line break & indent, print one value per\n line\n *       and then unindent and line break and print '}'.\n * - Array value:\n *     - if empty then print [] without indent and line break\n *     - if the array contains no object value, empty array or some other value\n types,\n *       and all the values fit on one lines, then print the array on a single\n line.\n *     - otherwise, it the values do not fit on one line, or the array contains\n *       object or non empty array, then print one value per line.\n *\n * If the Value have comments then they are outputed according to their\n #CommentPlacement.\n *\n * \\param indentation Each level will be indented by this amount extra.\n * \\sa Reader, Value, Value::setComment()\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API StyledStreamWriter {\npublic:\n  StyledStreamWriter(std::string indentation = \"\\t\");\n  ~StyledStreamWriter() {}\n\npublic:\n  /** \\brief Serialize a Value in <a HREF=\"http://www.json.org\">JSON</a> format.\n   * \\param out Stream to write to. (Can be ostringstream, e.g.)\n   * \\param root Value to serialize.\n   * \\note There is no point in deriving from Writer, since write() should not\n   * return a value.\n   */\n  void write(std::ostream& out, const Value& root);\n\nprivate:\n  void writeValue(const Value& value);\n  void writeArrayValue(const Value& value);\n  bool isMultineArray(const Value& value);\n  void pushValue(const std::string& value);\n  void writeIndent();\n  void writeWithIndent(const std::string& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(const Value& root);\n  void writeCommentAfterValueOnSameLine(const Value& root);\n  bool hasCommentForValue(const Value& value);\n  static std::string normalizeEOL(const std::string& text);\n\n  typedef std::vector<std::string> ChildValues;\n\n  ChildValues childValues_;\n  std::ostream* document_;\n  std::string indentString_;\n  int rightMargin_;\n  std::string indentation_;\n  bool addChildValues_ : 1;\n  bool indented_ : 1;\n};\n\n#if defined(JSON_HAS_INT64)\nstd::string JSON_API valueToString(Int value);\nstd::string JSON_API valueToString(UInt value);\n#endif // if defined(JSON_HAS_INT64)\nstd::string JSON_API valueToString(LargestInt value);\nstd::string JSON_API valueToString(LargestUInt value);\nstd::string JSON_API valueToString(double value);\nstd::string JSON_API valueToString(bool value);\nstd::string JSON_API valueToQuotedString(const char* value);\n\n/// \\brief Output using the StyledStreamWriter.\n/// \\see Json::operator>>()\nJSON_API std::ostream& operator<<(std::ostream&, const Value& root);\n\n} // namespace Json\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif // JSON_WRITER_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/writer.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/assertions.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED\n#define CPPTL_JSON_ASSERTIONS_H_INCLUDED\n\n#include <stdlib.h>\n#include <sstream>\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"config.h\"\n#endif // if !defined(JSON_IS_AMALGAMATION)\n\n/** It should not be possible for a maliciously designed file to\n *  cause an abort() or seg-fault, so these macros are used only\n *  for pre-condition violations and internal logic errors.\n */\n#if JSON_USE_EXCEPTION\n\n// @todo <= add detail about condition in exception\n# define JSON_ASSERT(condition)                                                \\\n  {if (!(condition)) {Json::throwLogicError( \"assert json failed\" );}}\n\n# define JSON_FAIL_MESSAGE(message)                                            \\\n  {                                                                            \\\n    std::ostringstream oss; oss << message;                                    \\\n    Json::throwLogicError(oss.str());                                          \\\n    abort();                                                                   \\\n  }\n\n#else // JSON_USE_EXCEPTION\n\n# define JSON_ASSERT(condition) assert(condition)\n\n// The call to assert() will show the failure message in debug builds. In\n// release builds we abort, for a core-dump or debugger.\n# define JSON_FAIL_MESSAGE(message)                                            \\\n  {                                                                            \\\n    std::ostringstream oss; oss << message;                                    \\\n    assert(false && oss.str().c_str());                                        \\\n    abort();                                                                   \\\n  }\n\n\n#endif\n\n#define JSON_ASSERT_MESSAGE(condition, message)                                \\\n  if (!(condition)) {                                                          \\\n    JSON_FAIL_MESSAGE(message);                                                \\\n  }\n\n#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/assertions.h\n// //////////////////////////////////////////////////////////////////////\n\n\n\n\n\n#endif //ifndef JSON_AMALGATED_H_INCLUDED\n"
  },
  {
    "path": "src/common/logger.cpp",
    "content": "#include \"logger.h\"\n\n#include <cstdarg>\n#include <cstring>\n#include <ctime>\n#include <unistd.h>\n#include <linux/un.h>\n#include <sys/socket.h>\n#include <vector>\n#include \"common/types.h\"\n\n\nnamespace Airwave {\n\n\nstatic int fd = -1;\nstatic std::string id;\nstatic std::vector<char> buffer;\nstatic LogLevel defaultLevel = LogLevel::kDebug;\n\n\nbool loggerInit(const std::string& socketPath, const std::string& senderId)\n{\n\tfd = socket(AF_UNIX, SOCK_DGRAM, 0);\n\tif(fd < 0)\n\t\treturn false;\n\n\tsockaddr_un address;\n\tmemset(&address, 0, sizeof(sockaddr_un));\n\taddress.sun_family = AF_UNIX;\n\tsnprintf(address.sun_path, UNIX_PATH_MAX, socketPath.c_str());\n\n\tif(connect(fd, reinterpret_cast<sockaddr*>(&address),\n\t\t\tsizeof(sockaddr_un)) != 0) {\n\t\tclose(fd);\n\t\tfd = -1;\n\t\treturn false;\n\t}\n\n\tid = senderId;\n\n\t// NOTE The buffer size is hardcoded, increase it if necessary.\n\tbuffer.resize(1024);\n\treturn true;\n}\n\n\nvoid loggerFree()\n{\n\tif(fd >= 0) {\n\t\tclose(fd);\n\t\tid.clear();\n\t}\n}\n\n\nLogLevel loggerLogLevel()\n{\n\treturn defaultLevel;\n}\n\n\nvoid loggerSetLogLevel(LogLevel level)\n{\n\tdefaultLevel = level;\n}\n\n\nstd::string loggerSenderId()\n{\n\treturn id;\n}\n\n\nvoid loggerSetSenderId(const std::string& senderId)\n{\n\tid = senderId;\n}\n\n\nvoid loggerMessage(LogLevel level, const char* format, ...)\n{\n\tif(level > defaultLevel)\n\t\treturn;\n\n\tif(fd == -1)\n\t\treturn;\n\n\ttimespec tm;\n\tclock_gettime(CLOCK_REALTIME, &tm);\n\n\tu64* timestamp = reinterpret_cast<u64*>(buffer.data());\n\t*timestamp = (static_cast<u64>(tm.tv_sec) << 32) + tm.tv_nsec;\n\n\tchar* output = buffer.data() + sizeof(u64);\n\toutput = std::copy(id.begin(), id.end(), output);\n\t*output = '\\x01';\n\t++output;\n\n\tuint count = output - buffer.data();\n\n\tva_list args;\n\tva_start(args, format);\n\n\tcount += std::vsnprintf(output, buffer.size() - count, format, args) + 1;\n\n\tva_end(args);\n\n\tsend(fd, buffer.data(), count, 0);\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/common/logger.h",
    "content": "#ifndef COMMON_LOGGER_H\n#define COMMON_LOGGER_H\n\n#include <string>\n\n\n#define FLOOD(format, ...) \\\n\t\tAirwave::loggerMessage(Airwave::LogLevel::kFlood, format, ##__VA_ARGS__)\n\n#define DEBUG(format, ...) \\\n\t\tAirwave::loggerMessage(Airwave::LogLevel::kDebug, format, ##__VA_ARGS__)\n\n#define TRACE(format, ...) \\\n\t\tAirwave::loggerMessage(Airwave::LogLevel::kTrace, format, ##__VA_ARGS__)\n\n// Workaround for wingdi.h\n#ifdef ERROR\n#undef ERROR\n#endif\n\n#define ERROR(format, ...) \\\n\t\tAirwave::loggerMessage(Airwave::LogLevel::kError, format, ##__VA_ARGS__)\n\nnamespace Airwave {\n\n\nenum class LogLevel {\n\tkDefault = -1,\n\tkQuiet,\n\tkError,\n\tkTrace,\n\tkDebug,\n\tkFlood\n};\n\n\nbool loggerInit(const std::string& socketPath, const std::string& senderId);\nvoid loggerFree();\nLogLevel loggerLogLevel();\nvoid loggerSetLogLevel(LogLevel level);\nstd::string loggerSenderId();\nvoid loggerSetSenderId(const std::string& senderId);\nvoid loggerMessage(LogLevel level, const char* format, ...);\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_LOGGER_H\n"
  },
  {
    "path": "src/common/moduleinfo.cpp",
    "content": "#include \"moduleinfo.h\"\n\n\nModuleInfo::ModuleInfo() :\n\tisInitialized_(false)\n{\n\tmagic_ = magic_open(MAGIC_NONE);\n\tif(!magic_) {\n\t\treturn;\n\t}\n\n\tif(magic_load(magic_, nullptr) != 0) {\n\t\tmagic_close(magic_);\n\t\treturn;\n\t}\n\n\tisInitialized_ = true;\n}\n\n\nModuleInfo::~ModuleInfo()\n{\n\tmagic_close(magic_);\n}\n\n\nModuleInfo* ModuleInfo::instance()\n{\n\tstatic ModuleInfo info;\n\treturn &info;\n}\n\n\nModuleInfo::Arch ModuleInfo::getArch(const std::string& fileName) const\n{\n\tif(isInitialized_) {\n\t\tconst char* buffer = magic_file(magic_, fileName.c_str());\n\n\t\tif(buffer) {\n\t\t\tstd::string string = buffer;\n\n\t\t\tif(string.find(\"80386\") != std::string::npos) {\n\t\t\t\treturn kArch32;\n\t\t\t}\n\t\t\telse if(string.find(\"x86-64\") != std::string::npos) {\n\t\t\t\treturn kArch64;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn kArchUnknown;\n}\n"
  },
  {
    "path": "src/common/moduleinfo.h",
    "content": "#ifndef COMMON_MODULEINFO_H\n#define COMMON_MODULEINFO_H\n\n#include <string>\n#include <magic.h>\n\n\nclass ModuleInfo {\npublic:\n\tenum Arch {\n\t\tkArchUnknown = 0,\n\t\tkArch32      = 32,\n\t\tkArch64      = 64\n\t};\n\n\tstatic ModuleInfo* instance();\n\n\tModuleInfo(const ModuleInfo&) = delete;\n\tModuleInfo& operator=(const ModuleInfo&) = delete;\n\n\t~ModuleInfo();\n\n\tArch getArch(const std::string& fileName) const;\n\nprivate:\n\tbool isInitialized_;\n\tmagic_t magic_;\n\n\tModuleInfo();\n};\n\n\n#endif // COMMON_MODULEINFO_H\n"
  },
  {
    "path": "src/common/protocol.h",
    "content": "#ifndef COMMON_PROTOCOL_H\n#define COMMON_PROTOCOL_H\n\n#include \"common/types.h\"\n\n\nnamespace Airwave {\n\n\nenum class Command {\n\tResponse,\n\tDispatch,\n\tGetParameter,\n\tSetParameter,\n\tProcessSingle,\n\tProcessDouble,\n\tHostInfo,\n\tPluginInfo,\n\tShowWindow,\n\tGetDataBlock,\n\tSetDataBlock,\n\tAudioMaster\n};\n\n\nstruct DataFrame {\n\tCommand command;\n\ti32     opcode;\n\ti32     index;\n\ti64     value;\t// The 64-bit value is used here to avoid 64->32 bridging issues\n\tfloat   opt;\n\tu8      data[];\n} __attribute__((packed));\n\n\nstruct PluginInfo {\n\ti32 flags;\n\ti32 programCount;\n\ti32 paramCount;\n\ti32 inputCount;\n\ti32 outputCount;\n\ti32 initialDelay;\n\ti32 uniqueId;\n\ti32 version;\n} __attribute__((packed));\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_PROTOCOL_H\n"
  },
  {
    "path": "src/common/storage.cpp",
    "content": "#include \"storage.h\"\n\n#include <fstream>\n#include \"common/config.h\"\n#include \"common/filesystem.h\"\n#include \"common/json.h\"\n\n\nnamespace Airwave {\n\n\nStorage::Storage() :\n\tisChanged_(false)\n{\n\treload();\n}\n\n\nStorage::~Storage()\n{\n\tsave();\n}\n\n\nbool Storage::reload()\n{\n\tstorageFilePath_.clear();\n\tlogSocketPath_.clear();\n\tbinariesPath_.clear();\n\tprefixByName_.clear();\n\tloaderByName_.clear();\n\tlinkByPath_.clear();\n\n\t// Add default WINE prefix\n\tstd::string name = \"default\";\n\tstd::string path = FileSystem::realPath(\"~\") + \"/.wine\";\n\tprefixByName_.emplace(makePair(name, path));\n\n\t// Add default WINE loader\n\tname = \"default\";\n\tpath = FileSystem::fullNameFromPath(\"wine\");\n\tloaderByName_.emplace(makePair(name, path));\n\n\t// Initialize default values\n\tbinariesPath_ = INSTALL_PREFIX \"/bin\";\n\n\tconst char* string = getenv(\"TMPDIR\");\n\tstd::string tempPath = string ? string : \"/tmp\";\n\tlogSocketPath_ = tempPath + \"/\" PROJECT_NAME \".sock\";\n\n\tdefaultLogLevel_ = LogLevel::kTrace;\n\n\t// Find and read a configuration file\n\tstring = getenv(\"XDG_CONFIG_PATH\");\n\tstd::string configPath = string ? string : std::string();\n\tif(configPath.empty())\n\t\tconfigPath = \"~/.config\";\n\n\tconfigPath = FileSystem::realPath(configPath);\n\tif(configPath.back() != '/')\n\t\tconfigPath += '/';\n\n\tif(!FileSystem::isDirExists(configPath))\n\t\treturn false;\n\n\tstorageFilePath_ = configPath + PROJECT_NAME \"/\" PROJECT_NAME \".conf\";\n\n\tstd::ifstream file(storageFilePath_);\n\tif(!file.is_open())\n\t\treturn false;\n\n\tJson::Value root;\n\tJson::Reader reader;\n\tif(!reader.parse(file, root, false))\n\t\treturn false;\n\n\t// Load general variables\n\tauto value = root[\"binaries_path\"];\n\tif(!value.isNull())\n\t\tbinariesPath_ = value.asString();\n\n\tvalue = root[\"log_socket_path\"];\n\tif(!value.isNull())\n\t\tlogSocketPath_ = value.asString();\n\n\tvalue = root[\"default_log_level\"];\n\tif(!value.isNull()) {\n\t\tdefaultLogLevel_ = static_cast<LogLevel>(value.asInt());\n\t\tif(defaultLogLevel_ < LogLevel::kQuiet || defaultLogLevel_ > LogLevel::kFlood)\n\t\t\tdefaultLogLevel_ = LogLevel::kTrace;\n\t}\n\n\t// Load prefixes\n\tJson::Value prefixes = root[\"prefixes\"];\n\tfor(uint i = 0; i < prefixes.size(); ++i) {\n\t\tJson::Value prefix = prefixes[i];\n\n\t\tname = prefix[\"name\"].asString();\n\t\tpath = prefix[\"path\"].asString();\n\n\t\tif(name.empty() || path.empty())\n\t\t\tcontinue;\n\n\t\tprefixByName_.emplace(makePair(name, path));\n\t}\n\n\t// Load loaders\n\tJson::Value loaders = root[\"loaders\"];\n\tfor(uint i = 0; i < loaders.size(); ++i) {\n\t\tJson::Value loader = loaders[i];\n\n\t\tname = loader[\"name\"].asString();\n\t\tpath = loader[\"path\"].asString();\n\n\t\tif(name.empty() || path.empty())\n\t\t\tcontinue;\n\n\t\tloaderByName_.emplace(makePair(name, path));\n\t}\n\n\t// Load links\n\tJson::Value links = root[\"links\"];\n\tfor(uint i = 0; i < links.size(); ++i) {\n\t\tJson::Value link = links[i];\n\n\t\tLinkInfo info;\n\t\tinfo.target = link[\"target\"].asString();\n\n\t\tinfo.loader = link[\"loader\"].asString();\n\t\tif(info.loader.empty())\n\t\t\tinfo.loader = \"default;\";\n\n\t\tinfo.prefix = link[\"prefix\"].asString();\n\t\tif(info.prefix.empty())\n\t\t\tinfo.prefix = \"default;\";\n\n\t\tauto value = link[\"log_level\"];\n\t\tif(value.isNull()) {\n\t\t\tinfo.level = LogLevel::kDefault;\n\t\t}\n\t\telse {\n\t\t\tinfo.level = static_cast<LogLevel>(value.asInt());\n\t\t\tif(info.level < LogLevel::kDefault || info.level > LogLevel::kFlood)\n\t\t\t\tinfo.level = LogLevel::kDefault;\n\t\t}\n\n\t\tpath = link[\"path\"].asString();\n\t\tlinkByPath_.emplace(makePair(path, info));\n\t}\n\n\treturn true;\n}\n\n\nbool Storage::save()\n{\n\tif(!isChanged_)\n\t\treturn true;\n\n\tstd::size_t pos = storageFilePath_.rfind('/');\n\tif(pos != std::string::npos) {\n\t\tstd::string dir = storageFilePath_.substr(0, pos);\n\t\tif(!FileSystem::makePath(dir))\n\t\t\treturn false;\n\t}\n\n\tstd::ofstream file(storageFilePath_, std::ios::out | std::ios::trunc);\n\n\tif(!file.is_open())\n\t\treturn false;\n\n\tJson::Value root;\n\troot[\"binaries_path\"] = binariesPath_;\n\troot[\"log_socket_path\"] = logSocketPath_;\n\troot[\"default_log_level\"] = static_cast<int>(defaultLogLevel_);\n\n\tJson::Value prefixes(Json::arrayValue);\n\tfor(auto it : prefixByName_) {\n\t\tif(it.first == \"default\")\n\t\t\tcontinue;\n\n\t\tJson::Value prefix;\n\t\tprefix[\"name\"] = it.first;\n\t\tprefix[\"path\"] = it.second;\n\n\t\tprefixes.append(prefix);\n\t}\n\n\troot[\"prefixes\"] = prefixes;\n\n\tJson::Value loaders(Json::arrayValue);\n\tfor(auto it : loaderByName_) {\n\t\tif(it.first == \"default\")\n\t\t\tcontinue;\n\n\t\tJson::Value loader;\n\t\tloader[\"name\"] = it.first;\n\t\tloader[\"path\"] = it.second;\n\n\t\tloaders.append(loader);\n\t}\n\n\troot[\"loaders\"] = loaders;\n\n\tJson::Value links(Json::arrayValue);\n\tfor(auto& it : linkByPath_) {\n\t\tJson::Value link;\n\t\tlink[\"path\"]   = it.first;\n\t\tlink[\"loader\"] = it.second.loader;\n\t\tlink[\"prefix\"] = it.second.prefix;\n\t\tlink[\"target\"] = it.second.target;\n\t\tlink[\"log_level\"] = static_cast<int>(it.second.level);\n\n\t\tlinks.append(link);\n\t}\n\n\troot[\"links\"] = links;\n\n\tJson::StyledWriter writer;\n\tfile << writer.write(root);\n\n\tisChanged_ = false;\n\treturn true;\n}\n\n\nstd::string Storage::logSocketPath() const\n{\n\treturn logSocketPath_;\n}\n\n\nvoid Storage::setLogSocketPath(const std::string& path)\n{\n\tlogSocketPath_ = path;\n\tisChanged_ = true;\n}\n\n\nstd::string Storage::binariesPath() const\n{\n\treturn binariesPath_;\n}\n\n\nvoid Storage::setBinariesPath(const std::string& path)\n{\n\tbinariesPath_ = path;\n\tisChanged_ = true;\n}\n\n\nLogLevel Storage::defaultLogLevel() const\n{\n\treturn defaultLogLevel_;\n}\n\n\nvoid Storage::setDefaultLogLevel(LogLevel level)\n{\n\tdefaultLogLevel_ = level;\n\tisChanged_ = true;\n}\n\n\nStorage::Prefix Storage::prefix(const std::string& name)\n{\n\tif(name.empty())\n\t\treturn Prefix(this, prefixByName_.begin());\n\n\treturn Prefix(this, prefixByName_.find(name));\n}\n\n\nStorage::Prefix Storage::createPrefix(const std::string& name, const std::string& path)\n{\n\tauto it = prefixByName_.find(name);\n\tif(it != prefixByName_.end())\n\t\treturn Prefix();\n\n\tauto result = prefixByName_.emplace(name, path);\n\tisChanged_ = true;\n\treturn Prefix(this, result.first);\n}\n\n\nbool Storage::removePrefix(Storage::Prefix prefix)\n{\n\tif(!prefix || prefix.name() == \"default\")\n\t\treturn false;\n\n\tprefixByName_.erase(prefix.it_);\n\tisChanged_ = true;\n\treturn true;\n}\n\n\nStorage::Loader Storage::loader(std::string const& name)\n{\n\tif(name.empty())\n\t\treturn Loader(this, loaderByName_.begin());\n\n\treturn Loader(this, loaderByName_.find(name));\n}\n\n\nStorage::Loader Storage::createLoader(const std::string& name, const std::string& path)\n{\n\tauto it = loaderByName_.find(name);\n\tif(it != loaderByName_.end())\n\t\treturn Loader();\n\n\tauto result = loaderByName_.emplace(name, path);\n\tisChanged_ = true;\n\treturn Loader(this, result.first);\n}\n\n\nbool Storage::removeLoader(Storage::Loader loader)\n{\n\tif(!loader || loader.name() == \"default\")\n\t\treturn false;\n\n\tloaderByName_.erase(loader.it_);\n\tisChanged_ = true;\n\treturn true;\n}\n\n\nStorage::Link Storage::link(std::string const& path)\n{\n\tif(path.empty())\n\t\treturn Link(this, linkByPath_.begin());\n\n\treturn Link(this, linkByPath_.find(path));\n}\n\n\nStorage::Link Storage::createLink(const std::string& path, const std::string& target,\n\t\tconst std::string& prefix, const std::string& loader)\n{\n\tauto it = linkByPath_.find(path);\n\tif(it != linkByPath_.end())\n\t\treturn Link();\n\n\tauto prefixIt = prefixByName_.find(prefix);\n\tif(prefixIt == prefixByName_.end())\n\t\treturn Link();\n\n\tauto loaderIt = loaderByName_.find(loader);\n\tif(loaderIt == loaderByName_.end())\n\t\treturn Link();\n\n\tLinkInfo info;\n\tinfo.target = target;\n\tinfo.prefix = prefix;\n\tinfo.loader = loader;\n\tinfo.level  = LogLevel::kDefault;\n\n\tauto result = linkByPath_.emplace(makePair(path, info));\n\tif(!result.second)\n\t\treturn Link();\n\n\tisChanged_ = true;\n\treturn Link(this, result.first);\n}\n\n\nbool Storage::removeLink(Storage::Link link)\n{\n\tif(!link)\n\t\treturn false;\n\n\tlinkByPath_.erase(link.it_);\n\tisChanged_ = true;\n\treturn true;\n}\n\n\nStorage::Prefix::Prefix() :\n\tstorage_(nullptr)\n{\n}\n\n\nStorage::Prefix::Prefix(Storage* storage, PrefixMap::iterator it) :\n\tstorage_(storage),\n\tit_(it)\n{\n}\n\n\nbool Storage::Prefix::isNull() const\n{\n\treturn !storage_ || it_ == storage_->prefixByName_.end();\n}\n\n\nstd::string Storage::Prefix::name() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->first;\n}\n\n\nbool Storage::Prefix::setName(const std::string& name)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tstd::string oldName = it_->first;\n\tstd::string path = it_->second;\n\tauto result = storage_->prefixByName_.emplace(makePair(name, path));\n\tif(!result.second)\n\t\treturn false;\n\n\tfor(auto& it : storage_->linkByPath_) {\n\t\tif(it.second.prefix == oldName)\n\t\t\tit.second.prefix = name;\n\t}\n\n\tstorage_->prefixByName_.erase(it_);\n\tstorage_->isChanged_ = true;\n\tit_ = result.first;\n\treturn true;\n}\n\n\nstd::string Storage::Prefix::path() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->second;\n}\n\n\nbool Storage::Prefix::setPath(const std::string& path)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(path == it_->second)\n\t\treturn true;\n\n\tit_->second = path;\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nStorage::Prefix Storage::Prefix::next() const\n{\n\tif(storage_ && it_ != storage_->prefixByName_.end()) {\n\t\tauto nextIt = it_;\n\t\treturn Prefix(storage_, ++nextIt);\n\t}\n\n\treturn Prefix();\n}\n\n\nbool Storage::Prefix::operator!() const\n{\n\treturn isNull();\n}\n\n\nStorage::Loader::Loader() :\n\tstorage_(nullptr)\n{\n}\n\n\nStorage::Loader::Loader(Storage* storage, LoaderMap::iterator it) :\n\tstorage_(storage),\n\tit_(it)\n{\n}\n\n\nbool Storage::Loader::isNull() const\n{\n\treturn !storage_ || it_ == storage_->loaderByName_.end();\n}\n\n\nstd::string Storage::Loader::name() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->first;\n}\n\n\nbool Storage::Loader::setName(const std::string& name)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tstd::string oldName = it_->first;\n\tstd::string path = it_->second;\n\tauto result = storage_->loaderByName_.emplace(makePair(name, path));\n\tif(!result.second)\n\t\treturn false;\n\n\tfor(auto& it : storage_->linkByPath_) {\n\t\tif(it.second.loader == oldName)\n\t\t\tit.second.loader = name;\n\t}\n\n\tstorage_->loaderByName_.erase(it_);\n\tstorage_->isChanged_ = true;\n\tit_ = result.first;\n\treturn true;\n}\n\n\nstd::string Storage::Loader::path() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->second;\n}\n\n\nbool Storage::Loader::setPath(const std::string& path)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(path == it_->second)\n\t\treturn true;\n\n\tit_->second = path;\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nStorage::Loader Storage::Loader::next() const\n{\n\tif(storage_ && it_ != storage_->loaderByName_.end()) {\n\t\tauto nextIt = it_;\n\t\treturn Loader(storage_, ++nextIt);\n\t}\n\n\treturn Loader();\n}\n\n\nbool Storage::Loader::operator!() const\n{\n\treturn isNull();\n}\n\n\nStorage::Link::Link() :\n\tstorage_(nullptr)\n{\n}\n\n\nStorage::Link::Link(Storage* storage, LinkMap::iterator it) :\n\tstorage_(storage),\n\tit_(it)\n{\n}\n\n\nbool Storage::Link::isNull() const\n{\n\treturn !storage_ || it_ == storage_->linkByPath_.end();\n}\n\n\nstd::string Storage::Link::path() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->first;\n}\n\n\nbool Storage::Link::setPath(const std::string& path)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(path == it_->first)\n\t\treturn true;\n\n\tauto it = storage_->linkByPath_.find(path);\n\tif(it != storage_->linkByPath_.end())\n\t\treturn false;\n\n\tLinkInfo info = it_->second;\n\tstorage_->linkByPath_.erase(it_);\n\tit_ = storage_->linkByPath_.emplace_hint(it, makePair(path, info));\n\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nstd::string Storage::Link::target() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->second.target;\n}\n\n\nbool Storage::Link::setTarget(const std::string& path)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(path == it_->second.target)\n\t\treturn true;\n\n\tit_->second.target = path;\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nstd::string Storage::Link::prefix() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->second.prefix;\n}\n\n\nbool Storage::Link::setPrefix(const std::string& prefix)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(prefix == it_->second.prefix)\n\t\treturn true;\n\n\tit_->second.prefix = prefix;\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nstd::string Storage::Link::loader() const\n{\n\tif(isNull())\n\t\treturn std::string();\n\n\treturn it_->second.loader;\n}\n\n\nbool Storage::Link::setLoader(const std::string& loader)\n{\n\tif(isNull())\n\t\treturn false;\n\n\tif(loader == it_->second.loader)\n\t\treturn true;\n\n\tit_->second.loader = loader;\n\tstorage_->isChanged_ = true;\n\treturn true;\n}\n\n\nLogLevel Storage::Link::logLevel() const\n{\n\tif(isNull())\n\t\treturn LogLevel::kDefault;\n\n\treturn it_->second.level;\n}\n\n\nvoid Storage::Link::setLogLevel(LogLevel level)\n{\n\tif(!isNull() && level != it_->second.level) {\n\t\tit_->second.level = level;\n\t\tstorage_->isChanged_ = true;\n\t}\n}\n\n\nStorage::Link Storage::Link::next() const\n{\n\tif(storage_ && it_ != storage_->linkByPath_.end()) {\n\t\tauto nextIt = it_;\n\t\treturn Link(storage_, ++nextIt);\n\t}\n\n\treturn Link();\n}\n\n\nbool Storage::Link::operator!() const\n{\n\treturn isNull();\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/common/storage.h",
    "content": "#ifndef COMMON_STORAGE_H\n#define COMMON_STORAGE_H\n\n#include <map>\n#include <string>\n#include \"common/logger.h\"\n#include \"common/types.h\"\n\n\nnamespace Airwave {\n\n\nclass Storage {\npublic:\n\tclass Prefix {\n\tpublic:\n\t\tPrefix();\n\t\tbool isNull() const;\n\n\t\tstd::string name() const;\n\t\tbool setName(const std::string& name);\n\n\t\tstd::string path() const;\n\t\tbool setPath(const std::string& path);\n\n\t\tPrefix next() const;\n\t\tbool operator!() const;\n\n\tprivate:\n\t\tfriend class Storage;\n\t\tusing PrefixMap = std::map<std::string, std::string>;\n\n\t\tStorage* storage_;\n\t\tPrefixMap::iterator it_;\n\n\t\tPrefix(Storage* storage, PrefixMap::iterator it);\n\t};\n\n\tclass Loader {\n\tpublic:\n\t\tLoader();\n\t\tbool isNull() const;\n\n\t\tstd::string name() const;\n\t\tbool setName(const std::string& name);\n\n\t\tstd::string path() const;\n\t\tbool setPath(const std::string& path);\n\n\t\tLoader next() const;\n\t\tbool operator!() const;\n\n\tprivate:\n\t\tfriend class Storage;\n\t\tusing LoaderMap = std::map<std::string, std::string>;\n\n\t\tStorage* storage_;\n\t\tLoaderMap::iterator it_;\n\n\t\tLoader(Storage* storage, LoaderMap::iterator it);\n\t};\n\n\n\tstruct LinkInfo {\n\t\tstd::string target;\n\t\tstd::string prefix;\n\t\tstd::string loader;\n\t\tLogLevel level;\n\t};\n\n\tclass Link {\n\tpublic:\n\t\tLink();\n\t\tbool isNull() const;\n\n\t\tstd::string path() const;\n\t\tbool setPath(const std::string& path);\n\n\t\tstd::string target() const;\n\t\tbool setTarget(const std::string& path);\n\n\t\tstd::string prefix() const;\n\t\tbool setPrefix(const std::string& prefix);\n\n\t\tstd::string loader() const;\n\t\tbool setLoader(const std::string& loader);\n\n\t\tLogLevel logLevel() const;\n\t\tvoid setLogLevel(LogLevel level);\n\n\t\tLink next() const;\n\t\tbool operator!() const;\n\n\tprivate:\n\t\tfriend class Storage;\n\t\tusing LinkMap = std::map<std::string, LinkInfo>;\n\n\t\tStorage* storage_;\n\t\tLinkMap::iterator it_;\n\n\t\tLink(Storage* storage, LinkMap::iterator it);\n\t};\n\n\tStorage();\n\t~Storage();\n\n\tbool reload();\n\tbool save();\n\n\tstd::string logSocketPath() const;\n\tvoid setLogSocketPath(const std::string& path);\n\n\tstd::string binariesPath() const;\n\tvoid setBinariesPath(const std::string& path);\n\n\tLogLevel defaultLogLevel() const;\n\tvoid setDefaultLogLevel(LogLevel level);\n\n\tPrefix prefix(const std::string& name = std::string());\n\tPrefix createPrefix(const std::string& name, const std::string& path);\n\tbool removePrefix(Prefix prefix);\n\n\tLoader loader(const std::string& name = std::string());\n\tLoader createLoader(const std::string& name, const std::string& path);\n\tbool removeLoader(Loader loader);\n\n\tLink link(const std::string& path = std::string());\n\n\tLink createLink(const std::string& path, const std::string& target,\n\t\t\tconst std::string& prefix, const std::string& loader);\n\n\tbool removeLink(Link link);\n\nprivate:\n\tfriend class Prefix;\n\tfriend class Loader;\n\tfriend class Link;\n\n\tbool isChanged_;\n\n\tstd::string storageFilePath_;\n\tstd::string logSocketPath_;\n\tstd::string binariesPath_;\n\tLogLevel defaultLogLevel_;\n\n\tstd::map<std::string, std::string> prefixByName_;\n\tstd::map<std::string, std::string> loaderByName_;\n\tstd::map<std::string, LinkInfo> linkByPath_;\n};\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_STORAGE_H\n"
  },
  {
    "path": "src/common/types.h",
    "content": "#ifndef COMMON_TYPES_H\n#define COMMON_TYPES_H\n\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <tuple>\n#include <type_traits>\n\n// Remove stupid definitions from stdbool.h\n#undef bool\n#undef true\n#undef false\n\n#define UNUSED(x) ((void) x)\n\n// Basic data types\nusing schar     = signed char;\nusing uchar     = unsigned char;\nusing ushort    = unsigned short;\nusing uint      = unsigned int;\nusing ulong     = unsigned long;\nusing longlong  = long long;\nusing ulonglong = unsigned long long;\n\nusing ch16 = char16_t;\nusing ch32 = char32_t;\nusing i8   = int8_t;\nusing u8   = uint8_t;\nusing i16  = int16_t;\nusing u16  = uint16_t;\nusing i32  = int32_t;\nusing u32  = uint32_t;\nusing i64  = int64_t;\nusing u64  = uint64_t;\nusing f32  = float;\nusing f64  = double;\n\n\ntemplate<typename T>\nusing Box = std::unique_ptr<T>;\n\ntemplate<typename T>\nusing Rc = std::shared_ptr<T>;\n\ntemplate<typename T>\nusing Ref = std::weak_ptr<T>;\n\ntemplate<typename First, typename Second>\nusing Pair = std::pair<First, Second>;\n\ntemplate<typename... Args>\nusing Tuple = std::tuple<Args...>;\n\n\ntemplate<typename T, typename... Args>\nconstexpr Box<T> makeBox(Args&&... args)\n{\n\treturn Box<T>(new T(std::forward<Args>(args)...));\n}\n\ntemplate<typename T, typename ...Args>\nconstexpr Rc<T> makeRc(Args&&... args)\n{\n\treturn Rc<T>(new T(std::forward<Args>(args)...));\n}\n\n\ntemplate<typename First, typename Second>\nconstexpr Pair<typename std::decay<First>::type, typename std::decay<Second>::type>\nmakePair(First&& first, Second&& second)\n{\n\tusing PairType = Pair<First, Second>;\n\treturn PairType(std::forward<First>(first), std::forward<Second>(second));\n}\n\n\ntemplate<typename ...Args>\nconstexpr Tuple<typename std::decay<Args>::type...> makeTuple(Args&&... args)\n{\n\treturn std::make_tuple<Args...>(std::forward<Args>(args)...);\n}\n\n\n#endif // COMMON_TYPES_H\n"
  },
  {
    "path": "src/common/vst24.h",
    "content": "#ifndef COMMON_VST24_H\n#define COMMON_VST24_H\n\n#include <aeffect.h>\n#include <aeffectx.h>\n#include \"common/types.h\"\n\n\nnamespace Airwave {\n\n\n//using AudioMasterProc = intptr_t (VSTCALLBACK*)(AEffect*, i32, i32, intptr_t, void*, float);\n//using VstPluginMainProc = AEffect* (VSTCALLBACK*)(AudioMasterProc);\n\ntypedef\tintptr_t (VSTCALLBACK *AudioMasterProc)(AEffect*, i32, i32, intptr_t, void*,\n\t\tfloat);\n\ntypedef AEffect* (VSTCALLBACK *VstPluginMainProc)(AudioMasterProc);\n\n\nstatic const char* const kDispatchEvents[] = {\n\t\"effOpen\",\n\t\"effClose\",\n\t\"effSetProgram\",\n\t\"effGetProgram\",\n\t\"effSetProgramName\",\n\t\"effGetProgramName\",\n\t\"effGetParamLabel\",\n\t\"effGetParamDisplay\",\n\t\"effGetParamName\",\n\t\"effGetVu\",\n\t\"effSetSampleRate\",\n\t\"effSetBlockSize\",\n\t\"effMainsChanged\",\n\t\"effEditGetRect\",\n\t\"effEditOpen\",\n\t\"effEditClose\",\n\t\"effEditDraw\",\n\t\"effEditMouse\",\n\t\"effEditKey\",\n\t\"effEditIdle\",\n\t\"effEditTop\",\n\t\"effEditSleep\",\n\t\"effIdentify\",\n\t\"effGetChunk\",\n\t\"effSetChunk\",\n\t\"effProcessEvents\",\n\t\"effCanBeAutomated\",\n\t\"effString2Parameter\",\n\t\"effGetNumProgramCategories\",\n\t\"effGetProgramNameIndexed\",\n\t\"effCopyProgram\",\n\t\"effConnectInput\",\n\t\"effConnectOutput\",\n\t\"effGetInputProperties\",\n\t\"effGetOutputProperties\",\n\t\"effGetPlugCategory\",\n\t\"effGetCurrentPosition\",\n\t\"effGetDestinationBuffer\",\n\t\"effOfflineNotify\",\n\t\"effOfflinePrepare\",\n\t\"effOfflineRun\",\n\t\"effProcessVarIo\",\n\t\"effSetSpeakerArrangement\",\n\t\"effSetBlockSizeAndSampleRate\",\n\t\"effSetBypass\",\n\t\"effGetEffectName\",\n\t\"effGetErrorText\",\n\t\"effGetVendorString\",\n\t\"effGetProductString\",\n\t\"effGetVendorVersion\",\n\t\"effVendorSpecific\",\n\t\"effCanDo\",\n\t\"effGetTailSize\",\n\t\"effIdle\",\n\t\"effGetIcon\",\n\t\"effSetViewPosition\",\n\t\"effGetParameterProperties\",\n\t\"effKeysRequired\",\n\t\"effGetVstVersion\",\n\t\"effEditKeyDown\",\n\t\"effEditKeyUp\",\n\t\"effSetEditKnobMode\",\n\t\"effGetMidiProgramName\",\n\t\"effGetCurrentMidiProgram\",\n\t\"effGetMidiProgramCategory\",\n\t\"effHasMidiProgramsChanged\",\n\t\"effGetMidiKeyName\",\n\t\"effBeginSetProgram\",\n\t\"effEndSetProgram\",\n\t\"effGetSpeakerArrangement\",\n\t\"effShellGetNextPlugin\",\n\t\"effStartProcess\",\n\t\"effStopProcess\",\n\t\"effSetTotalSampleToProcess\",\n\t\"effSetPanLaw\",\n\t\"effBeginLoadBank\",\n\t\"effBeginLoadProgram\",\n\t\"effSetProcessPrecision\",\n\t\"effGetNumMidiInputChannels\",\n\t\"effGetNumMidiOutputChannels\"\n};\n\n\nstatic const char* const kAudioMasterEvents[] = {\n\t\"audioMasterAutomate\",\n\t\"audioMasterVersion\",\n\t\"audioMasterCurrentId\",\n\t\"audioMasterIdle\",\n\t\"audioMasterPinConnected\",\n\t\"\",\n\t\"audioMasterWantMidi\",\n\t\"audioMasterGetTime\",\n\t\"audioMasterProcessEvents\",\n\t\"audioMasterSetTime\",\n\t\"audioMasterTempoAt\",\n\t\"audioMasterGetNumAutomatableParameters\",\n\t\"audioMasterGetParameterQuantization\",\n\t\"audioMasterIOChanged\",\n\t\"audioMasterNeedIdle\",\n\t\"audioMasterSizeWindow\",\n\t\"audioMasterGetSampleRate\",\n\t\"audioMasterGetBlockSize\",\n\t\"audioMasterGetInputLatency\",\n\t\"audioMasterGetOutputLatency\",\n\t\"audioMasterGetPreviousPlug\",\n\t\"audioMasterGetNextPlug\",\n\t\"audioMasterWillReplaceOrAccumulate\",\n\t\"audioMasterGetCurrentProcessLevel\",\n\t\"audioMasterGetAutomationState\",\n\t\"audioMasterOfflineStart\",\n\t\"audioMasterOfflineRead\",\n\t\"audioMasterOfflineWrite\",\n\t\"audioMasterOfflineGetCurrentPass\",\n\t\"audioMasterOfflineGetCurrentMetaPass\",\n\t\"audioMasterSetOutputSampleRate\",\n\t\"audioMasterGetOutputSpeakerArrangement\",\n\t\"audioMasterGetVendorString\",\n\t\"audioMasterGetProductString\",\n\t\"audioMasterGetVendorVersion\",\n\t\"audioMasterVendorSpecific\",\n\t\"audioMasterSetIcon\",\n\t\"audioMasterCanDo\",\n\t\"audioMasterGetLanguage\",\n\t\"audioMasterOpenWindow\",\n\t\"audioMasterCloseWindow\",\n\t\"audioMasterGetDirectory\",\n\t\"audioMasterUpdateDisplay\",\n\t\"audioMasterBeginEdit\",\n\t\"audioMasterEndEdit\",\n\t\"audioMasterOpenFileSelector\",\n\t\"audioMasterCloseFileSelector\",\n\t\"audioMasterEditFile\",\n\t\"audioMasterGetChunkFile\",\n\t\"audioMasterGetInputSpeakerArrangement\"\n};\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_VST24_H\n"
  },
  {
    "path": "src/common/vsteventkeeper.cpp",
    "content": "#include \"vsteventkeeper.h\"\n\n#include <algorithm>\n#include <cstdint>\n\n\nnamespace Airwave {\n\n\nVstEventKeeper::VstEventKeeper() :\n\tevents_(nullptr),\n\tdata_(nullptr)\n{\n}\n\n\nVstEventKeeper::~VstEventKeeper()\n{\n\tdelete [] events_;\n}\n\n\nvoid VstEventKeeper::reload(int count, const VstEvent events[])\n{\n\tif(!events_ || events_->numEvents < count) {\n\t\tdelete [] events_;\n\n\t\tint extraCount = std::max(count - 2, 0);\n\n\t\tsize_t size = sizeof(VstEvents) + extraCount * sizeof(VstEvent*) +\n\t\t\t\tcount * sizeof(VstEvent);\n\n\t\tuint8_t* buffer = new uint8_t[size];\n\t\tevents_ = reinterpret_cast<VstEvents*>(buffer);\n\n\t\tsize_t offset = sizeof(VstEvents) + extraCount * sizeof(VstEvent*);\n\t\tdata_ = reinterpret_cast<VstEvent*>(buffer + offset);\n\t}\n\n\tevents_->numEvents = count;\n\tevents_->reserved = 0;\n\n\tfor(int i = 0; i < count; ++i) {\n\t\tdata_[i] = events[i];\n\t\tevents_->events[i] = &data_[i];\n\t}\n}\n\n\nVstEvents* VstEventKeeper::events()\n{\n\treturn events_;\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/common/vsteventkeeper.h",
    "content": "#ifndef COMMON_VSTEVENTSKEEPER_H\n#define COMMON_VSTEVENTSKEEPER_H\n\n#include <aeffectx.h>\n\n\nnamespace Airwave {\n\n\nclass VstEventKeeper {\npublic:\n\tVstEventKeeper();\n\t~VstEventKeeper();\n\n\tvoid reload(int count, const VstEvent events[]);\n\tVstEvents* events();\n\nprivate:\n\tVstEvents* events_;\n\tVstEvent* data_;\n};\n\n\n} // namespace Airwave\n\n\n#endif // COMMON_VSTEVENTSKEEPER_H\n"
  },
  {
    "path": "src/host/CMakeLists.txt",
    "content": "# Configure base name\nset(TARGET_NAME ${HOST_BASENAME})\n\nproject(${TARGET_NAME})\n\noption(DISABLE_64BIT  \"Disable building of the 64-bit host endpoint\" OFF)\n\nfind_package(Threads REQUIRED)\n\ninclude_directories(\n\t${CMAKE_CURRENT_BINARY_DIR}\n\t${CMAKE_CURRENT_SOURCE_DIR}\n\t${VSTSDK_INCLUDE_DIR}\n)\n\n# Set target operating system\nset(CMAKE_SYSTEM_NAME Windows)\n\n# Set compilers to use\nset(CMAKE_C_COMPILER winegcc)\nset(CMAKE_CXX_COMPILER wineg++)\n\nadd_definitions(-DNOMINMAX)\n\nif(DEBUG_BINARY_DIR)\n\tset(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${DEBUG_BINARY_DIR})\n\tset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${DEBUG_BINARY_DIR})\nendif()\n\n# Sources\nset(SOURCES\n\t../common/dataport.cpp\n\t../common/event.cpp\n\t../common/filesystem.cpp\n\t../common/logger.cpp\n\t../common/vsteventkeeper.cpp\n\thost.cpp\n\tmain.cpp\n)\n\nif(NOT DISABLE_64BIT AND PLATFORM_64BIT)\n\t# Set target\n\tadd_executable(${TARGET_NAME}-64 WIN32 ${SOURCES})\n\n\tset_target_properties(${TARGET_NAME}-64 PROPERTIES\n\t\tCOMPILE_FLAGS \"-m64\"\n\t\tLINK_FLAGS \"-m64\"\n\t)\n\n\t# Link with libraries\n\ttarget_link_libraries(${TARGET_NAME}-64\n\t\t${CMAKE_THREAD_LIBS_INIT}\n\t)\n\n\tinstall(PROGRAMS\n\t\t${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}-64.exe\n\t\t${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}-64.exe.so\n\t\tDESTINATION bin\n\t)\nendif()\n\n\n# Set target\nadd_executable(${TARGET_NAME}-32 WIN32 ${SOURCES})\n\nset_target_properties(${TARGET_NAME}-32 PROPERTIES\n\tCOMPILE_FLAGS \"-m32\"\n\tLINK_FLAGS \"-m32\"\n)\n\n# Link with libraries\ntarget_link_libraries(${TARGET_NAME}-32\n\t${CMAKE_THREAD_LIBS_INIT}\n)\n\ninstall(PROGRAMS\n\t${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}-32.exe\n\t${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}-32.exe.so\n\tDESTINATION bin\n)\n"
  },
  {
    "path": "src/host/host.cpp",
    "content": "#include \"host.h\"\n\n#include <cstring>\n#include \"common/logger.h\"\n#include \"common/protocol.h\"\n\n\nnamespace Airwave {\n\n\nHost* Host::self_ = nullptr;\n\n\nHost::Host() :\n\tisInitialized_(false),\n\thwnd_(0),\n\tdata_(nullptr),\n\tdataLength_(0),\n\trunAudio_(ATOMIC_FLAG_INIT),\n\tisEditorOpen_(false),\n\toldWndProc_(nullptr),\n\tchildHwnd_(0)\n{\n\tDEBUG(\"Main thread id: %p\", GetCurrentThreadId());\n}\n\n\nHost::~Host()\n{\n\tif(isInitialized_) {\n\t\tTRACE(\"Waiting for audio thread termination...\");\n\n\t\trunAudio_.clear();\n\t\tWaitForSingleObject(audioThread_, INFINITE);\n\n\t\tdestroyEditorWindow();\n\n\t\tDeleteCriticalSection(&cs_);\n\t\tFreeLibrary(module_);\n\t}\n}\n\n\nbool Host::initialize(const char* fileName, int portId)\n{\n\tif(isInitialized_) {\n\t\tTRACE(\"Host endpoint is already initialized\");\n\t\treturn false;\n\t}\n\n\tmodule_ = LoadLibrary(fileName);\n\tif(!module_) {\n\t\tERROR(\"Unable to load '%s' shared library: %s\", fileName, errorString().c_str());\n\t\treturn false;\n\t}\n\n\tif(!InitializeCriticalSectionAndSpinCount(&cs_, 0x00010000))  {\n\t\tFreeLibrary(module_);\n\t\treturn false;\n\t}\n\n\tVstPluginMainProc vstMainProc = reinterpret_cast<VstPluginMainProc>(\n\t\t\tGetProcAddress(module_, \"VSTPluginMain\"));\n\n\tif(!vstMainProc) {\n\t\tvstMainProc = reinterpret_cast<VstPluginMainProc>(\n\t\t\t\tGetProcAddress(module_, \"main\"));\n\n\t\tif(!vstMainProc) {\n\t\t\tERROR(\"The %s is not a VST plugin\");\n\t\t\tDeleteCriticalSection(&cs_);\n\t\t\tFreeLibrary(module_);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif(!controlPort_.connect(portId)) {\n\t\tERROR(\"Unable to connect control port (id = %d)\", portId);\n\t\tDeleteCriticalSection(&cs_);\n\t\tFreeLibrary(module_);\n\t\treturn false;\n\t}\n\n\tTRACE(\"Waiting for plugin endpoint request...\");\n\n\tif(!controlPort_.waitRequest()) {\n\t\tERROR(\"Unable to get initial request from plugin endpoint\");\n\t\tcontrolPort_.disconnect();\n\t\tDeleteCriticalSection(&cs_);\n\t\tFreeLibrary(module_);\n\t\treturn false;\n\t}\n\n\tTRACE(\"Request from plugin endpoint received, sending response\");\n\n\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\tif(!callbackPort_.connect(frame->opcode)) {\n\t\tERROR(\"Unable to connect callback port (id = %d)\", frame->opcode);\n\t\tcontrolPort_.disconnect();\n\t\tDeleteCriticalSection(&cs_);\n\t\tFreeLibrary(module_);\n\t\treturn false;\n\t}\n\n\t// When we call vstMainProc(), the audioMasterProc() can be called from there with\n\t// effect argument set to the nullptr. This is because VST plugin object is not yet\n\t// initialized at this point. Since we need the pointer to our object inside of\n\t// audioMasterProc(), we must store it in some accessible place. Static member\n\t// pointer is very ugly, but very effective solution, since we don't need more than\n\t// one instance of host endpoint anyway.\n\tself_ = this;\n\n\tTRACE(\"Initializing VST plugin...\");\n\n\teffect_ = vstMainProc(audioMasterProc);\n\tif(!effect_ || effect_->magic != kEffectMagic) {\n\t\tERROR(\"Unable to initialize VST plugin\");\n\t\tcontrolPort_.disconnect();\n\t\tcallbackPort_.disconnect();\n\t\tDeleteCriticalSection(&cs_);\n\t\tFreeLibrary(module_);\n\t\treturn false;\n\t}\n\n\tTRACE(\"VST plugin is initialized\");\n\n\tstd::memset(&timeInfo_, 0, sizeof(VstTimeInfo));\n\n\tframe->command = Command::PluginInfo;\n\tPluginInfo* info = reinterpret_cast<PluginInfo*>(frame->data);\n\n\tinfo->flags        = effect_->flags;\n\tinfo->programCount = effect_->numPrograms;\n\tinfo->paramCount   = effect_->numParams;\n\tinfo->inputCount   = effect_->numInputs;\n\tinfo->outputCount  = effect_->numOutputs;\n\tinfo->initialDelay = effect_->initialDelay;\n\tinfo->uniqueId     = effect_->uniqueID;\n\tinfo->version      = effect_->version;\n\n\t// Workaround for plugins from Waves\n\tchar vendorName[kVstMaxVendorStrLen];\n\tif(effect_->dispatcher(effect_, effGetVendorString, 0, 0, &vendorName, 0.0f)) {\n\t\tif(strncmp(vendorName, \"Waves\", kVstMaxVendorStrLen) == 0)\n\t\t\tinfo->flags |= effFlagsHasEditor;\n\t}\n\n\tcontrolPort_.sendResponse();\n\n\tisInitialized_ = true;\n\treturn true;\n}\n\n\nbool Host::processRequest()\n{\n\tif(!controlPort_.isConnected()) {\n\t\tTRACE(\"Control port isn't connected anymore, exiting\");\n\t\treturn false;\n\t}\n\n\tif(!controlPort_.waitRequest(10))\n\t\treturn true;\n\n\tbool result = true;\n\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\n\tswitch(frame->command) {\n\tcase Command::Dispatch:\n\t\tresult = handleDispatch(frame);\n\t\tbreak;\n\n\tcase Command::GetDataBlock:\n\t\thandleGetDataBlock(frame);\n\t\tbreak;\n\n\tcase Command::SetDataBlock:\n\t\thandleSetDataBlock(frame);\n\t\tbreak;\n\n\tcase Command::ShowWindow: {\n\t\tif(hwnd_) {\n\t\t\tShowWindow(hwnd_, SW_SHOW);\n\t\t\tUpdateWindow(hwnd_);\n\t\t}\n\t\tbreak; }\n\n\tdefault:\n\t\tERROR(\"processRequest() unacceptable command: %d\", frame->command);\n\t\tbreak;\n\t}\n\n\tframe->command = Command::Response;\n\tcontrolPort_.sendResponse();\n\treturn result;\n}\n\n\nstd::string Host::errorString() const\n{\n\tDWORD error = GetLastError();\n\n\tif(error) {\n\t\tLPVOID buffer;\n\t\tDWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |\n\t\t\t\tFORMAT_MESSAGE_IGNORE_INSERTS;\n\n\t\tDWORD length = FormatMessage(flags, nullptr, error,\n\t\t\t\tMAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\n\t\t\t\treinterpret_cast<LPTSTR>(&buffer), 0, nullptr);\n\n\t\tif(length) {\n\t\t\tLPCSTR message = static_cast<LPCSTR>(buffer);\n\t\t\tstd::string result(message, message + length);\n\n\t\t\tLocalFree(buffer);\n\t\t\treturn result;\n\t\t}\n\t}\n\n\treturn std::string();\n}\n\n\nvoid Host::destroyEditorWindow()\n{\n\tif(hwnd_) {\n\t\tKillTimer(hwnd_, timerId_);\n\t\tDestroyWindow(hwnd_);\n\t\tUnregisterClass(kWindowClass, GetModuleHandle(nullptr));\n\t\thwnd_ = 0;\n\t}\n}\n\n\nvoid Host::audioThread()\n{\n\tcondition_.post();\n\n\twhile(runAudio_.test_and_set()) {\n\t\tif(audioPort_.waitRequest(100)) {\n\t\t\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\n\t\t\tif(frame->command == Command::ProcessSingle) {\n\t\t\t\thandleProcessSingle();\n\t\t\t}\n\t\t\telse if(frame->command == Command::GetParameter) {\n\t\t\t\thandleGetParameter();\n\t\t\t}\n\t\t\telse if(frame->command == Command::SetParameter) {\n\t\t\t\thandleSetParameter();\n\t\t\t}\n\t\t\telse if(frame->command == Command::ProcessDouble) {\n\t\t\t\thandleProcessDouble();\n\t\t\t}\n\t\t\telse if(frame->command == Command::Dispatch) {\n\t\t\t\thandleDispatch(frame);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tERROR(\"audioThread() unacceptable command: %d\", frame->command);\n\t\t\t}\n\n\t\t\tframe->command = Command::Response;\n\t\t\taudioPort_.sendResponse();\n\t\t}\n\t}\n}\n\n\nvoid Host::handleGetDataBlock(DataFrame* frame)\n{\n\tsize_t blockSize = frame->index;\n\tDEBUG(\"handleGetDataBlock: %d bytes\", blockSize);\n\tframe->index = dataLength_ < blockSize ? dataLength_ : blockSize;\n\tstd::copy(data_, data_ + frame->index, frame->data);\n\tdata_ += frame->index;\n\tdataLength_ -= frame->index;\n}\n\n\nvoid Host::handleSetDataBlock(DataFrame* frame)\n{\n\tchunk_.insert(chunk_.end(), frame->data, frame->data + frame->index);\n}\n\n\nbool Host::handleDispatch(DataFrame* frame)\n{\n\tFLOOD(\"handleDispatch: %s\", kDispatchEvents[frame->opcode]);\n\n\tif(isEditorOpen_ && frame->opcode != effEditIdle) {\n\t\t// Postpone the effEditIdle event by 100 milliseconds\n\t\tSetTimer(hwnd_, timerId_, 100, nullptr);\n\t}\n\n\tswitch(frame->opcode) {\n\tcase effClose:\n\t\t// Some stupid hosts doesn't send the effEditClose event before sending\n\t\t// the effClose event. This leads to crashes of some stupid plugins. So\n\t\t// we just emulate correct behavior here to avoid these crashes.\n\t\tif(isEditorOpen_) {\n\t\t\teffect_->dispatcher(effect_, effEditClose, 0, 0, nullptr, 0.0f);\n\t\t\tdestroyEditorWindow();\n\t\t\tisEditorOpen_ = false;\n\t\t}\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, nullptr, frame->opt);\n\t\tbreak;\n\n\tcase effGetVstVersion:\n\tcase effOpen:\n\tcase effGetPlugCategory:\n\tcase effSetSampleRate:\n\tcase effGetVendorVersion:\n\tcase effEditIdle:\n\tcase effMainsChanged:\n\tcase effCanBeAutomated:\n\tcase effGetProgram:\n\tcase effStartProcess:\n\tcase effSetProgram:\n\tcase effBeginSetProgram:\n\tcase effEndSetProgram:\n\tcase effStopProcess:\n\tcase effGetTailSize:\n\tcase effSetEditKnobMode:\n\tcase __effConnectInputDeprecated:\n\tcase __effConnectOutputDeprecated:\n\tcase __effKeysRequiredDeprecated:\n\tcase __effIdentifyDeprecated:\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, nullptr, frame->opt);\n\t\tbreak;\n\n\tcase effEditClose:\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, nullptr, frame->opt);\n\n\t\tdestroyEditorWindow();\n\t\tisEditorOpen_ = false;\n\t\tbreak;\n\n\tcase effSetBlockSize:\n\t\tif(runAudio_.test_and_set()) {\n\t\t\trunAudio_.clear();\n\t\t\tWaitForSingleObject(audioThread_, INFINITE);\n\t\t}\n\n\t\taudioPort_.disconnect();\n\t\tif(!audioPort_.connect(frame->index)) {\n\t\t\tERROR(\"Unable to connect audio port\");\n\t\t\treturn false;\n\t\t}\n\n\t\trunAudio_.test_and_set();\n\t\taudioThread_ = CreateThread(nullptr, 0, audioThreadProc, this, 0, nullptr);\n\n\t\tcondition_.wait();\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, nullptr, frame->opt);\n\t\tbreak;\n\n\tcase effEditOpen: {\n\t\tWNDCLASSEX wclass;\n\t\tstd::memset(&wclass, 0, sizeof(WNDCLASSEX));\n\n\t\twclass.cbSize        = sizeof(WNDCLASSEX);\n\t\twclass.style         = CS_HREDRAW | CS_VREDRAW;\n\t\twclass.lpfnWndProc   = windowProc;\n\t\twclass.cbClsExtra    = 0;\n\t\twclass.cbWndExtra    = 0;\n\t\twclass.hInstance     = GetModuleHandle(nullptr);\n\t\twclass.hIcon         = LoadIcon(nullptr, kWindowClass);\n\t\twclass.hCursor       = LoadCursor(nullptr, IDC_ARROW);\n\t\twclass.lpszClassName = kWindowClass;\n\n\t\tif(!RegisterClassEx(&wclass)) {\n\t\t\tERROR(\"Unable to register window class: %s\", errorString().c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\thwnd_ = CreateWindowEx(WS_EX_TOOLWINDOW, kWindowClass, \"Plugin\", WS_POPUP, 0, 0,\n\t\t\t\t200, 200, 0, 0, GetModuleHandle(nullptr), 0);\n\n\t\tif(!hwnd_) {\n\t\t\tERROR(\"Unable to create window: %s\", errorString().c_str());\n\t\t\tUnregisterClass(kWindowClass, module_);\n\t\t\treturn false;\n\t\t}\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, hwnd_, frame->opt);\n\n\t\t// Obtain the size of the VST window in advance.\n\t\tERect* rect = nullptr;\n\t\teffect_->dispatcher(effect_, effEditGetRect, 0, 0, &rect, 0.0f);\n\n\t\tRECT wndRect = { rect->left, rect->top, rect->right, rect->bottom };\n\n\t\tAdjustWindowRectEx(&wndRect, GetWindowLong(hwnd_, GWL_STYLE),\n\t\t\t\tGetMenu(hwnd_) != nullptr, GetWindowLong(hwnd_, GWL_EXSTYLE));\n\n\t\tSetWindowPos(hwnd_, 0, 0, 0, rect->right - rect->left, rect->bottom - rect->top,\n\t\t\t\tSWP_NOACTIVATE | SWP_NOMOVE);\n\n\t\tstd::memcpy(&frame->data, rect, sizeof(ERect));\n\n\t\ttimerId_ = SetTimer(hwnd_, 0, 100, nullptr);\n\n\t\tHANDLE handle = GetPropA(hwnd_, \"__wine_x11_whole_window\");\n\t\tframe->value = reinterpret_cast<intptr_t>(handle);\n\n\t\tisEditorOpen_ = true;\n\t\tbreak; }\n\n\tcase effEditGetRect: {\n\t\tERect* rect = nullptr;\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, &rect, frame->opt);\n\n\t\tstd::memcpy(&frame->data, rect, sizeof(ERect));\n\t\tbreak; }\n\n\tcase effCanDo:\n\tcase effGetVendorString:\n\tcase effGetProductString:\n\tcase effGetParamName:\n\tcase effGetParamLabel:\n\tcase effGetProgramNameIndexed:\n\tcase effGetParamDisplay:\n\tcase effGetProgramName:\n\tcase effSetProgramName:\n\tcase effGetParameterProperties:\n\tcase effGetOutputProperties:\n\tcase effGetInputProperties:\n\tcase effGetMidiKeyName:\n\tcase effBeginLoadBank:\n\tcase effBeginLoadProgram:\n\tcase effGetEffectName:\n\tcase effShellGetNextPlugin:\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tframe->value, frame->data, frame->opt);\n\t\tbreak;\n\n\tcase effProcessEvents: {\n\t\tVstEvent* events = reinterpret_cast<VstEvent*>(frame->data);\n\t\tevents_.reload(frame->index, events);\n\t\tVstEvents* e = events_.events();\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, 0,\n\t\t\t\tframe->value, e, frame->opt);\n\t\tbreak; }\n\n\tcase effGetChunk: {\n\t\tsize_t blockSize = frame->value;\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index, 0,\n\t\t\t&data_, frame->opt);\n\n\t\tdataLength_ = frame->value;\n\n\t\tDEBUG(\"effGetChunk: %d\", dataLength_);\n\t\tif(dataLength_ == 0)\n\t\t\tbreak;\n\n\t\tframe->index = dataLength_ < blockSize ? dataLength_ : blockSize;\n\t\tstd::copy(data_, data_ + frame->index, frame->data);\n\t\tdata_ += frame->index;\n\t\tdataLength_ -= frame->index;\n\t\tbreak; }\n\n\tcase effSetChunk: {\n\t\tDEBUG(\"effSetChunk: %d bytes\", chunk_.size());\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index,\n\t\t\t\tchunk_.size(), chunk_.data(), frame->opt);\n\n\t\tchunk_.clear();\n\t\tbreak; }\n\n\tcase effSetSpeakerArrangement: {\n\t\tu8* data = frame->data;\n\n\t\tintptr_t value = reinterpret_cast<intptr_t>(data);\n\t\tvoid* ptr = data + sizeof(VstSpeakerArrangement);\n\n\t\tframe->value = effect_->dispatcher(effect_, frame->opcode, frame->index, value,\n\t\t\tptr, frame->opt);\n\n\t\tbreak; }\n\n\tdefault:\n\t\tERROR(\"Unhandled dispatch event: %s\", kDispatchEvents[frame->opcode]);\n\t}\n\n\treturn true;\n}\n\n\nvoid Host::handleGetParameter()\n{\n//\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\tframe->opt = effect_->getParameter(effect_, frame->index);\n}\n\n\nvoid Host::handleSetParameter()\n{\n//\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\teffect_->setParameter(effect_, frame->index, frame->opt);\n}\n\n\nvoid Host::handleProcessSingle()\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\n\tfloat* inputs[effect_->numInputs];\n\tfloat* outputs[effect_->numOutputs];\n\ti32 sampleCount = frame->value;\n\tfloat* data = reinterpret_cast<float*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numInputs; ++i)\n\t\tinputs[i] = data + i * sampleCount;\n\n\tfor(int i = 0; i < effect_->numOutputs; ++i)\n\t\toutputs[i] = data + i * sampleCount;\n\n\teffect_->processReplacing(effect_, inputs, outputs, sampleCount);\n}\n\n\nvoid Host::handleProcessDouble()\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\n\tdouble* inputs[effect_->numInputs];\n\tdouble* outputs[effect_->numOutputs];\n\ti32 sampleCount = frame->value;\n\tdouble* data = reinterpret_cast<double*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numInputs; ++i)\n\t\tinputs[i] = data + i * sampleCount;\n\n\tfor(int i = 0; i < effect_->numOutputs; ++i)\n\t\toutputs[i] = data + i * sampleCount;\n\n\teffect_->processDoubleReplacing(effect_, inputs, outputs, sampleCount);\n}\n\n\nintptr_t Host::audioMaster(i32 opcode, i32 index, intptr_t value, void* ptr, float opt)\n{\n\tif(opcode != audioMasterGetTime && opcode != audioMasterIdle)\n\t\tFLOOD(\"handleAudioMaster(%s)\", kAudioMasterEvents[opcode]);\n\n\tDataFrame* frame = callbackPort_.frame<DataFrame>();\n\tframe->command = Command::AudioMaster;\n\tframe->opcode  = opcode;\n\tframe->index   = index;\n\tframe->value   = value;\n\tframe->opt     = opt;\n\n\tswitch(opcode) {\n\tcase audioMasterVersion:\n\tcase __audioMasterWantMidiDeprecated:\n\tcase audioMasterAutomate:\n\tcase audioMasterBeginEdit:\n\tcase audioMasterEndEdit:\n\tcase audioMasterGetVendorVersion:\n\tcase audioMasterSizeWindow:\n\tcase audioMasterGetInputLatency:\n\tcase audioMasterGetOutputLatency:\n\tcase audioMasterGetCurrentProcessLevel:\n\tcase audioMasterGetAutomationState:\n\tcase audioMasterCurrentId:\n\tcase audioMasterGetSampleRate:\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\t\treturn frame->value;\n\n\tcase audioMasterIOChanged: {\n\t\tif(!isInitialized_)\n\t\t\treturn 0;\n\n\t\tPluginInfo* info = reinterpret_cast<PluginInfo*>(frame->data);\n\t\tinfo->flags        = effect_->flags;\n\t\tinfo->programCount = effect_->numPrograms;\n\t\tinfo->paramCount   = effect_->numParams;\n\t\tinfo->inputCount   = effect_->numInputs;\n\t\tinfo->outputCount  = effect_->numOutputs;\n\t\tinfo->initialDelay = effect_->initialDelay;\n\t\tinfo->uniqueId     = effect_->uniqueID;\n\t\tinfo->version      = effect_->version;\n\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\t\treturn frame->value; }\n\n\t// FIXME Passing the audioMasterUpdateDisplay request to the plugin endpoint leads to\n\t// crash (or lock in Renoise) with some plugins (u-he TripleCheese).\n\tcase audioMasterUpdateDisplay:\n//\t\tcallbackPort_.sendRequest();\n//\t\tcallbackPort_.waitResponse();\n\t\treturn 1;\n\n\tcase audioMasterIdle:\n\tcase __audioMasterNeedIdleDeprecated:\n\t\t// There is no need to translate this request to the VST host, because we can\n\t\t// simply call the dispatcher.\n//\t\tif(isEditorOpen_)\n//\t\t\teffect_->dispatcher(effect_, effEditIdle, 0, 0, nullptr, 0.0f);\n\n\t\t// NOTE Currently we run effEditIdle periodically on timer event\n\t\treturn 1;\n\n\tcase audioMasterGetVendorString: {\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\n\t\tif(!frame->value)\n\t\t\treturn 0;\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tstd::strncpy(dest, source, kVstMaxVendorStrLen);\n\t\tdest[kVstMaxVendorStrLen-1] = '\\0';\n\t\treturn frame->value; }\n\n\tcase audioMasterGetProductString: {\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\n\t\tif(!frame->value)\n\t\t\treturn 0;\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tstd::strncpy(dest, source, kVstMaxProductStrLen);\n\t\tdest[kVstMaxProductStrLen-1] = '\\0';\n\t\treturn frame->value; }\n\n\tcase audioMasterCanDo: {\n\t\tconst char* source = static_cast<const char*>(ptr);\n\t\tchar* dest         = reinterpret_cast<char*>(frame->data);\n\t\tsize_t maxLength   = callbackPort_.frameSize() - sizeof(DataFrame);\n\n\t\tstd::strncpy(dest, source, maxLength);\n\t\tdest[maxLength-1] = '\\0';\n\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\t\treturn frame->value; }\n\n\tcase audioMasterGetTime:\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\n\t\tif(!frame->value)\n\t\t\treturn 0;\n\n\t\tstd::memcpy(&timeInfo_, frame->data, sizeof(VstTimeInfo));\n\t\treturn reinterpret_cast<intptr_t>(&timeInfo_);\n\n\tcase audioMasterProcessEvents: {\n\t\tVstEvents* events = static_cast<VstEvents*>(ptr);\n\t\tVstEvent* event = reinterpret_cast<VstEvent*>(frame->data);\n\t\tframe->index = events->numEvents;\n\n\t\tfor(int i = 0; i < events->numEvents; ++i)\n\t\t\tevent[i] = *events->events[i];\n\n\t\tcallbackPort_.sendRequest();\n\t\tcallbackPort_.waitResponse();\n\t\treturn frame->value; }\n\t}\n\n\tERROR(\"Unhandled audio master request: %s\", kAudioMasterEvents[opcode]);\n\treturn 0;\n}\n\n\nintptr_t VSTCALLBACK Host::audioMasterProc(AEffect* effect, i32 opcode, i32 index,\n\t\tintptr_t value, void* ptr, float opt)\n{\n\tUNUSED(effect);\n\n\tEnterCriticalSection(&self_->cs_);\n\tintptr_t result = self_->audioMaster(opcode, index, value, ptr, opt);\n\n\tLeaveCriticalSection(&self_->cs_);\n\treturn result;\n}\n\n\nDWORD CALLBACK Host::audioThreadProc(void* param)\n{\n\tTRACE(\"Audio thread started\");\n\n\tHost* host = static_cast<Host*>(param);\n\thost->audioThread();\n\n\tTRACE(\"Audio thread terminated\");\n\treturn 0;\n}\n\n\nLRESULT CALLBACK Host::windowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\n{\n\tif(hwnd == self_->hwnd_) {\n\t\tswitch(message) {\n\t\tcase WM_CLOSE:\n\t\t\tDEBUG(\"Received WM_CLOSE event\");\n\t\t\tShowWindow(hwnd, SW_HIDE);\n\t\t\treturn 0;\n\n\t\tcase WM_PARENTNOTIFY:\n\t\t\tif(wParam == WM_CREATE) {\n\t\t\t\tself_->childHwnd_ = reinterpret_cast<HWND>(lParam);\n\n\t\t\t\tLONG_PTR value = SetWindowLongPtr(self_->childHwnd_, GWLP_WNDPROC,\n\t\t\t\t\t\treinterpret_cast<LONG_PTR>(windowProc));\n\n\t\t\t\tself_->oldWndProc_ = reinterpret_cast<WNDPROC>(value);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_TIMER:\n\t\t\tself_->effect_->dispatcher(self_->effect_, effEditIdle, 0, 0, nullptr, 0.0f);\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if(self_->childHwnd_ && hwnd == self_->childHwnd_) {\n\t\treturn CallWindowProc(self_->oldWndProc_, hwnd, message, wParam, lParam);\n\t}\n\n\treturn DefWindowProc(hwnd, message, wParam, lParam);\n}\n\n\n} //namespace Airwave\n"
  },
  {
    "path": "src/host/host.h",
    "content": "#ifndef HOST_HOST_H\n#define HOST_HOST_H\n\n#include <atomic>\n#include <string>\n#include <vector>\n#include <wine/windows/windows.h>\n#include \"common/config.h\"\n#include \"common/dataport.h\"\n#include \"common/event.h\"\n#include \"common/vst24.h\"\n#include \"common/vsteventkeeper.h\"\n\n\nnamespace Airwave {\n\n\nstruct DataFrame;\n\n\nclass Host {\npublic:\n\tHost();\n\t~Host();\n\n\tbool initialize(const char* fileName, int portId);\n\tbool processRequest();\n\nprivate:\n\tbool isInitialized_;\n\tHMODULE module_;\n\tHWND hwnd_;\n\tCRITICAL_SECTION cs_;\n\tUINT_PTR timerId_;\n\tAEffect* effect_;\n\tVstTimeInfo timeInfo_;\n\tVstEventKeeper events_;\n\n\tu8* data_;\n\tsize_t dataLength_;\n\tstd::vector<u8> chunk_;\n\n\tDataPort controlPort_;\n\tDataPort callbackPort_;\n\tDataPort audioPort_;\n\n\tEvent condition_;\n\n\tHANDLE audioThread_;\n\tstd::atomic_flag runAudio_;\n\n\tbool isEditorOpen_;\n\n\tWNDPROC oldWndProc_;\n\tHWND childHwnd_;\n\n\tstatic Host* self_;\n\tstatic constexpr const char* kWindowClass = PROJECT_NAME;\n\n\tstd::string errorString() const;\n\tvoid destroyEditorWindow();\n\n\tvoid audioThread();\n\n\tvoid handleGetDataBlock(DataFrame* frame);\n\tvoid handleSetDataBlock(DataFrame* frame);\n\n\tbool handleDispatch(DataFrame* frame);\n\tvoid handleGetParameter();\n\tvoid handleSetParameter();\n\tvoid handleProcessSingle();\n\tvoid handleProcessDouble();\n\n\tintptr_t audioMaster(i32 opcode, i32 index, intptr_t value, void* ptr, float opt);\n\n\tstatic intptr_t VSTCALLBACK audioMasterProc(AEffect* effect, i32 opcode, i32 index,\n\t\t\tintptr_t value, void* ptr, float opt);\n\n\tstatic DWORD CALLBACK audioThreadProc(void* param);\n\n\tstatic LRESULT CALLBACK windowProc(HWND hwnd, UINT message, WPARAM wParam,\n\t\t\tLPARAM lParam);\n};\n\n\n} // namespace Airwave\n\n\n#endif // HOST_HOST_H\n"
  },
  {
    "path": "src/host/main.cpp",
    "content": "#include <cstdlib>\n#include \"host.h\"\n#include \"common/config.h\"\n#include \"common/filesystem.h\"\n#include \"common/logger.h\"\n\n\nusing namespace Airwave;\n\n\nint __cdecl main(int argc, const char* argv[])\n{\n\tif(argc != 5) {\n\t\tfprintf(stderr, \"Airwave host endpoint, version \" VERSION_STRING);\n\t\tfprintf(stderr, \"error: wrong number of arguments: %d\", argc);\n\t\tfprintf(stderr, \"usage: %s <vst path> <port id> <log level> <log socket path>\",\n\t\t\t\targv[0]);\n\n\t\tloggerFree();\n\t\treturn -1;\n\t}\n\n\tloggerInit(argv[4], HOST_BASENAME);\n\tloggerSetSenderId(FileSystem::baseName(argv[1]));\n\n\tLogLevel level = static_cast<LogLevel>(atoi(argv[3]));\n\tif(level < LogLevel::kQuiet || level > LogLevel::kFlood) {\n\t\tloggerSetLogLevel(LogLevel::kTrace);\n\t\tERROR(\"Invalid log level '%d', using log level 'trace' instead\", argc);\n\t}\n\telse {\n\t\tloggerSetLogLevel(level);\n\t}\n\n\tTRACE(\"Initializing host endpoint %s\", VERSION_STRING);\n\n\tHost* host = new Host;\n\tif(!host->initialize(argv[1], atoi(argv[2]))) {\n\t\tERROR(\"Unable to initialize host endpoint\");\n\t\tloggerFree();\n\t\treturn -2;\n\t}\n\n\tTRACE(\"Host endpoint is initialized\");\n\n\twhile(host->processRequest()) {\n\t\tMSG message;\n\n\t\twhile(PeekMessage(&message, 0, 0, 0, PM_REMOVE)) {\n\t\t\tTranslateMessage(&message);\n\t\t\tDispatchMessage(&message);\n\t\t}\n\t}\n\n\tTRACE(\"Terminating the host endpoint...\");\n\tdelete host;\n\n\tTRACE(\"Host endpoint terminated\");\n\tloggerFree();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/manager/CMakeLists.txt",
    "content": "set(TARGET_NAME ${PROJECT_NAME}-manager)\n\nproject(${TARGET_NAME})\n\nfind_package(Qt5Widgets REQUIRED)\nfind_package(Qt5Network REQUIRED)\nfind_package(LibMagic REQUIRED)\n\ninclude_directories(\n\t${CMAKE_CURRENT_BINARY_DIR}\n\t${CMAKE_CURRENT_SOURCE_DIR}\n\t${LIBMAGIC_INCLUDE_DIR}\n)\n\n# Instruct CMake to run moc automatically when needed\nset(CMAKE_AUTOMOC ON)\n\nset(SOURCES\n\tmain.cpp\n\t../common/filesystem.cpp\n\t../common/json.cpp\n\t../common/moduleinfo.cpp\n\t../common/storage.cpp\n\tcore/application.cpp\n\tcore/logsocket.cpp\n\tcore/singleapplication.cpp\n\tforms/filedialog.cpp\n\tforms/folderdialog.cpp\n\tforms/linkdialog.cpp\n\tforms/loaderdialog.cpp\n\tforms/mainform.cpp\n\tforms/prefixdialog.cpp\n\tforms/settingsdialog.cpp\n\tmodels/directorymodel.cpp\n\tmodels/linksmodel.cpp\n\tmodels/loadersmodel.cpp\n\tmodels/prefixesmodel.cpp\n\twidgets/lineedit.cpp\n\twidgets/linksview.cpp\n\twidgets/logview.cpp\n\twidgets/directoryview.cpp\n\twidgets/loadersview.cpp\n\twidgets/nofocusdelegate.cpp\n\twidgets/prefixesview.cpp\n\twidgets/separatorlabel.cpp\n)\n\n# Help the stupid IDE to consider following headers as a part of the project\nset(HEADERS\n\t../common/config.h\n\t../common/types.h\n\tmodels/generictreemodel.h\n\twidgets/generictreeview.h\n)\n\n\nset(RESOURCES\n\tresources/resources.qrc\n)\n\nset(LIBRARIES\n\tQt5::Widgets\n\tQt5::Network\n\t${LIBMAGIC_LIBRARY}\n)\n\nqt5_add_resources(RCC_SOURCES ${RESOURCES})\n\n# Set target\nadd_executable(${TARGET_NAME} ${SOURCES} ${HEADERS} ${RCC_SOURCES})\n\n# Use the Widgets module from Qt 5\ntarget_link_libraries(${TARGET_NAME} ${LIBRARIES})\n\n# Generate desktop file\nconfigure_file(\n\t${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}.desktop.in\n\t${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.desktop\n)\n\n\ninstall(TARGETS ${TARGET_NAME} RUNTIME DESTINATION bin)\ninstall(FILES resources/${TARGET_NAME}.png DESTINATION share/icons/hicolor/48x48/apps)\n\ninstall(FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.desktop\n\t\tDESTINATION share/applications)\n"
  },
  {
    "path": "src/manager/airwave-manager.desktop.in",
    "content": "[Desktop Entry]\nType=Application\nVersion=1.0\nName=Airwave manager\nComment=A tool for managing the Airwave VST bridge\nExec=@TARGET_NAME@\nIcon=@TARGET_NAME@\nTerminal=false\nCategories=Multimedia;AudioVideo;Player;Recorder;\n"
  },
  {
    "path": "src/manager/core/application.cpp",
    "content": "#include <QDir>\n#include \"application.h\"\n#include \"common/config.h\"\n#include \"common/storage.h\"\n#include \"models/linksmodel.h\"\n#include \"models/loadersmodel.h\"\n#include \"models/prefixesmodel.h\"\n\n\nApplication::Application(int& argc, char** argv) :\n\tSingleApplication(argc, argv),\n\tstorage_(new Airwave::Storage),\n\tlinks_(new LinksModel(this)),\n\tloaders_(new LoadersModel(this)),\n\tprefixes_(new PrefixesModel(this))\n{\n}\n\n\nApplication::~Application()\n{\n\tdelete prefixes_;\n\tdelete loaders_;\n\tdelete links_;\n\tdelete storage_;\n}\n\n\nLogSocket* Application::logSocket()\n{\n\treturn &logSocket_;\n}\n\n\nStorage* Application::storage() const\n{\n\treturn storage_;\n}\n\n\nLinksModel* Application::links() const\n{\n\treturn links_;\n}\n\n\nLoadersModel* Application::loaders() const\n{\n\treturn loaders_;\n}\n\n\nPrefixesModel* Application::prefixes() const\n{\n\treturn prefixes_;\n}\n\n\nQStringList Application::checkMissingBinaries(const QString& path) const\n{\n\tQString binPath = path;\n\n\tif(binPath.isEmpty())\n\t\tbinPath = QString::fromStdString(storage_->binariesPath());\n\n\tQDir binDir(binPath);\n\n\tQStringList fileList;\n\tfileList += HOST_BASENAME \"-32.exe\";\n\tfileList += PLUGIN_BASENAME \".so\";\n#ifdef PLATFORM_64BIT\n\tfileList += HOST_BASENAME \"-64.exe\";\n#endif\n\n\tQStringList result;\n\n\tforeach(const QString& fileName, fileList) {\n\t\tif(!binDir.exists(fileName))\n\t\t\tresult += fileName;\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "src/manager/core/application.h",
    "content": "#ifndef CORE_APPLICATION_H\n#define CORE_APPLICATION_H\n\n#include \"core/logsocket.h\"\n#include \"core/singleapplication.h\"\n\n#ifdef qApp\n#undef qApp\n#define qApp (static_cast<Application*>(QApplication::instance()))\n#endif\n\n\nclass LinksModel;\nclass LoadersModel;\nclass PrefixesModel;\n\nnamespace Airwave {\nclass Storage;\n}\n\n\nclass Application : public SingleApplication {\npublic:\n\tApplication(int& argc, char** argv);\n\t~Application();\n\n\tLogSocket* logSocket();\n\tAirwave::Storage* storage() const;\n\tLinksModel* links() const;\n\tLoadersModel* loaders() const;\n\tPrefixesModel* prefixes() const;\n\n\tQStringList checkMissingBinaries(const QString& path = QString()) const;\n\nprivate:\n\tLogSocket logSocket_;\n\tAirwave::Storage* storage_;\n\tLinksModel* links_;\n\tLoadersModel* loaders_;\n\tPrefixesModel* prefixes_;\n};\n\n\n#endif // CORE_APPLICATION_H\n"
  },
  {
    "path": "src/manager/core/logsocket.cpp",
    "content": "#include \"logsocket.h\"\n\n#include <cerrno>\n#include <cstring>\n#include <unistd.h>\n#include <linux/un.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <QByteArray>\n\n\nLogSocket::LogSocket(QObject* parent) :\n\tQObject(parent),\n\tfd_(-1),\n\tnotifier_(nullptr)\n{\n}\n\n\nLogSocket::~LogSocket()\n{\n\tclose();\n}\n\n\nQString LogSocket::id() const\n{\n\treturn id_;\n}\n\n\nbool LogSocket::listen(const QString& id)\n{\n\tfd_ = socket(AF_UNIX, SOCK_DGRAM, 0);\n\tif(fd_ < 0) {\n\t\tqDebug(\"Unable to create socket: %s\", strerror(errno));\n\t\treturn false;\n\t}\n\n\tunlink(id.toUtf8().constData());\n\n\tstruct sockaddr_un address;\n\tstd::memset(&address, 0, sizeof(address));\n\n\taddress.sun_family = AF_UNIX;\n\tstd::snprintf(address.sun_path, UNIX_PATH_MAX, id.toUtf8().constData());\n\n\tif(bind(fd_, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0) {\n\t\tqDebug(\"Unable to bind socket: %s\", strerror(errno));\n\t\t::close(fd_);\n\t\tfd_ = -1;\n\t\treturn false;\n\t}\n\n\tid_ = id;\n\tnotifier_ = new QSocketNotifier(fd_, QSocketNotifier::Read);\n\tconnect(notifier_, SIGNAL(activated(int)), SLOT(handleDatagram()));\n\treturn true;\n}\n\n\nvoid LogSocket::close()\n{\n\tif(fd_ != -1) {\n\t\tdelete notifier_;\n\t\t::close(fd_);\n\t\tunlink(id_.toUtf8().constData());\n\t}\n}\n\n\nvoid LogSocket::handleDatagram()\n{\n\tsize_t length = 0;\n\tif(ioctl(fd_, FIONREAD, &length) < 0) {\n\t\tqDebug(\"ioctl() call failed: %s\", strerror(errno));\n\t\treturn;\n\t}\n\n\tchar* buffer = new char[length];\n\n\tif(recvfrom(fd_, buffer, length, 0, nullptr, nullptr) < 0) {\n\t\tqDebug(\"recvfrom() call failed: %s\", strerror(errno));\n\t\tdelete [] buffer;\n\t\treturn;\n\t}\n\n\tquint64* timeStamp = reinterpret_cast<quint64*>(buffer);\n\tconst char* sender = buffer + sizeof(quint64);\n\n\tconst char* message;\n\tmessage = static_cast<const char*>(std::memchr(sender, 0x01, length));\n\tif(!message) {\n\t\tqDebug(\"Discarding invalid datagram.\");\n\t\tdelete [] buffer;\n\t\treturn;\n\t}\n\n\tQString senderId;\n\tsenderId = QString::fromUtf8(sender, message - sender);\n\temit newMessage(*timeStamp, senderId, message + 1);\n\tdelete [] buffer;\n}\n"
  },
  {
    "path": "src/manager/core/logsocket.h",
    "content": "#ifndef CORE_LOGSOCKET_H\n#define CORE_LOGSOCKET_H\n\n#include <QSocketNotifier>\n#include <QString>\n\n\nclass LogSocket : public QObject {\n\tQ_OBJECT\npublic:\n\tLogSocket(QObject* parent = nullptr);\n\t~LogSocket();\n\n\tQString id() const;\n\tbool listen(const QString& id);\n\tvoid close();\n\nsignals:\n\tvoid newMessage(quint64 time, const QString& sender, const QString& text);\n\nprivate:\n\tint fd_;\n\tQSocketNotifier* notifier_;\n\tQString id_;\n\nprivate slots:\n\tvoid handleDatagram();\n};\n\n\n#endif // CORE_LOGSOCKET_H\n"
  },
  {
    "path": "src/manager/core/singleapplication.cpp",
    "content": "#include \"singleapplication.h\"\n\n#include <QLocalServer>\n#include <QLocalSocket>\n#include <QWidget>\n\n\nSingleApplication::SingleApplication(int& argc, char** argv) :\n\tQApplication(argc, argv),\n\tserver_(nullptr),\n\twindow_(nullptr),\n\tisActivateOnMessage_(false)\n{\n\tQString id = QApplication::applicationFilePath().replace('/', '.');\n\tinitialize(id);\n}\n\n\nSingleApplication::SingleApplication(const QString& id, int& argc, char** argv) :\n\tQApplication(argc, argv),\n\tserver_(nullptr),\n\twindow_(nullptr),\n\tisActivateOnMessage_(false)\n{\n\tinitialize(id);\n}\n\n\nSingleApplication::~SingleApplication()\n{\n\tfinalize();\n}\n\n\nbool SingleApplication::isRunning() const\n{\n\treturn !server_;\n}\n\n\nQWidget* SingleApplication::activationWindow() const\n{\n\treturn window_;\n}\n\n\nvoid SingleApplication::setActivationWindow(QWidget* window)\n{\n\twindow_ = window;\n}\n\n\nbool SingleApplication::isActivateOnMessage() const\n{\n\treturn isActivateOnMessage_;\n}\n\n\nvoid SingleApplication::setActivateOnMessage(bool enable)\n{\n\tisActivateOnMessage_ = enable;\n}\n\n\nvoid SingleApplication::activateWindow()\n{\n\tif(window_) {\n\t\twindow_->show();\n\t\twindow_->activateWindow();\n\t}\n}\n\n\nbool SingleApplication::sendMessage(const QString& message)\n{\n\tQLocalSocket socket;\n\tsocket.connectToServer(id_);\n\tif(socket.waitForConnected(kConnectTimeout_)) {\n\t\tsocket.write(message.toUtf8());\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nvoid SingleApplication::initialize(const QString& id)\n{\n\tid_ = id;\n\n\tQLocalSocket socket;\n\tsocket.connectToServer(id);\n\tif(socket.waitForConnected(kConnectTimeout_))\n\t\treturn;\n\n\tserver_ = new QLocalServer(this);\n\tconnect(server_, SIGNAL(newConnection()), SLOT(processClientConnection()));\n\n\tif(!server_->listen(id)) {\n\t\tif(server_->serverError() == QAbstractSocket::AddressInUseError) {\n\t\t\tQLocalServer::removeServer(id);\n\n\t\t\tif(!server_->listen(id))\n\t\t\t\tfinalize();\n\t\t}\n\t}\n}\n\n\nvoid SingleApplication::finalize()\n{\n\tif(server_) {\n\t\tdelete server_;\n\t\tserver_ = nullptr;\n\t\tid_.clear();\n\t}\n}\n\n\nvoid SingleApplication::processClientConnection()\n{\n\tQLocalSocket* socket = server_->nextPendingConnection();\n\tif(socket) {\n\t\tQString message;\n\t\tif(socket->waitForReadyRead(kReadTimeout_))\n\t\t\tmessage = socket->readAll();\n\n\t\tdelete socket;\n\n\t\tif(!message.isEmpty()) {\n\t\t\tif(isActivateOnMessage_)\n\t\t\t\tactivateWindow();\n\n\t\t\temit messageReceived(message);\n\t\t}\n\t\telse if(!isActivateOnMessage_) {\n\t\t\tactivateWindow();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/manager/core/singleapplication.h",
    "content": "#ifndef CORE_SINGLEAPPLICATION_H\n#define CORE_SINGLEAPPLICATION_H\n\n#include <QApplication>\n\n#ifdef qApp\n#undef qApp\n#define qApp (static_cast<SingleApplication*>(QApplication::instance()))\n#endif\n\nclass QLocalServer;\n\n\nclass SingleApplication : public QApplication {\n\tQ_OBJECT\npublic:\n\tSingleApplication(int& argc, char** argv);\n\tSingleApplication(const QString& id, int& argc, char** argv);\n\n\t~SingleApplication();\n\n\tbool isRunning() const;\n\n\tQWidget* activationWindow() const;\n\tvoid setActivationWindow(QWidget* window);\n\n\tbool isActivateOnMessage() const;\n\tvoid setActivateOnMessage(bool enable);\n\npublic slots:\n\tvoid activateWindow();\n\tbool sendMessage(const QString& message);\n\nsignals:\n\tvoid messageReceived(const QString& message);\n\nprivate:\n\tstatic const int kConnectTimeout_ = 500;\n\tstatic const int kReadTimeout_ = 1000;\n\n\tQString id_;\n\tQLocalServer* server_;\n\tQWidget* window_;\n\tbool isActivateOnMessage_;\n\n\tvoid initialize(const QString& id);\n\tvoid finalize();\n\nprivate slots:\n\tvoid processClientConnection();\n};\n\n\n#endif // CORE_SINGLEAPPLICATION_H\n"
  },
  {
    "path": "src/manager/forms/filedialog.cpp",
    "content": "#include \"filedialog.h\"\n\n#include <QIcon>\n#include <QComboBox>\n#include <QGridLayout>\n#include <QHeaderView>\n#include <QLabel>\n#include <QPushButton>\n#include <QToolButton>\n#include \"forms/folderdialog.h\"\n#include \"models/directorymodel.h\"\n#include \"widgets/directoryview.h\"\n#include \"widgets/lineedit.h\"\n\n\nFileDialog::FileDialog(DialogMode mode, QWidget* parent) :\n\tQDialog(parent),\n\tdialogMode_(mode),\n\tacceptMode_(kAcceptFile)\n{\n\tsetupUi();\n\tsetRootDirectory(QDir::rootPath());\n\tsetDirectory(QDir::homePath());\n}\n\n\nvoid FileDialog::setupUi()\n{\n\tresize(600, 450);\n\n\tQLabel* label = new QLabel(\"Directory: \");\n\tcurrentDirEdit_ = new LineEdit;\n\tcurrentDirEdit_->setReadOnly(true);\n\tcurrentDirEdit_->setStyleSheet(\"QLineEdit { border: 1px solid #AAAAAA; }\");\n\n\tgoUpButton_ = new QToolButton;\n\tgoUpButton_->setIconSize(QSize(20, 20));\n\tgoUpButton_->setIcon(QIcon(\":/go_up.png\"));\n\tgoUpButton_->setToolTip(\"Go to the upper folder\");\n\tgoUpButton_->setAutoRaise(true);\n\tconnect(goUpButton_, SIGNAL(clicked()), SLOT(goUp()));\n\n\tcreateDirButton_ = new QToolButton;\n\tcreateDirButton_->setIconSize(QSize(20, 20));\n\tcreateDirButton_->setIcon(QIcon(\":/create_folder.png\"));\n\tcreateDirButton_->setToolTip(\"Create new folder\");\n\tcreateDirButton_->setAutoRaise(true);\n\tconnect(createDirButton_, SIGNAL(clicked()), SLOT(onCreateDirButtonClicked()));\n\n\ttoggleHiddenButton_ = new QToolButton;\n\ttoggleHiddenButton_->setIconSize(QSize(20, 20));\n\ttoggleHiddenButton_->setIcon(QIcon(\":/show.png\"));\n\ttoggleHiddenButton_->setToolTip(\"Show hidden files\");\n\ttoggleHiddenButton_->setAutoRaise(true);\n\ttoggleHiddenButton_->setCheckable(true);\n\tconnect(toggleHiddenButton_, SIGNAL(toggled(bool)), SLOT(setShowHidden(bool)));\n\n\tQHBoxLayout* currentDirLayout = new QHBoxLayout;\n\tcurrentDirLayout->setContentsMargins(2, 0, 2, 0);\n\tcurrentDirLayout->setSpacing(2);\n\tcurrentDirLayout->addWidget(label);\n\tcurrentDirLayout->addWidget(currentDirEdit_);\n\tcurrentDirLayout->addWidget(goUpButton_);\n\tcurrentDirLayout->addWidget(createDirButton_);\n\tcurrentDirLayout->addWidget(toggleHiddenButton_);\n\n\tmodel_ = new DirectoryModel;\n\n\tview_ = new DirectoryView;\n\tview_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\tview_->setModel(model_);\n\n\tconnect(view_,\n\t\t\tSIGNAL(currentItemChanged(DirectoryItem*,DirectoryItem*)),\n\t\t\tSLOT(onItemChanged(DirectoryItem*)));\n\n\tconnect(view_,\n\t\t\tSIGNAL(itemDoubleClicked(DirectoryItem*)),\n\t\t\tSLOT(onItemDoubleClicked(DirectoryItem*)));\n\n\tQHeaderView* header = view_->header();\n\theader->setStretchLastSection(false);\n\theader->setSectionResizeMode(0, QHeaderView::Stretch);\n\n\tnameEdit_ = new LineEdit;\n\tconnect(nameEdit_, SIGNAL(textChanged(QString)), SLOT(onNameEditChanged(QString)));\n\n\tif(dialogMode_ == kOpenDialog) {\n\t\tactionButton_ = new QPushButton(QIcon(\":/open.png\"), \"Open\");\n\t}\n\telse {\n\t\tactionButton_ = new QPushButton(QIcon(\":/save.png\"), \"Save\");\n\t}\n\n\tactionButton_->setIconSize(QSize(16, 16));\n\tconnect(actionButton_, SIGNAL(clicked()), SLOT(accept()));\n\n\tcancelButton_ = new QPushButton(QIcon(\":/remove.png\"), \"Cancel\");\n\tcancelButton_->setIconSize(QSize(16, 16));\n\tconnect(cancelButton_, SIGNAL(clicked()), SLOT(reject()));\n\n\tQGridLayout* bottomLayout = new QGridLayout;\n\tbottomLayout->setContentsMargins(1, 4, 1, 4);\n\tbottomLayout->setSpacing(5);\n\tbottomLayout->addWidget(new QLabel(\"File name: \"), 0, 0);\n\tbottomLayout->addWidget(nameEdit_, 0, 1);\n\tbottomLayout->addWidget(actionButton_, 0, 2);\n\tbottomLayout->addWidget(cancelButton_, 1, 2);\n\n\tQVBoxLayout* mainLayout = new QVBoxLayout;\n\tmainLayout->setMargin(1);\n\tmainLayout->setSpacing(1);\n\tmainLayout->addLayout(currentDirLayout);\n\tmainLayout->addWidget(view_);\n\tmainLayout->addLayout(bottomLayout);\n\n\tsetLayout(mainLayout);\n\n\tonNameEditChanged(QString());\n}\n\n\nFileDialog::AcceptMode FileDialog::acceptMode() const\n{\n\treturn acceptMode_;\n}\n\n\nvoid FileDialog::setAcceptMode(FileDialog::AcceptMode mode)\n{\n\tacceptMode_ = mode;\n\tmodel_->setFilesEnabled(mode == kAcceptFile || mode == kAcceptExistingFile);\n}\n\n\nQDir::Filters FileDialog::filters() const\n{\n\treturn model_->filters();\n}\n\n\nvoid FileDialog::setFilter(QDir::Filters filters)\n{\n\tmodel_->setFilters(filters);\n}\n\n\nQStringList FileDialog::nameFilters() const\n{\n\treturn model_->nameFilters();\n}\n\n\nvoid FileDialog::setNameFilter(const QString& filter)\n{\n\tmodel_->setNameFilters(QStringList() << filter);\n}\n\n\nvoid FileDialog::setNameFilters(const QStringList& filters)\n{\n\tmodel_->setNameFilters(filters);\n}\n\n\nQString FileDialog::rootDirectory() const\n{\n\treturn rootPath_;\n}\n\n\nvoid FileDialog::setRootDirectory(const QString& path)\n{\n\tQDir dir(path);\n\tif(!dir.exists())\n\t\treturn;\n\n\trootPath_ = path;\n\n\tif(!model_->directory().startsWith(path))\n\t\tmodel_->setDirectory(path);\n}\n\n\nQString FileDialog::directory() const\n{\n\treturn model_->directory();\n}\n\n\nvoid FileDialog::setDirectory(const QString& path)\n{\n\tif(!path.startsWith(rootPath_))\n\t\treturn;\n\n\tif(QFileInfo::exists(path)) {\n\t\tmodel_->setDirectory(path);\n\t\tgoUpButton_->setEnabled(path != rootPath_);\n\t\tcurrentDirEdit_->setText(path);\n\t}\n}\n\n\nvoid FileDialog::goUp()\n{\n//\tif(rootPath_ == model_->directory())\n//\t\treturn;\n\n\tQDir dir(model_->directory() + \"/..\");\n\tsetDirectory(dir.absolutePath());\n}\n\n\nbool FileDialog::showHidden() const\n{\n\treturn model_->filters() & QDir::Hidden;\n}\n\n\nvoid FileDialog::setShowHidden(bool enable)\n{\n\tQDir::Filters filters = model_->filters();\n\n\tif(enable) {\n\t\tfilters |= QDir::Hidden;\n\t}\n\telse {\n\t\tfilters &= ~QDir::Hidden;\n\t}\n\n\tmodel_->setFilters(filters);\n}\n\n\nvoid FileDialog::accept()\n{\n\tif(!nameEdit_->text().contains('.'))\n\t\tnameEdit_->setText(nameEdit_->text() + suffix_);\n\n\tselectedPath_ = model_->directory() + '/' + nameEdit_->text();\n\tQDialog::accept();\n}\n\n\nint FileDialog::exec()\n{\n\tselectedPath_.clear();\n\treturn QDialog::exec();\n}\n\n\nQString FileDialog::selectedPath() const\n{\n\treturn selectedPath_;\n}\n\n\nQString FileDialog::selectedName() const\n{\n\tDirectoryItem* item = view_->currentItem();\n\tif(!item)\n\t\treturn QString();\n\n\treturn item->name();\n}\n\n\nQString FileDialog::defaultSuffix() const\n{\n\treturn suffix_;\n}\n\n\nvoid FileDialog::setDefaultSuffix(const QString& suffix)\n{\n\tif(suffix_.startsWith('.')) {\n\t\tsuffix_ = suffix.mid(1);\n\t}\n\telse {\n\t\tsuffix_ = suffix;\n\t}\n}\n\n\nvoid FileDialog::onItemChanged(DirectoryItem* item)\n{\n\tif(!item || item->isDirectory()) {\n\t\tnameEdit_->clear();\n\n\t\tif(acceptMode_ == kAcceptFile || acceptMode_ == kAcceptExistingFile) {\n\t\t\tactionButton_->setEnabled(false);\n\t\t}\n\n\t}\n\telse {\n\t\tnameEdit_->setText(item->name());\n\t\tactionButton_->setEnabled(true);\n\t}\n\n}\n\n\nvoid FileDialog::onItemDoubleClicked(DirectoryItem* item)\n{\n\tif(item->isDirectory()) {\n\t\tsetDirectory(item->fullPath());\n\t}\n\telse if(acceptMode_ == kAcceptFile || acceptMode_ == kAcceptExistingFile) {\n\t\taccept();\n\t}\n}\n\n\nvoid FileDialog::onNameEditChanged(const QString& text)\n{\n\tif(dialogMode_ == kSaveDialog) {\n\t\tactionButton_->setEnabled(!text.isEmpty());\n\t}\n}\n\n\nvoid FileDialog::onCreateDirButtonClicked()\n{\n\tFolderDialog dialog(model_);\n\tdialog.exec();\n}\n"
  },
  {
    "path": "src/manager/forms/filedialog.h",
    "content": "#ifndef FORMS_FILEDIALOG_H\n#define FORMS_FILEDIALOG_H\n\n#include <QDialog>\n#include <QDir>\n\n\nclass QFileSystemModel;\nclass QItemSelection;\nclass QPushButton;\nclass QToolButton;\nclass DirectoryItem;\nclass DirectoryModel;\nclass DirectoryView;\nclass LineEdit;\n\n\nclass FileDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tenum DialogMode {\n\t\tkOpenDialog,\n\t\tkSaveDialog\n\t};\n\n\tenum AcceptMode {\n\t\tkAcceptFile,\n\t\tkAcceptExistingFile,\n\t\tkAcceptDirectory,\n\t\tkAcceptExistingDirectory\n\t};\n\n\tFileDialog(DialogMode mode, QWidget* parent = nullptr);\n\n\tAcceptMode acceptMode() const;\n\tvoid setAcceptMode(AcceptMode mode);\n\n\tQDir::Filters filters() const;\n\tvoid setFilter(QDir::Filters filters);\n\n\tQStringList\tnameFilters() const;\n\tvoid setNameFilter(const QString& filter);\n\tvoid setNameFilters(const QStringList& filters);\n\n\tbool showHidden() const;\n\tQString directory() const;\n\tQString rootDirectory() const;\n\tQString selectedPath() const;\n\tQString selectedName() const;\n\tQString defaultSuffix() const;\n\npublic slots:\n\tvoid setShowHidden(bool enable);\n\tvoid setDirectory(const QString& path);\n\tvoid setRootDirectory(const QString& path);\n\tvoid setDefaultSuffix(const QString& suffix);\n\tvoid accept();\n\tint exec();\n\nprivate:\n\tLineEdit* currentDirEdit_;\n\tQToolButton* goUpButton_;\n\tQToolButton* createDirButton_;\n\tQToolButton* toggleHiddenButton_;\n\n\tDirectoryModel* model_;\n\tDirectoryView* view_;\n\n\tLineEdit* nameEdit_;\n\tQPushButton* actionButton_;\n\n\tQPushButton* cancelButton_;\n\n\tDialogMode dialogMode_;\n\tAcceptMode acceptMode_;\n\n\tQString rootPath_;\n\tQString selectedPath_;\n\tQString suffix_;\n\n\tvoid setupUi();\n\nprivate slots:\n\tvoid goUp();\n\tvoid onItemChanged(DirectoryItem* item);\n\tvoid onItemDoubleClicked(DirectoryItem* item);\n\tvoid onNameEditChanged(const QString& text);\n\tvoid onCreateDirButtonClicked();\n};\n\n\n#endif //FORMS_FILEDIALOG_H\n"
  },
  {
    "path": "src/manager/forms/folderdialog.cpp",
    "content": "#include \"folderdialog.h\"\n\n#include <QDialogButtonBox>\n#include <QGridLayout>\n#include <QLabel>\n#include <QMessageBox>\n#include \"models/directorymodel.h\"\n#include \"widgets/lineedit.h\"\n\n\nFolderDialog::FolderDialog(DirectoryModel* model, QWidget* parent) :\n\tQDialog(parent),\n\tmodel_(model)\n{\n\tsetupUi();\n}\n\n\nvoid FolderDialog::accept()\n{\n\tQString name = nameEdit_->text();\n\n\tQDir dir(model_->directory() + '/' + name);\n\tif(dir.exists()) {\n\t\tQString message = QString(\"Directory '%1' is already exists\").arg(name);\n\t\tQMessageBox::critical(this, \"Error\", message);\n\t}\n\telse {\n\t\tdir.mkpath(\".\");\n\t\tQDialog::accept();\n\t}\n}\n\n\nvoid FolderDialog::setupUi()\n{\n\tsetWindowTitle(\"New folder\");\n\n\tnameEdit_ = new LineEdit;\n\tnameEdit_->setText(\"New folder\");\n\tnameEdit_->selectAll();\n\n\tbuttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tconnect(buttons_, SIGNAL(accepted()), SLOT(accept()));\n\tconnect(buttons_, SIGNAL(rejected()), SLOT(reject()));\n\n\tQGridLayout* layout = new QGridLayout;\n\tlayout->addWidget(new QLabel(\"Folder name:\"), 0, 0, Qt::AlignRight);\n\tlayout->addWidget(nameEdit_, 0, 1);\n\tlayout->addWidget(buttons_, 1, 0, 1, 2);\n\n\tsetLayout(layout);\n}\n"
  },
  {
    "path": "src/manager/forms/folderdialog.h",
    "content": "#ifndef FORMS_FOLDERDIALOG_H\n#define FORMS_FOLDERDIALOG_H\n\n#include <QDialog>\n\n\nclass QDialogButtonBox;\nclass DirectoryModel;\nclass LineEdit;\n\n\nclass FolderDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tFolderDialog(DirectoryModel* model, QWidget* parent = nullptr);\n\npublic slots:\n\tvoid accept();\n\nprivate:\n\tDirectoryModel* model_;\n\tLineEdit* nameEdit_;\n\tQDialogButtonBox* buttons_;\n\n\tvoid setupUi();\n};\n\n\n#endif // FORMS_FOLDERDIALOG_H\n"
  },
  {
    "path": "src/manager/forms/linkdialog.cpp",
    "content": "#include \"linkdialog.h\"\n\n#include <QComboBox>\n#include <QDialogButtonBox>\n#include <QFile>\n#include <QGridLayout>\n#include <QLabel>\n#include <QMessageBox>\n#include <QSettings>\n#include \"common/config.h\"\n#include \"core/application.h\"\n#include \"forms/filedialog.h\"\n#include \"models/linksmodel.h\"\n#include \"models/loadersmodel.h\"\n#include \"models/prefixesmodel.h\"\n#include \"widgets/lineedit.h\"\n\n\nLinkDialog::LinkDialog(QWidget* parent) :\n\tQDialog(parent),\n\titem_(nullptr)\n{\n\tsetupUi();\n}\n\n\nLinkItem* LinkDialog::item() const\n{\n\treturn item_;\n}\n\n\nvoid LinkDialog::setItem(LinkItem* item)\n{\n\titem_ = item;\n\n\tif(item) {\n\t\tlocationEdit_->setText(item->location());\n\n\t\tint index = prefixCombo_->findText(item->prefix());\n\t\tprefixCombo_->setCurrentIndex(index);\n\n\t\tindex = loaderCombo_->findText(item->loader());\n\t\tloaderCombo_->setCurrentIndex(index);\n\n\t\tindex = static_cast<int>(item->logLevel()) + 1;\n\t\tlogLevelCombo_->setCurrentIndex(index);\n\n\t\tnameEdit_->setText(item->name());\n\t\ttargetEdit_->setText(item->target());\n\t}\n\telse {\n\t\tnameEdit_->clear();\n\t\tlocationEdit_->clear();\n\t\ttargetEdit_->clear();\n\n\t\tint index = prefixCombo_->findText(\"default\");\n\t\tprefixCombo_->setCurrentIndex(index);\n\n\t\tindex = loaderCombo_->findText(\"default\");\n\t\tloaderCombo_->setCurrentIndex(index);\n\t}\n}\n\n\nvoid LinkDialog::setupUi()\n{\n\tsetWindowIcon(QIcon(\":/edit_link.png\"));\n\tsetWindowTitle(\"Link properties\");\n\tsetMinimumWidth(500);\n\tresize(600, 180);\n\tsetMinimumHeight(260);\n\n\tloaderCombo_ = new QComboBox;\n\tloaderCombo_->setModel(qApp->loaders());\n\tloaderCombo_->setCurrentIndex(loaderCombo_->findText(\"default\"));\n\n\tprefixCombo_ = new QComboBox;\n\tprefixCombo_->setModel(qApp->prefixes());\n\tprefixCombo_->setCurrentIndex(prefixCombo_->findText(\"default\"));\n\tconnect(prefixCombo_, SIGNAL(currentIndexChanged(int)), SLOT(onPrefixChanged()));\n\n\tlogLevelCombo_ = new QComboBox;\n\tlogLevelCombo_->addItem(QIcon(\":/star.png\"), \"default\");\n\tlogLevelCombo_->addItem(QIcon(\":/mute.png\"), \"quiet\");\n\tlogLevelCombo_->addItem(QIcon(\":/warning.png\"), \"error\");\n\tlogLevelCombo_->addItem(QIcon(\":/trace.png\"), \"trace\");\n\tlogLevelCombo_->addItem(QIcon(\":/bug.png\"), \"debug\");\n\tlogLevelCombo_->addItem(QIcon(\":/scull.png\"), \"flood\");\n\n\ttargetEdit_ = new LineEdit;\n\ttargetEdit_->setButtonEnabled(true);\n\ttargetEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\ttargetEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\ttargetEdit_->setButtonToolTip(\"Browse\");\n\ttargetEdit_->setPrefix(\"${WINEPREFIX}/\");\n\ttargetEdit_->setPrefixColor(Qt::darkGreen);\n\tconnect(targetEdit_, SIGNAL(buttonClicked()), SLOT(browsePlugin()));\n\n\tlocationEdit_ = new LineEdit;\n\tlocationEdit_->setButtonEnabled(true);\n\tlocationEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tlocationEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tlocationEdit_->setButtonToolTip(\"Browse\");\n\n\tQSettings settings;\n\tQString vstPath = settings.value(\"vstPath\", qgetenv(\"VST_PATH\")).toString();\n\tlocationEdit_->setText(vstPath.split(':').first());\n\tconnect(locationEdit_, SIGNAL(buttonClicked()), SLOT(browseLocation()));\n\n\tnameEdit_ = new LineEdit;\n\n\tbuttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tconnect(buttons_, SIGNAL(accepted()), SLOT(accept()));\n\tconnect(buttons_, SIGNAL(rejected()), SLOT(reject()));\n\n\tQGridLayout* mainLayout = new QGridLayout;\n\n\tmainLayout->addWidget(new QLabel(\"WINE loader:\"), 0, 0, Qt::AlignRight);\n\tmainLayout->addWidget(loaderCombo_, 0, 1, 1, 1);\n\n\tmainLayout->addWidget(new QLabel(\"WINE prefix:\"), 1, 0, Qt::AlignRight);\n\tmainLayout->addWidget(prefixCombo_, 1, 1, 1, 1);\n\n\tmainLayout->addWidget(new QLabel(\"VST plugin:\"), 2, 0, Qt::AlignRight);\n\tmainLayout->addWidget(targetEdit_, 2, 1, 1, 2);\n\n\tmainLayout->addWidget(new QLabel(\"Link location:\"), 3, 0, Qt::AlignRight);\n\tmainLayout->addWidget(locationEdit_, 3, 1, 1, 2);\n\n\tmainLayout->addWidget(new QLabel(\"Link name:\"), 4, 0, Qt::AlignRight);\n\tmainLayout->addWidget(nameEdit_, 4, 1, 1, 2);\n\n\tmainLayout->addWidget(new QLabel(\"Log level:\"), 5, 0, Qt::AlignRight);\n\tmainLayout->addWidget(logLevelCombo_, 5, 1, 1, 1);\n\n\tmainLayout->addWidget(new QWidget, 6, 0);\n\n\tmainLayout->addWidget(buttons_, 7, 1, 1, 2);\n\n\tmainLayout->setRowStretch(5, 1);\n\n\tmainLayout->setColumnStretch(0, 0);\n\tmainLayout->setColumnStretch(1, 0);\n\tmainLayout->setColumnStretch(2, 1);\n\n\tsetLayout(mainLayout);\n}\n\n\nvoid LinkDialog::browsePlugin()\n{\n\tQString prefix = currentPrefix();\n\tQFileInfo prefixInfo(prefix);\n\n\tif(!prefixInfo.isDir()) {\n\t\tQMessageBox::critical(this, \"Error\", \"Selected prefix directory doesn't exists.\");\n\t\treturn;\n\t}\n\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingFile);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files);\n\tdialog.setNameFilter(\"*.dll\");\n\tdialog.setWindowTitle(\"Select the source VST plugin\");\n\tdialog.setRootDirectory(prefix);\n\n\tif(targetEdit_->text().isEmpty()) {\n\t\tQFileInfo info(prefixInfo.absoluteFilePath(), \"drive_c\");\n\t\tdialog.setDirectory(info.absoluteFilePath());\n\t}\n\telse {\n\t\tQFileInfo info(prefix, targetEdit_->text());\n\t\twhile(!info.isDir())\n\t\t\tinfo = QFileInfo(info.absolutePath());\n\n\t\tdialog.setDirectory(info.absoluteFilePath());\n\t}\n\n\tif(dialog.exec()) {\n\t\tQString prefixPath = prefixInfo.absoluteFilePath();\n\t\tint length = prefixPath.length();\n\t\tif(!prefixPath.endsWith('/'))\n\t\t\tlength++;\n\n\t\ttargetEdit_->setText(dialog.selectedPath().mid(length));\n\n\t\tQString name = dialog.selectedName();\n\t\tname.chop(4); // Remove \".dll\" extension\n\t\tnameEdit_->setText(name);\n\t}\n}\n\n\nvoid LinkDialog::browseLocation()\n{\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingDirectory);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);\n\tdialog.setWindowTitle(\"Select directory where the link will be placed\");\n\n\tQString text = locationEdit_->text();\n\tif(text.isEmpty()) {\n\t\tdialog.setDirectory(QDir::homePath());\n\t}\n\telse {\n\t\tdialog.setDirectory(text);\n\t}\n\n\tif(dialog.exec()) {\n\t\tlocationEdit_->setText(dialog.selectedPath());\n\t}\n}\n\n\nvoid LinkDialog::accept()\n{\n\tQString prefix = currentPrefix();\n\tQFileInfo vstInfo(prefix, targetEdit_->text());\n\n\tif(!vstInfo.isFile() || vstInfo.suffix() != \"dll\") {\n\t\tQMessageBox::critical(this, \"Error\", \"VST plugin is invalid or doesn't exists.\");\n\t\treturn;\n\t}\n\n\tQFileInfo locationInfo(locationEdit_->text());\n\n\tif(!locationInfo.isDir()) {\n\t\tQMessageBox::critical(this, \"Error\", \"Location directory doesn't exists.\");\n\t\treturn;\n\t}\n\n\tQString name = nameEdit_->text();\n\tif(name.isEmpty()) {\n\t\tQMessageBox::critical(this, \"Error\", \"Link name cannot be empty.\");\n\t\treturn;\n\t}\n\n\tQString pluginPath = getPluginPath();\n\tif(pluginPath.isEmpty()) {\n\t\tQMessageBox::critical(this, \"Error\", \"VST plugin is corrupted.\");\n\t\treturn;\n\t}\n\n\tint index = logLevelCombo_->currentIndex();\n\tAirwave::LogLevel level = static_cast<Airwave::LogLevel>(index - 1);\n\n\tif(level == Airwave::LogLevel::kFlood) {\n\t\tQString message = \"<b>WARNING!</b><br>\"\n\t\t\t\t\"By using the 'flood' log level, you will get an enormous count of log \"\n\t\t\t\t\"messages from this link, the plugin performance will be low and the \"\n\t\t\t\t\"audio playback may become very choppy.<br><br>\"\n\t\t\t\t\"Do you really want to proceed?\";\n\n\t\tif(QMessageBox::question(this, \"Question\", message) == QMessageBox::No)\n\t\t\treturn;\n\t}\n\n\tif(!item_) {\n\t\titem_ = qApp->links()->createLink(nameEdit_->text(), locationEdit_->text(),\n\t\t\t\ttargetEdit_->text(), prefixCombo_->currentText(),\n\t\t\t\tloaderCombo_->currentText());\n\n\t\tif(!item_) {\n\t\t\tQMessageBox::critical(this, \"Error\", \"Unable to create link.\");\n\t\t\treturn;\n\t\t}\n\n\t\tint value = logLevelCombo_->currentIndex() - 1;\n\t\titem_->setLogLevel(static_cast<LogLevel>(value));\n\t}\n\telse {\n\t\tif(item_->name() != name) {\n\t\t\tLinkItem* item = qApp->links()->root()->firstChild();\n\n\t\t\twhile(item) {\n\t\t\t\tif(item->name() == name) {\n\t\t\t\t\tQString message = QString(\"Link with name '%1' is already exists.\")\n\t\t\t\t\t\t\t.arg(name);\n\n\t\t\t\t\tQMessageBox::critical(this, \"Error\", message);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\titem = item->nextSibling();\n\t\t\t}\n\t\t}\n\n\t\tQDir dir(locationInfo.absoluteFilePath());\n\t\tdir.remove(item_->name() + \".so\");\n\n\t\titem_->setName(nameEdit_->text());\n\t\titem_->setLocation(locationEdit_->text());\n\t\titem_->setTarget(targetEdit_->text());\n\t\titem_->setPrefix(prefixCombo_->currentText());\n\t\titem_->setLoader(loaderCombo_->currentText());\n\n\t\tint value = logLevelCombo_->currentIndex() - 1;\n\t\titem_->setLogLevel(static_cast<LogLevel>(value));\n\t}\n\n\tqApp->storage()->save();\n\tQFile::copy(pluginPath, locationInfo.absoluteFilePath() + '/' + name + \".so\");\n\n\tQDialog::accept();\n}\n\n\nvoid LinkDialog::onPrefixChanged()\n{\n\ttargetEdit_->clear();\n\tnameEdit_->clear();\n}\n\n\nQString LinkDialog::currentPrefix() const\n{\n\tint index = prefixCombo_->currentIndex();\n\n\tif(index != -1) {\n\t\tPrefixItem* item = qApp->prefixes()->root()->childAt(index);\n\t\tif(item)\n\t\t\treturn item->path();\n\t}\n\n\treturn QString();\n}\n\n\nQString LinkDialog::getPluginPath() const\n{\n\tQString pluginPath = QString::fromStdString(qApp->storage()->binariesPath());\n\treturn pluginPath + \"/\" PLUGIN_BASENAME \".so\";\n}\n"
  },
  {
    "path": "src/manager/forms/linkdialog.h",
    "content": "#ifndef FORMS_LINKEDITDIALOG_H\n#define FORMS_LINKEDITDIALOG_H\n\n#include <QDialog>\n\n\nclass QComboBox;\nclass QDialogButtonBox;\nclass LineEdit;\nclass LinkItem;\n\n\nclass LinkDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tLinkDialog(QWidget* parent = nullptr);\n\n\tLinkItem* item() const;\n\tvoid setItem(LinkItem* item);\n\nprivate:\n\tQComboBox* loaderCombo_;\n\tQComboBox* prefixCombo_;\n\tQComboBox* logLevelCombo_;\n\tLineEdit* targetEdit_;\n\tLineEdit* locationEdit_;\n\tLineEdit* nameEdit_;\n\tQDialogButtonBox* buttons_;\n\tLinkItem* item_;\n\n\tvoid setupUi();\n\tQString currentPrefix() const;\n\tQString getPluginPath() const;\n\nprivate slots:\n\tvoid browsePlugin();\n\tvoid browseLocation();\n\tvoid onPrefixChanged();\n\tvoid accept();\n};\n\n\n#endif // FORMS_LINKEDITDIALOG_H\n"
  },
  {
    "path": "src/manager/forms/loaderdialog.cpp",
    "content": "#include \"loaderdialog.h\"\n\n#include <QDialogButtonBox>\n#include <QGridLayout>\n#include <QIcon>\n#include <QLabel>\n#include <QMessageBox>\n#include \"core/application.h\"\n#include \"forms/filedialog.h\"\n#include \"models/loadersmodel.h\"\n#include \"widgets/lineedit.h\"\n\n\nLoaderDialog::LoaderDialog(QWidget* parent) :\n\tQDialog(parent),\n\titem_(nullptr)\n{\n\tsetupUi();\n}\n\n\nvoid LoaderDialog::setupUi()\n{\n\tsetWindowIcon(QIcon(\":/windows.png\"));\n\tsetWindowTitle(\"WINE loader properties\");\n\tsetFixedWidth(320);\n\tsetFixedHeight(130);\n\n\tnameEdit_ = new LineEdit;\n\n\tpathEdit_ = new LineEdit;\n\tpathEdit_->setButtonEnabled(true);\n\tpathEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tpathEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tpathEdit_->setButtonToolTip(\"Browse\");\n\tconnect(pathEdit_, SIGNAL(buttonClicked()), SLOT(browseForWineLoader()));\n\n\tbuttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tconnect(buttons_, SIGNAL(accepted()), SLOT(accept()));\n\tconnect(buttons_, SIGNAL(rejected()), SLOT(reject()));\n\n\tQGridLayout* mainLayout = new QGridLayout;\n\n\tmainLayout->addWidget(new QLabel(\"Name:\"), 0, 0, Qt::AlignRight);\n\tmainLayout->addWidget(nameEdit_, 0, 1);\n\n\tmainLayout->addWidget(new QLabel(\"Path:\"), 1, 0, Qt::AlignRight);\n\tmainLayout->addWidget(pathEdit_, 1, 1);\n\n\tmainLayout->addWidget(new QWidget, 2, 1);\n\tmainLayout->setRowStretch(2, 1);\n\n\tmainLayout->addWidget(buttons_, 3, 0, 1, -1);\n\n\tsetLayout(mainLayout);\n}\n\n\nvoid LoaderDialog::browseForWineLoader()\n{\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingFile);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files);\n\tdialog.setWindowTitle(\"Select the WINE loader binary\");\n\n\tQString text = pathEdit_->text();\n\tif(text.isEmpty()) {\n\t\tdialog.setDirectory(QDir::homePath());\n\t}\n\telse {\n\t\tdialog.setDirectory(text);\n\t}\n\n\tif(dialog.exec()) {\n\t\tpathEdit_->setText(dialog.selectedPath());\n\t}\n}\n\nLoaderItem* LoaderDialog::item() const\n{\n\treturn item_;\n}\n\n\nvoid LoaderDialog::setItem(LoaderItem* item)\n{\n\titem_ = item;\n\n\tif(item_) {\n\t\tnameEdit_->setText(item->name());\n\t\tpathEdit_->setText(item->path());\n\t}\n\telse {\n\t\tnameEdit_->clear();\n\t\tpathEdit_->clear();\n\t}\n}\n\n\nvoid LoaderDialog::accept()\n{\n\tQString name = nameEdit_->text();\n\tQString message = QString(\"The loader with name '%1' is already exist.\").arg(name);\n\n\tif(!item_) {\n\t\tif(!qApp->loaders()->createLoader(nameEdit_->text(), pathEdit_->text())) {\n\t\t\tQMessageBox::critical(this, \"Error\", message);\n\t\t\treturn;\n\t\t}\n\t}\n\telse if(name != item_->name()) {\n\t\tStorage::Loader loader = qApp->storage()->loader(name.toStdString());\n\t\tif(!loader.isNull()) {\n\t\t\tQMessageBox::critical(this, \"Error\", message);\n\t\t\treturn;\n\t\t}\n\n\t\titem_->setName(nameEdit_->text());\n\t\titem_->setPath(pathEdit_->text());\n\t}\n\telse {\n\t\titem_->setPath(pathEdit_->text());\n\t}\n\n\tQDialog::accept();\n}\n"
  },
  {
    "path": "src/manager/forms/loaderdialog.h",
    "content": "#ifndef FORMS_LOADERDIALOG_H\n#define FORMS_LOADERDIALOG_H\n\n#include <QDialog>\n\n\nclass QDialogButtonBox;\nclass LineEdit;\nclass LoaderItem;\n\n\nclass LoaderDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tLoaderDialog(QWidget* parent = nullptr);\n\n\tLoaderItem* item() const;\n\tvoid setItem(LoaderItem* item);\n\nprivate:\n\tLineEdit* nameEdit_;\n\tLineEdit* pathEdit_;\n\tQDialogButtonBox* buttons_;\n\tLoaderItem* item_;\n\n\tvoid setupUi();\n\nprivate slots:\n\tvoid browseForWineLoader();\n\tvoid accept();\n};\n\n\n#endif // FORMS_LOADERDIALOG_H\n"
  },
  {
    "path": "src/manager/forms/mainform.cpp",
    "content": "#include \"mainform.h\"\n\n#include <QAction>\n#include <QDesktopServices>\n#include <QDir>\n#include <QFileInfo>\n#include <QHBoxLayout>\n#include <QHeaderView>\n#include <QMessageBox>\n#include <QSettings>\n#include <QSplitter>\n#include <QToolBar>\n#include \"common/config.h\"\n#include \"core/application.h\"\n#include \"forms/linkdialog.h\"\n#include \"forms/settingsdialog.h\"\n#include \"models/linksmodel.h\"\n#include \"widgets/linksview.h\"\n#include \"widgets/logview.h\"\n\n\nMainForm::MainForm(QWidget* parent) :\n\tQMainWindow(parent)\n{\n\tsetupUi();\n\tloadSettings();\n\tupdateToolbarButtons();\n\n\tQString logSocketPath = QString::fromStdString(qApp->storage()->logSocketPath());\n\n\tLogSocket* socket = qApp->logSocket();\n\tif(!socket->listen(logSocketPath))\n\t\tqDebug(\"Unable to create logger socket.\");\n\n\tconnect(socket,\n\t\t\tSIGNAL(newMessage(quint64,QString,QString)),\n\t\t\tlogView_,\n\t\t\tSLOT(addMessage(quint64,QString,QString)));\n\n\tconnect(qApp->links(),\n\t\t\tSIGNAL(rowsInserted(QModelIndex,int,int)),\n\t\t\tSLOT(updateToolbarButtons()));\n\n\tconnect(qApp->links(),\n\t\t\tSIGNAL(rowsRemoved(QModelIndex,int,int)),\n\t\t\tSLOT(updateToolbarButtons()));\n\n\tconnect(qApp->links(),\n\t\t\tSIGNAL(layoutChanged()),\n\t\t\tSLOT(updateToolbarButtons()));\n}\n\n\nMainForm::~MainForm()\n{\n\tsaveSettings();\n}\n\n\nvoid MainForm::loadSettings()\n{\n\tQSettings settings;\n\tsettings.beginGroup(\"mainForm\");\n\n\tresize(settings.value(\"size\", QSize(800, 600)).toSize());\n\trestoreState(settings.value(\"windowState\").toByteArray());\n\n\tsplitter_->restoreState(settings.value(\"mainSplitter\").toByteArray());\n\n\ttoggleWordWrap_->setChecked(settings.value(\"logWordWrap\", true).toBool());\n\ttoggleAutoScroll_->setChecked(settings.value(\"logAutoScroll\", true).toBool());\n\n\tQHeaderView* header = linksView_->header();\n\n\tint width = settings.value(\"linkNameWidth\", 150).toInt();\n\theader->resizeSection(0, width);\n\n\twidth = settings.value(\"logLevelWidth\", 90).toInt();\n\theader->resizeSection(1, width);\n\n\twidth = settings.value(\"prefixNameWidth\", 70).toInt();\n\theader->resizeSection(2, width);\n\n\twidth = settings.value(\"loaderNameWidth\", 70).toInt();\n\theader->resizeSection(3, width);\n\n\tsettings.endGroup();\n}\n\n\nvoid MainForm::saveSettings()\n{\n\tQSettings settings;\n\tsettings.beginGroup(\"mainForm\");\n\n\tsettings.setValue(\"size\", size());\n\tsettings.setValue(\"windowState\", saveState());\n\n\tsettings.setValue(\"mainSplitter\", splitter_->saveState());\n\n\tsettings.setValue(\"logWordWrap\", toggleWordWrap_->isChecked());\n\tsettings.setValue(\"logAutoScroll\", toggleAutoScroll_->isChecked());\n\n\tQHeaderView* header = linksView_->header();\n\tsettings.setValue(\"linkNameWidth\", header->sectionSize(0));\n\tsettings.setValue(\"logLevelWidth\", header->sectionSize(1));\n\tsettings.setValue(\"prefixNameWidth\", header->sectionSize(2));\n\tsettings.setValue(\"loaderNameWidth\", header->sectionSize(3));\n\n\tsettings.endGroup();\n}\n\n\nvoid MainForm::setupUi()\n{\n\tsetWindowIcon(QIcon(\":/\" PROJECT_NAME \"-manager.png\"));\n\tsetWindowTitle(PROJECT_NAME \" manager \" VERSION_STRING);\n\n\tsetCentralWidget(new QWidget);\n\n\tQVBoxLayout* layout = new QVBoxLayout;\n\tlayout->setSpacing(0);\n\tlayout->setMargin(1);\n\n\tlinksView_ = new LinksView;\n\tlinksView_->setModel(qApp->links());\n\n\tQHeaderView* header = linksView_->header();\n\theader->setStretchLastSection(false);\n\theader->setSectionResizeMode(0, QHeaderView::Interactive);\n\theader->setSectionResizeMode(1, QHeaderView::Interactive);\n\theader->setSectionResizeMode(2, QHeaderView::Interactive);\n\theader->setSectionResizeMode(3, QHeaderView::Interactive);\n\theader->setSectionResizeMode(4, QHeaderView::Stretch);\n\n\tconnect(linksView_,\n\t\t\tSIGNAL(itemSelectionChanged(QItemSelection,QItemSelection)),\n\t\t\tSLOT(updateToolbarButtons()));\n\n\tconnect(linksView_,\n\t\t\tSIGNAL(itemDoubleClicked(LinkItem*)),\n\t\t\tSLOT(editLink()));\n\n\tlogView_ = new LogView;\n\n\tsplitter_ = new QSplitter(Qt::Vertical);\n\tsplitter_->addWidget(linksView_);\n\tsplitter_->addWidget(logView_);\n\n\tint size = splitter_->height();\n\tsplitter_->setSizes(QList<int>() << size * 0.618 << size * 0.382);\n\n\tlayout->addWidget(splitter_);\n\n\tcentralWidget()->setLayout(layout);\n\n\t//\n\t// Toolbar\n\t//\n\ttoolBar_ = new QToolBar(this);\n\ttoolBar_->setObjectName(\"toolBar_\");\n\ttoolBar_->setFloatable(false);\n\ttoolBar_->setMovable(false);\n\ttoolBar_->setIconSize(QSize(20, 20));\n\taddToolBar(toolBar_);\n\n\t// Create link action\n\tcreateLink_ = new QAction(QIcon(\":/create_link.png\"), \"Create link\", this);\n\ttoolBar_->addAction(createLink_);\n\tconnect(createLink_, SIGNAL(triggered()), SLOT(createLink()));\n\n\t// Edit link action\n\teditLink_ = new QAction(QIcon(\":/edit.png\"), \"Edit link\", this);\n\teditLink_->setEnabled(false);\n\ttoolBar_->addAction(editLink_);\n\tconnect(editLink_, SIGNAL(triggered()), SLOT(editLink()));\n\n\t// Remove link action\n\tremoveLink_ = new QAction(QIcon(\":/remove.png\"), \"Remove link\", this);\n\tremoveLink_->setEnabled(false);\n\ttoolBar_->addAction(removeLink_);\n\tconnect(removeLink_, SIGNAL(triggered()), SLOT(removeLink()));\n\n\ttoolBar_->addSeparator();\n\n\t// Show in file browser action\n\tshowInBrowser_ = new QAction(QIcon(\":/open_in_browser.png\"),\n\t\t\t\"Open in file manager the WINE prefix directory of the link\", this);\n\n\tshowInBrowser_->setEnabled(false);\n\ttoolBar_->addAction(showInBrowser_);\n\tconnect(showInBrowser_, SIGNAL(triggered()), SLOT(showInBrowser()));\n\n\t// Update all links action\n\tupdateLinks_ = new QAction(QIcon(\":/update.png\"), \"Update links\", this);\n\ttoolBar_->addAction(updateLinks_);\n\tconnect(updateLinks_, SIGNAL(triggered()), SLOT(updateLinks()));\n\n\ttoolBar_->addSeparator();\n\n\t// Toggle word wrap action\n\ttoggleWordWrap_ = new QAction(\n\t\t\tQIcon(\":/outline.png\"), \"Wrap long lines in the log view\", this);\n\n\ttoggleWordWrap_->setCheckable(true);\n\ttoolBar_->addAction(toggleWordWrap_);\n\tconnect(toggleWordWrap_, SIGNAL(triggered(bool)), logView_, SLOT(setWordWrap(bool)));\n\n\t// Toggle auto scroll action\n\ttoggleAutoScroll_ = new QAction(\n\t\t\tQIcon(\":/download.png\"), \"Autoscroll log on new message\", this);\n\n\ttoggleAutoScroll_->setCheckable(true);\n\ttoolBar_->addAction(toggleAutoScroll_);\n\n\tconnect(toggleAutoScroll_,\n\t\t\tSIGNAL(triggered(bool)),\n\t\t\tlogView_,\n\t\t\tSLOT(setAutoScroll(bool)));\n\n\t// Add separator action\n\taddSeparator_ = new QAction(QIcon(\":/draw_line.png\"), \"Add separation line\", this);\n\n\ttoolBar_->addAction(addSeparator_);\n\tconnect(addSeparator_, SIGNAL(triggered()), logView_, SLOT(addSeparator()));\n\n\t// Clear log action\n\tclearLog_ = new QAction(QIcon(\":/erase.png\"), \"Clear log messages\", this);\n\ttoolBar_->addAction(clearLog_);\n\tconnect(clearLog_, SIGNAL(triggered()), logView_, SLOT(clear()));\n\n\ttoolBar_->addSeparator();\n\n\tQWidget* spacer = new QWidget;\n\tspacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\ttoolBar_->addWidget(spacer);\n\n\ttoolBar_->addSeparator();\n\n\t// Show about window action\n//\tshowAbout_ = new QAction(QIcon(\":/about.png\"), \"About\", this);\n//\ttoolBar_->addAction(showAbout_);\n//\tconnect(showAbout_, SIGNAL(triggered()), SLOT(showAbout()));\n\n\t// Show settings action\n\tshowSettings_ = new QAction(QIcon(\":/settings.png\"), \"Settings\", this);\n\ttoolBar_->addAction(showSettings_);\n\tconnect(showSettings_, SIGNAL(triggered()), SLOT(showSettings()));\n}\n\n\nbool MainForm::checkBinaries()\n{\n\tQStringList files = qApp->checkMissingBinaries();\n\tif(!files.isEmpty()) {\n\t\tQString message = \"Some binaries aren't found, please choose the correct\\n\"\n\t\t\t\t\"binaries location in settings dialog!\\n\\nThe missed binaries are:\\n\\n\";\n\t\tforeach(const QString& fileName, files)\n\t\t\tmessage += fileName + '\\n';\n\n\t\tQMessageBox::critical(this, \"Error\", message);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n\nvoid MainForm::createLink()\n{\n\tif(checkBinaries()) {\n\t\tLinkDialog dialog;\n\t\tdialog.exec();\n\t}\n\telse {\n\t\tSettingsDialog dialog;\n\t\tdialog.exec();\n\t}\n}\n\n\nvoid MainForm::editLink()\n{\n\tif(checkBinaries()) {\n\t\tLinkDialog dialog;\n\t\tdialog.setItem(linksView_->currentItem());\n\t\tdialog.exec();\n\t}\n\telse {\n\t\tSettingsDialog dialog;\n\t\tdialog.exec();\n\t}\n}\n\n\nvoid MainForm::removeLink()\n{\n\tLinkItem* item = linksView_->currentItem();\n\tif(!item)\n\t\treturn;\n\n\tQString message = QString(\"Do you really want to remove the '%1' link?\")\n\t\t\t.arg(item->name());\n\n\tif(QMessageBox::question(this, \"Question\", message) == QMessageBox::Yes) {\n\t\tQFileInfo info(item->path());\n\t\tQString fileName = item->name() + \".so\";\n\n\t\tif(qApp->links()->removeLink(linksView_->currentItem())) {\n\t\t\tinfo.dir().remove(fileName);\n\t\t\tqApp->storage()->save();\n\t\t}\n\t}\n}\n\n\nvoid MainForm::updateLinks()\n{\n\tQString pluginPath = QString::fromStdString(qApp->storage()->binariesPath());\n\n\tLinkItem* item = qApp->links()->root()->firstChild();\n\twhile(item) {\n\t\tQFileInfo linkInfo(item->path());\n\t\tQDir dir(linkInfo.absoluteDir());\n\t\tdir.remove(linkInfo.fileName());\n\n\t\tQString pluginName = PLUGIN_BASENAME \".so\";\n\t\tQFile::copy(pluginPath + '/' + pluginName, linkInfo.absoluteFilePath());\n\n\t\titem = item->nextSibling();\n\t}\n}\n\n\nvoid MainForm::showInBrowser()\n{\n\tLinkItem* item = linksView_->currentItem();\n\tif(!item)\n\t\treturn;\n\n\tauto prefix = qApp->storage()->prefix(item->prefix().toStdString());\n\tif(!prefix) {\n\t\tQMessageBox::critical(this, \"Error\", \"WINE prefix is corrupted\");\n\t\treturn;\n\t}\n\n\tQString path = QString::fromStdString(prefix.path());\n\tif(!QDir(path).exists()) {\n\t\tQMessageBox::critical(this, \"Error\", \"WINE prefix directory doesn't exist\");\n\t\treturn;\n\t}\n\n\tpath = QDir::toNativeSeparators(path);\n\tQDesktopServices::openUrl(QUrl(\"file:///\" + path));\n}\n\n\nvoid MainForm::showAbout()\n{\n\n}\n\n\nvoid MainForm::showSettings()\n{\n\tSettingsDialog dialog;\n\tdialog.exec();\n}\n\n\nvoid MainForm::updateToolbarButtons()\n{\n\tbool enable = linksView_->hasSelection();\n\n\teditLink_->setEnabled(enable);\n\tshowInBrowser_->setEnabled(enable);\n\tremoveLink_->setEnabled(enable);\n\n\tupdateLinks_->setEnabled(linksView_->model()->root()->childCount());\n}\n"
  },
  {
    "path": "src/manager/forms/mainform.h",
    "content": "#ifndef FORMS_MAINFORM_H\n#define FORMS_MAINFORM_H\n\n#include <QMainWindow>\n\n\nclass QAction;\nclass QSplitter;\nclass LinksModel;\nclass LinksView;\nclass LogView;\n\n\nclass MainForm : public QMainWindow {\n\tQ_OBJECT\npublic:\n\texplicit MainForm(QWidget* parent = nullptr);\n\t~MainForm();\n\npublic slots:\n\tvoid loadSettings();\n\tvoid saveSettings();\n\nprivate:\n\tQToolBar* toolBar_;\n\n\tQAction* createLink_;\n\tQAction* editLink_;\n\tQAction* showInBrowser_;\n\tQAction* removeLink_;\n\tQAction* updateLinks_;\n\n\tQAction* toggleWordWrap_;\n\tQAction* toggleAutoScroll_;\n\tQAction* addSeparator_;\n\tQAction* clearLog_;\n\n\tQAction* showAbout_;\n\tQAction* showSettings_;\n\n\tQSplitter* splitter_;\n\tLinksView* linksView_;\n\tLogView* logView_;\n\n\tvoid setupUi();\n\tbool checkBinaries();\n\nprivate slots:\n\tvoid createLink();\n\tvoid editLink();\n\tvoid removeLink();\n\tvoid updateLinks();\n\tvoid showInBrowser();\n\tvoid showAbout();\n\tvoid showSettings();\n\n\tvoid updateToolbarButtons();\n};\n\n\n#endif // FORMS_MAINFORM_H\n"
  },
  {
    "path": "src/manager/forms/prefixdialog.cpp",
    "content": "#include \"prefixdialog.h\"\n\n#include <QDialogButtonBox>\n#include <QGridLayout>\n#include <QIcon>\n#include <QLabel>\n#include <QMessageBox>\n#include \"core/application.h\"\n#include \"forms/filedialog.h\"\n#include \"models/prefixesmodel.h\"\n#include \"widgets/lineedit.h\"\n\n\nPrefixDialog::PrefixDialog(QWidget* parent) :\n\tQDialog(parent),\n\titem_(nullptr)\n{\n\tsetupUi();\n}\n\n\nvoid PrefixDialog::setupUi()\n{\n\tsetWindowIcon(QIcon(\":/windows.png\"));\n\tsetWindowTitle(\"WINE prefix properties\");\n\tsetFixedWidth(320);\n\tsetFixedHeight(130);\n\n\tnameEdit_ = new LineEdit;\n\n\tpathEdit_ = new LineEdit;\n\tpathEdit_->setButtonEnabled(true);\n\tpathEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tpathEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tpathEdit_->setButtonToolTip(\"Browse\");\n\tconnect(pathEdit_, SIGNAL(buttonClicked()), SLOT(browseForWinePrefix()));\n\n\tbuttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tconnect(buttons_, SIGNAL(accepted()), SLOT(accept()));\n\tconnect(buttons_, SIGNAL(rejected()), SLOT(reject()));\n\n\tQGridLayout* mainLayout = new QGridLayout;\n\n\tmainLayout->addWidget(new QLabel(\"Name:\"), 0, 0, Qt::AlignRight);\n\tmainLayout->addWidget(nameEdit_, 0, 1);\n\n\tmainLayout->addWidget(new QLabel(\"Path:\"), 1, 0, Qt::AlignRight);\n\tmainLayout->addWidget(pathEdit_, 1, 1);\n\n\tmainLayout->addWidget(new QWidget, 2, 1);\n\tmainLayout->setRowStretch(2, 1);\n\n\tmainLayout->addWidget(buttons_, 3, 0, 1, -1);\n\n\tsetLayout(mainLayout);\n}\n\n\nvoid PrefixDialog::browseForWinePrefix()\n{\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingDirectory);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);\n\tdialog.setWindowTitle(\"Select the WINE prefix directory\");\n\n\tQString text = pathEdit_->text();\n\tif(text.isEmpty()) {\n\t\tdialog.setDirectory(QDir::homePath());\n\t}\n\telse {\n\t\tdialog.setDirectory(text);\n\t}\n\n\tif(dialog.exec()) {\n\t\tpathEdit_->setText(dialog.selectedPath());\n\t}\n\n}\n\n\nPrefixItem* PrefixDialog::item() const\n{\n\treturn item_;\n}\n\n\nvoid PrefixDialog::setItem(PrefixItem* item)\n{\n\titem_ = item;\n\n\tif(item_) {\n\t\tnameEdit_->setText(item->name());\n\t\tpathEdit_->setText(item->path());\n\t}\n\telse {\n\t\tnameEdit_->clear();\n\t\tpathEdit_->clear();\n\t}\n}\n\n\nvoid PrefixDialog::accept()\n{\n\tQString name = nameEdit_->text();\n\tQString message = QString(\"The prefix with name '%1' is already exist.\").arg(name);\n\n\tif(!item_) {\n\t\tif(!qApp->prefixes()->createPrefix(nameEdit_->text(), pathEdit_->text())) {\n\t\t\tQMessageBox::critical(this, \"Error\", message);\n\t\t\treturn;\n\t\t}\n\t}\n\telse if(name != item_->name()) {\n\t\tStorage::Prefix prefix = qApp->storage()->prefix(name.toStdString());\n\t\tif(!prefix.isNull()) {\n\t\t\tQMessageBox::critical(this, \"Error\", message);\n\t\t\treturn;\n\t\t}\n\n\t\titem_->setName(nameEdit_->text());\n\t\titem_->setPath(pathEdit_->text());\n\t}\n\telse {\n\t\titem_->setPath(pathEdit_->text());\n\t}\n\n\tQDialog::accept();\n}\n"
  },
  {
    "path": "src/manager/forms/prefixdialog.h",
    "content": "#ifndef FORMS_PREFIXDIALOG_H\n#define FORMS_PREFIXDIALOG_H\n\n#include <QDialog>\n\n\nclass QDialogButtonBox;\nclass LineEdit;\nclass PrefixItem;\n\n\nclass PrefixDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tPrefixDialog(QWidget* parent = nullptr);\n\n\tPrefixItem* item() const;\n\tvoid setItem(PrefixItem* item);\n\nprivate:\n\tLineEdit* nameEdit_;\n\tLineEdit* pathEdit_;\n\tQDialogButtonBox* buttons_;\n\tPrefixItem* item_;\n\n\tvoid setupUi();\n\nprivate slots:\n\tvoid browseForWinePrefix();\n\tvoid accept();\n};\n\n\n#endif // FORMS_PREFIXDIALOG_H\n"
  },
  {
    "path": "src/manager/forms/settingsdialog.cpp",
    "content": "#include \"settingsdialog.h\"\n\n#include <QComboBox>\n#include <QDialogButtonBox>\n#include <QGridLayout>\n#include <QHBoxLayout>\n#include <QLabel>\n#include <QMessageBox>\n#include <QPushButton>\n#include <QSettings>\n#include <QTreeWidget>\n#include \"common/config.h\"\n#include \"core/application.h\"\n#include \"core/logsocket.h\"\n#include \"forms/filedialog.h\"\n#include \"forms/loaderdialog.h\"\n#include \"forms/prefixdialog.h\"\n#include \"models/loadersmodel.h\"\n#include \"models/prefixesmodel.h\"\n#include \"widgets/lineedit.h\"\n#include \"widgets/loadersview.h\"\n#include \"widgets/prefixesview.h\"\n#include \"widgets/separatorlabel.h\"\n\n\nSettingsDialog::SettingsDialog(QWidget* parent) :\n\tQDialog(parent)\n{\n\tsetupUi();\n\n\tQSettings settings;\n\tQString vstPath = settings.value(\"vstPath\", qgetenv(\"VST_PATH\")).toString();\n\tvstPathEdit_->setText(vstPath.split(':').first());\n\n\tStorage* storage = qApp->storage();\n\tbinariesPathEdit_->setText(QString::fromStdString(storage->binariesPath()));\n\tlogSocketEdit_->setText(QString::fromStdString(storage->logSocketPath()));\n\n\tint index = static_cast<int>(storage->defaultLogLevel());\n\tlogLevelCombo_->setCurrentIndex(index);\n}\n\n\nSettingsDialog::~SettingsDialog()\n{\n\tQSettings settings;\n\tsettings.setValue(\"vstPath\", vstPathEdit_->text());\n}\n\n\nvoid SettingsDialog::setupUi()\n{\n\tsetWindowIcon(QIcon(\":/settings.png\"));\n\tsetMinimumWidth(400);\n\tresize(500, 450);\n\n\tvstPathEdit_ = new LineEdit;\n\tvstPathEdit_->setToolTip(\"Directory, where all of your native VSTs are located\");\n\tvstPathEdit_->setButtonEnabled(true);\n\tvstPathEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tvstPathEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tvstPathEdit_->setButtonToolTip(\"Browse\");\n\tconnect(vstPathEdit_, SIGNAL(buttonClicked()), SLOT(browseForVstPath()));\n\n\tbinariesPathEdit_ = new LineEdit;\n\tbinariesPathEdit_->setToolTip(\"Directory, where airwave binaries are located\");\n\tbinariesPathEdit_->setButtonEnabled(true);\n\tbinariesPathEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tbinariesPathEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tbinariesPathEdit_->setButtonToolTip(\"Browse\");\n\tconnect(binariesPathEdit_, SIGNAL(buttonClicked()), SLOT(browseForBinariesPath()));\n\n\tlogSocketEdit_ = new LineEdit;\n\tlogSocketEdit_->setToolTip(\"Socket file, used for logging\");\n\tlogSocketEdit_->setButtonEnabled(true);\n\tlogSocketEdit_->setButtonStyle(LineEdit::kLightAutoRaise);\n\tlogSocketEdit_->setButtonIcon(QIcon(\":/open.png\"));\n\tlogSocketEdit_->setButtonToolTip(\"Browse\");\n\tconnect(logSocketEdit_, SIGNAL(buttonClicked()), SLOT(browseForSocketPath()));\n\n\tlogLevelCombo_ = new QComboBox;\n\tlogLevelCombo_->setToolTip(\"Log level, used for links with the 'default' log level.\\n\"\n\t\t\t\"The higher the level, the more messages will appear.\");\n\n\tlogLevelCombo_->addItem(QIcon(\":/mute.png\"), \"quiet\");\n\tlogLevelCombo_->addItem(QIcon(\":/warning.png\"), \"error\");\n\tlogLevelCombo_->addItem(QIcon(\":/trace.png\"), \"trace\");\n\tlogLevelCombo_->addItem(QIcon(\":/bug.png\"), \"debug\");\n\tlogLevelCombo_->addItem(QIcon(\":/scull.png\"), \"flood\");\n\n\tQGridLayout* generalLayout = new QGridLayout;\n\tgeneralLayout->addWidget(new QLabel(\"VST location:\"), 0, 0, Qt::AlignRight);\n\tgeneralLayout->addWidget(vstPathEdit_, 0, 1, 1, 4);\n\tgeneralLayout->addWidget(new QLabel(\"Binaries location:\"), 1, 0, Qt::AlignRight);\n\tgeneralLayout->addWidget(binariesPathEdit_, 1, 1, 1, 4);\n\tgeneralLayout->addWidget(new QLabel(\"Log socket path:\"), 2, 0, Qt::AlignRight);\n\tgeneralLayout->addWidget(logSocketEdit_, 2, 1, 1, 4);\n\tgeneralLayout->addWidget(new QLabel(\"Default log level:\"), 3, 0, Qt::AlignRight);\n\tgeneralLayout->addWidget(logLevelCombo_, 3, 1);\n\n\tprefixesView_ = new PrefixesView;\n\tprefixesView_->setModel(qApp->prefixes());\n\n\taddPrefixButton_ = new QPushButton(\"Add\");\n\tconnect(addPrefixButton_, SIGNAL(clicked()), SLOT(createPrefix()));\n\n\teditPrefixButton_ = new QPushButton(\"Edit\");\n\tconnect(editPrefixButton_, SIGNAL(clicked()), SLOT(editPrefix()));\n\n\tremovePrefixButton_ = new QPushButton(\"Remove\");\n\tconnect(removePrefixButton_, SIGNAL(clicked()), SLOT(removePrefix()));\n\n\tQVBoxLayout* vl = new QVBoxLayout;\n\tvl->addWidget(addPrefixButton_);\n\tvl->addWidget(editPrefixButton_);\n\tvl->addWidget(removePrefixButton_);\n\tvl->addStretch(1);\n\n\tQHBoxLayout* prefixLayout = new QHBoxLayout;\n\tprefixLayout->addWidget(prefixesView_);\n\tprefixLayout->addLayout(vl);\n\n\tloadersView_ = new LoadersView;\n\tloadersView_->setModel(qApp->loaders());\n\n\taddLoaderButton_ = new QPushButton(\"Add\");\n\tconnect(addLoaderButton_, SIGNAL(clicked()), SLOT(createLoader()));\n\n\teditLoaderButton_ = new QPushButton(\"Edit\");\n\tconnect(editLoaderButton_, SIGNAL(clicked()), SLOT(editLoader()));\n\n\tremoveLoaderButton_ = new QPushButton(\"Remove\");\n\tconnect(removeLoaderButton_, SIGNAL(clicked()), SLOT(removeLoader()));\n\n\tvl = new QVBoxLayout;\n\tvl->addWidget(addLoaderButton_);\n\tvl->addWidget(editLoaderButton_);\n\tvl->addWidget(removeLoaderButton_);\n\tvl->addStretch(1);\n\n\tQHBoxLayout* loaderslayout = new QHBoxLayout;\n\tloaderslayout->addWidget(loadersView_);\n\tloaderslayout->addLayout(vl);\n\n\tbuttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tconnect(buttons_, SIGNAL(accepted()), SLOT(accept()));\n\tconnect(buttons_, SIGNAL(rejected()), SLOT(reject()));\n\n\tQVBoxLayout* mainLayout = new QVBoxLayout;\n\tmainLayout->addWidget(new SeparatorLabel(\"General:\"));\n\tmainLayout->addLayout(generalLayout);\n\tmainLayout->addSpacing(10);\n\tmainLayout->addWidget(new SeparatorLabel(\"WINE prefixes:\"));\n\tmainLayout->addLayout(prefixLayout);\n\tmainLayout->addSpacing(10);\n\tmainLayout->addWidget(new SeparatorLabel(\"WINE loaders:\"));\n\tmainLayout->addLayout(loaderslayout);\n\tmainLayout->addSpacing(10);\n\tmainLayout->addWidget(buttons_);\n\n\tsetLayout(mainLayout);\n}\n\n\nvoid SettingsDialog::browseForVstPath()\n{\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingDirectory);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);\n\tdialog.setWindowTitle(\"Select directory with the native VST plugins\");\n\n\tQString path = vstPathEdit_->text();\n\tQFileInfo info(path);\n\tif(!info.exists(path) || !info.isDir())\n\t\tpath = QDir::homePath();\n\n\tdialog.setDirectory(path);\n\n\tif(dialog.exec())\n\t\tvstPathEdit_->setText(dialog.selectedPath());\n}\n\n\nvoid SettingsDialog::browseForBinariesPath()\n{\n\tFileDialog dialog(FileDialog::kOpenDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptExistingDirectory);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files);\n\n\tQStringList nameFilters;\n\tnameFilters << HOST_BASENAME \"-32.exe\" << HOST_BASENAME \"-64.exe\";\n\tnameFilters << PLUGIN_BASENAME \".so\";\n\tdialog.setNameFilters(nameFilters);\n\n\tdialog.setWindowTitle(\"Select directory containing airwave binaries\");\n\n\tQString path = binariesPathEdit_->text();\n\tif(path.isEmpty())\n\t\tpath = QString::fromStdString(qApp->storage()->binariesPath());\n\n\tdialog.setDirectory(path);\n\n\tif(dialog.exec())\n\t\tbinariesPathEdit_->setText(dialog.selectedPath());\n}\n\n\nvoid SettingsDialog::browseForSocketPath()\n{\n\tFileDialog dialog(FileDialog::kSaveDialog);\n\tdialog.setAcceptMode(FileDialog::kAcceptFile);\n\tdialog.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files);\n\tdialog.setDefaultSuffix(\".sock\");\n\tdialog.setWindowTitle(\"Enter file name of domain socket\");\n\n\tQString path = logSocketEdit_->text();\n\tif(path.isEmpty())\n\t\tpath = QString::fromStdString(qApp->storage()->logSocketPath());\n\n\tQFileInfo info(path);\n\tpath = info.absolutePath();\n\tdialog.setDirectory(path);\n\n\tif(dialog.exec())\n\t\tlogSocketEdit_->setText(dialog.selectedPath());\n}\n\n\nvoid SettingsDialog::createPrefix()\n{\n\tPrefixDialog dialog;\n\tdialog.exec();\n}\n\n\nvoid SettingsDialog::editPrefix()\n{\n\tPrefixItem* item = prefixesView_->currentItem();\n\tif(!item) {\n\t\tQMessageBox::critical(this, \"Error\", \"You should select prefix item to edit.\");\n\t\treturn;\n\t}\n\n\tif(item->name() == \"default\") {\n\t\tQMessageBox::critical(this, \"Error\", \"You cannot edit default WINE prefix.\");\n\t\treturn;\n\t}\n\n\tPrefixDialog dialog;\n\tdialog.setItem(item);\n\tdialog.exec();\n}\n\n\nvoid SettingsDialog::removePrefix()\n{\n\tPrefixItem* item = prefixesView_->currentItem();\n\tif(!item) {\n\t\tQMessageBox::critical(this, \"Error\", \"You should select prefix item to remove.\");\n\t\treturn;\n\t}\n\n\tQString prefix = item->name();\n\n\tif(prefix == \"default\") {\n\t\tQMessageBox::critical(this, \"Error\", \"You cannot remove default WINE prefix.\");\n\t\treturn;\n\t}\n\n\tQString text = QString(\"Do you really want to remove the '%1' prefix?\").arg(prefix);\n\tif(QMessageBox::question(this, \"Question\", text) == QMessageBox::Yes) {\n\t\tif(!qApp->prefixes()->removePrefix(item))\n\t\t\tQMessageBox::critical(this, \"Error\", \"Unable to delete selected prefix.\");\n\t}\n}\n\n\nvoid SettingsDialog::createLoader()\n{\n\tLoaderDialog dialog;\n\tdialog.exec();\n}\n\n\nvoid SettingsDialog::editLoader()\n{\n\tLoaderItem* item = loadersView_->currentItem();\n\tif(!item) {\n\t\tQMessageBox::critical(this, \"Error\", \"You should select loader item to edit.\");\n\t\treturn;\n\t}\n\n\tif(item->name() == \"default\") {\n\t\tQMessageBox::critical(this, \"Error\", \"You cannot edit default WINE loader.\");\n\t\treturn;\n\t}\n\n\tLoaderDialog dialog;\n\tdialog.setItem(item);\n\tdialog.exec();\n}\n\n\nvoid SettingsDialog::removeLoader()\n{\n\tLoaderItem* item = loadersView_->currentItem();\n\tif(!item) {\n\t\tQMessageBox::critical(this, \"Error\", \"You should select loader item to remove.\");\n\t\treturn;\n\t}\n\n\tQString loader = item->name();\n\n\tif(loader == \"default\") {\n\t\tQMessageBox::critical(this, \"Error\", \"You cannot remove default WINE loader.\");\n\t\treturn;\n\t}\n\n\tQString text = QString(\"Do you really want to remove the '%1' loader?\").arg(loader);\n\tif(QMessageBox::question(this, \"Question\", text) == QMessageBox::Yes) {\n\t\tif(!qApp->loaders()->removeLoader(item))\n\t\t\tQMessageBox::critical(this, \"Error\", \"Unable to delete selected loader.\");\n\t}\n}\n\n\nvoid SettingsDialog::accept()\n{\n\tif(binariesPathEdit_->text().isEmpty()) {\n\t\tQMessageBox::critical(this, \"Error\", \"You should select the binaries location.\");\n\t\treturn;\n\t}\n\n\tif(!QDir().exists(binariesPathEdit_->text())) {\n\t\tQMessageBox::critical(this, \"Error\", \"Binaries location doesn't exists.\");\n\t\treturn;\n\t}\n\n\tQFileInfo fileInfo(binariesPathEdit_->text());\n\tif(!fileInfo.isDir()) {\n\t\tQMessageBox::critical(this, \"Error\", \"Binaries location is not a directory.\");\n\t\treturn;\n\t}\n\n\tQStringList files = qApp->checkMissingBinaries(binariesPathEdit_->text());\n\tif(!files.isEmpty()) {\n\t\tQString message = \"Some binaries aren't found, please choose the correct\\n\"\n\t\t\t\t\"binaries location!\\n\\nThe missed binaries are:\\n\\n\";\n\t\tforeach(const QString& fileName, files)\n\t\t\tmessage += fileName + '\\n';\n\n\t\tQMessageBox::critical(this, \"Error\", message);\n\t\treturn;\n\t}\n\n\tif(logSocketEdit_->text().isEmpty()) {\n\t\tQMessageBox::critical(this, \"Error\", \"Log socket is not defined\");\n\t\treturn;\n\t}\n\n\tAirwave::Storage* storage = qApp->storage();\n\n\tint index = logLevelCombo_->currentIndex();\n\tAirwave::LogLevel level = static_cast<Airwave::LogLevel>(index);\n\n\tif(level == Airwave::LogLevel::kFlood) {\n\t\tQString message = \"<b>WARNING!</b><br>\"\n\t\t\t\t\"By using the 'flood' log level as default, you will get an enormous \"\n\t\t\t\t\"count of log messages from links with 'default' log level, the \"\n\t\t\t\t\"performance will be low and the audio playback may become very choppy.\"\n\t\t\t\t\"<br><br>\"\n\t\t\t\t\"Do you really want to proceed?\";\n\n\t\tif(QMessageBox::question(this, \"Question\", message) == QMessageBox::No)\n\t\t\treturn;\n\t}\n\n\tLogSocket* socket = qApp->logSocket();\n\tif(logSocketEdit_->text() != socket->id()) {\n\t\tsocket->close();\n\t\tsocket->listen(logSocketEdit_->text());\n\t}\n\n\tstorage->setDefaultLogLevel(level);\n\tstorage->setBinariesPath(binariesPathEdit_->text().toStdString());\n\n\tstorage->save();\n\tQDialog::accept();\n}\n"
  },
  {
    "path": "src/manager/forms/settingsdialog.h",
    "content": "#ifndef FORMS_SETTINGSDIALOG_H\n#define FORMS_SETTINGSDIALOG_H\n\n#include <QDialog>\n\n\nclass QComboBox;\nclass QDialogButtonBox;\nclass QLabel;\nclass QPushButton;\nclass LineEdit;\nclass LoadersModel;\nclass LoadersView;\nclass PrefixesModel;\nclass PrefixesView;\n\n\nclass SettingsDialog : public QDialog {\n\tQ_OBJECT\npublic:\n\tSettingsDialog(QWidget* parent = nullptr);\n\t~SettingsDialog();\n\nprivate:\n\tLineEdit* vstPathEdit_;\n\tLineEdit* binariesPathEdit_;\n\tLineEdit* logSocketEdit_;\n\tQComboBox* logLevelCombo_;\n\tPrefixesView* prefixesView_;\n\tQPushButton* addPrefixButton_;\n\tQPushButton* editPrefixButton_;\n\tQPushButton* removePrefixButton_;\n\tLoadersView* loadersView_;\n\tQPushButton* addLoaderButton_;\n\tQPushButton* editLoaderButton_;\n\tQPushButton* removeLoaderButton_;\n\tQDialogButtonBox* buttons_;\n\n\tvoid setupUi();\n\nprivate slots:\n\tvoid browseForVstPath();\n\tvoid browseForBinariesPath();\n\tvoid browseForSocketPath();\n\tvoid createPrefix();\n\tvoid editPrefix();\n\tvoid removePrefix();\n\tvoid createLoader();\n\tvoid editLoader();\n\tvoid removeLoader();\n\tvoid accept();\n};\n\n\n#endif // FORMS_SETTINGSDIALOG_H\n"
  },
  {
    "path": "src/manager/main.cpp",
    "content": "#include \"common/config.h\"\n#include \"core/application.h\"\n#include \"forms/mainform.h\"\n\n\nint main(int argc, char** argv)\n{\n\tApplication application(argc, argv);\n\n\tif(application.isRunning())\n\t\treturn EXIT_SUCCESS;\n\n\tapplication.setOrganizationName(\"phantom-code\");\n\tapplication.setOrganizationDomain(\"darkhub.net\");\n\tapplication.setApplicationName(PROJECT_NAME \"-manager\");\n\tapplication.setApplicationVersion(VERSION_STRING);\n\n\tMainForm mainForm;\n\tmainForm.show();\n\n\tapplication.setActivationWindow(&mainForm);\n\treturn application.exec();\n}\n"
  },
  {
    "path": "src/manager/models/directorymodel.cpp",
    "content": "#include \"directorymodel.h\"\n\n#include <QApplication>\n#include <QIcon>\n#include <QStyle>\n\n\nDirectoryItem::DirectoryItem(const QFileInfo& info) :\n\tinfo_(info),\n\ttype_(getType())\n{\n}\n\n\nbool DirectoryItem::isDirectory() const\n{\n\treturn info_.isDir();\n}\n\n\nQString DirectoryItem::name() const\n{\n\treturn info_.fileName();\n}\n\n\nQString DirectoryItem::path() const\n{\n\treturn info_.filePath();\n}\n\n\nQString DirectoryItem::fullPath() const\n{\n\treturn info_.canonicalFilePath();\n}\n\n\ni64 DirectoryItem::size() const\n{\n\treturn i64(info_.size());\n}\n\n\nQString DirectoryItem::humanReadableSize() const\n{\n\tconst char* kUnitsTable[] = { \" bytes\", \" KiB\", \" MiB\", \" GiB\", \" TiB\" };\n\tdouble value = size();\n\tuint i = 0;\n\n\twhile(value >= 1024.0 && i < sizeof(kUnitsTable)) {\n\t\tvalue /= 1024.0;\n\t\t++i;\n\t}\n\n\treturn QString().setNum(value, 'f', 1) + kUnitsTable[i];\n}\n\n\nQString DirectoryItem::type() const\n{\n\treturn type_;\n}\n\n\nQString DirectoryItem::getType() const\n{\n\tif(info_.isRoot())\n\t\treturn \"Drive\";\n\n\tif(info_.isFile()) {\n\t\tif(!info_.suffix().isEmpty())\n\t\t\treturn QString(\"%1 File\").arg(info_.suffix());\n\n\t\treturn \"File\";\n\t}\n\n\tif(info_.isDir())\n\t\treturn \"Folder\";\n\n\tif(info_.isSymLink())\n\t\treturn \"Shortcut\";\n\n\treturn \"Unknown\";\n}\n\n\nDirectoryModel::DirectoryModel(QObject* parent) :\n\tGenericTreeModel<DirectoryItem>(new DirectoryItem(), parent),\n\tfilters_(QDir::AllEntries),\n\tisFilesEnabled_(true)\n{\n\tconnect(&watcher_,\n\t\t\tSIGNAL(directoryChanged(QString)),\n\t\t\tSLOT(update(QString)));\n}\n\n\nint DirectoryModel::columnCount(const QModelIndex& parent) const\n{\n\tQ_UNUSED(parent);\n\treturn 3;\n}\n\n\nQVariant DirectoryModel::data(const QModelIndex& index, int role) const\n{\n\tif(index.isValid()) {\n\t\tDirectoryItem* item = indexToItem(index);\n\n\t\tif(role == Qt::DisplayRole) {\n\t\t\tint column = index.column();\n\t\t\tif(column == 0) {\n\t\t\t\treturn item->name();\n\t\t\t}\n\t\t\telse if(column == 1) {\n\t\t\t\tif(!item->isDirectory()) {\n\t\t\t\t\treturn item->humanReadableSize();\n\t\t\t\t}\n\n\t\t\t\treturn \"<DIR>\";\n\t\t\t}\n\t\t\telse if(column == 2) {\n\t\t\t\treturn item->type();\n\t\t\t}\n\t\t}\n\t\telse if(role == Qt::DecorationRole && index.column() == 0) {\n\t\t\tif(item->name() == \"..\") {\n\t\t\t\treturn qApp->style()->standardIcon(QStyle::SP_ArrowUp);\n\t\t\t}\n\t\t\telse if(item->isDirectory()) {\n\t\t\t\treturn qApp->style()->standardIcon(QStyle::SP_DirIcon);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn qApp->style()->standardIcon(QStyle::SP_FileIcon);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nQVariant DirectoryModel::headerData(int section, Qt::Orientation orientation,\n\t\tint role) const\n{\n\tQ_UNUSED(orientation);\n\n\tif(role == Qt::DisplayRole) {\n\t\tif(section == 0) {\n\t\t\treturn \"Name\";\n\t\t}\n\t\telse if(section == 1) {\n\t\t\treturn \"Size\";\n\t\t}\n\t\telse if(section == 2) {\n\t\t\treturn \"Type\";\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nQt::ItemFlags DirectoryModel::flags(const QModelIndex& index) const\n{\n\tQt::ItemFlags flags = GenericTreeModel<DirectoryItem>::flags(index);\n\n\tDirectoryItem* item = indexToItem(index);\n\tif(item && !item->isDirectory() && !isFilesEnabled_)\n\t\tflags &= ~Qt::ItemIsEnabled;\n\n\treturn flags;\n}\n\n\nvoid DirectoryModel::update(const QString& path)\n{\n\tQDir dir(path);\n\tif(!dir.exists())\n\t\treturn;\n\n\tclear();\n\n\tdir_ = dir.canonicalPath();\n\tdir_.setFilter(filters_);\n\tdir_.setNameFilters(nameFilters_);\n\n\tfor(const QFileInfo& info : dir_.entryInfoList()) {\n\t\tif(dir.isRoot() && info.fileName() == \"..\")\n\t\t\tcontinue;\n\n\t\tDirectoryItem* item = new DirectoryItem(info);\n\t\troot()->insertChild(item);\n\t}\n\n\tsort(0, Qt::AscendingOrder);\n}\n\n\nQString DirectoryModel::directory() const\n{\n\treturn dir_.canonicalPath();\n}\n\n\nvoid DirectoryModel::setDirectory(const QString& path)\n{\n\twatcher_.removePath(dir_.canonicalPath());\n\n\tupdate(path);\n\n\twatcher_.addPath(dir_.canonicalPath());\n\temit directoryChanged(dir_.canonicalPath());\n}\n\n\nQStringList DirectoryModel::nameFilters() const\n{\n\treturn QStringList();\n}\n\n\nvoid DirectoryModel::setNameFilters(const QStringList& filters)\n{\n\tnameFilters_ = filters;\n\tupdate(directory());\n}\n\n\nQDir::Filters DirectoryModel::filters() const\n{\n\treturn filters_;\n}\n\n\nvoid DirectoryModel::setFilters(QDir::Filters filters)\n{\n\tfilters_ = filters;\n\tupdate(directory());\n}\n\n\nvoid DirectoryModel::sort(int column, Qt::SortOrder order)\n{\n\tQ_UNUSED(column);\n\temit layoutAboutToBeChanged();\n\n\tif(order == Qt::AscendingOrder) {\n\t\troot()->sortChildren(lessThan);\n\t}\n\telse {\n\t\troot()->sortChildren(greaterThan);\n\t}\n\n\temit layoutChanged();\n}\n\n\nbool DirectoryModel::lessThan(DirectoryItem* item1, DirectoryItem* item2)\n{\n\tif(item1->name() == \"..\") {\n\t\treturn true;\n\t}\n\telse if(item2->name() == \"..\") {\n\t\treturn false;\n\t}\n\n\tif(item1->isDirectory() == item2->isDirectory())\n\t\treturn item1->name().compare(item2->name(), Qt::CaseInsensitive) < 0;\n\n\treturn item1->isDirectory();\n}\n\n\nbool DirectoryModel::greaterThan(DirectoryItem* item1, DirectoryItem* item2)\n{\n\tif(item1->name() == \"..\") {\n\t\treturn false;\n\t}\n\telse if(item2->name() == \"..\") {\n\t\treturn true;\n\t}\n\n\tif(item1->isDirectory() == item2->isDirectory())\n\t\treturn item1->name().compare(item2->name(), Qt::CaseInsensitive) > 0;\n\n\treturn item2->isDirectory();\n}\n\n\nbool DirectoryModel::isFilesEnabled() const\n{\n\treturn isFilesEnabled_;\n}\n\n\nvoid DirectoryModel::setFilesEnabled(bool enabled)\n{\n\tif(isFilesEnabled_ == enabled)\n\t\treturn;\n\n\tisFilesEnabled_ = enabled;\n\tupdate(directory());\n}\n"
  },
  {
    "path": "src/manager/models/directorymodel.h",
    "content": "#ifndef MODELS_DIRECTORYMODEL_H\n#define MODELS_DIRECTORYMODEL_H\n\n#include <QDir>\n#include <QFileSystemWatcher>\n#include \"common/types.h\"\n#include \"models/generictreemodel.h\"\n\n\nclass DirectoryItem : public GenericTreeItem<DirectoryItem> {\npublic:\n\tDirectoryItem(const QFileInfo& info = QFileInfo());\n\n\tbool isDirectory() const;\n\n\tQString name() const;\n\tQString path() const;\n\tQString fullPath() const;\n\ti64 size() const;\n\n\tQString humanReadableSize() const;\n\tQString type() const;\n\nprivate:\n\tQFileInfo info_;\n\tQString type_;\n\n\tQString getType() const;\n};\n\n\nclass DirectoryModel : public GenericTreeModel<DirectoryItem> {\n\tQ_OBJECT\npublic:\n\tDirectoryModel(QObject* parent = nullptr);\n\n\tint columnCount(const QModelIndex& parent = QModelIndex()) const;\n\n\tQVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;\n\n\tQVariant headerData(int section, Qt::Orientation orientation,\n\t\t\tint role = Qt::DisplayRole) const;\n\n\tQt::ItemFlags flags(const QModelIndex& index) const;\n\n\tQString directory() const;\n\tQStringList nameFilters() const;\n\tQDir::Filters filters() const;\n\tbool isFilesEnabled() const;\n\npublic slots:\n\tvoid setDirectory(const QString& path);\n\tvoid setNameFilters(const QStringList& filters);\n\tvoid setFilters(QDir::Filters filters);\n\tvoid setFilesEnabled(bool enabled);\n\nsignals:\n\tvoid directoryChanged(const QString& path);\n\nprivate:\n\tQDir dir_;\n\tQFileSystemWatcher watcher_;\n\tQStringList nameFilters_;\n\tQDir::Filters filters_;\n\tbool isFilesEnabled_;\n\n\tvoid sort(int column, Qt::SortOrder order);\n\n\tstatic bool lessThan(DirectoryItem* item1, DirectoryItem* item2);\n\tstatic bool greaterThan(DirectoryItem* item1, DirectoryItem* item2);\n\nprivate slots:\n\n\tvoid update(const QString& path);\n};\n\n\n#endif // MODELS_DIRECTORYMODEL_H\n"
  },
  {
    "path": "src/manager/models/generictreemodel.h",
    "content": "#ifndef MODELS_GENERICTREEMODEL_H\n#define MODELS_GENERICTREEMODEL_H\n\n#include <QAbstractItemModel>\n\n\ntemplate<typename T>\nclass GenericTreeModel;\n\n\ntemplate<typename Derived>\nclass GenericTreeItem {\npublic:\n\ttypedef bool (*LessThanProc)(Derived*, Derived*);\n\n\t/**\n\t * Конструирует новый элемент и устанавливает признак возможности отложенной загрузки\n\t * информации о его дочерних элементах.\n\t */\n\tGenericTreeItem(bool canFetchMore = false);\n\n\tvirtual ~GenericTreeItem();\n\n\t/**\n\t * Возвращает родительский элемент или @c nullptr, в случае его отсутствия.\n\t */\n\tDerived* parent() const;\n\n\t/**\n\t * Возвращает количество дочерниих элементов.\n\t */\n\tint childCount() const;\n\n\t/**\n\t * Возвращает @c true, если @p item является дочерним элементом для текущего.\n\t */\n\tbool hasChild(Derived* item) const;\n\n\t/**\n\t * Возвращает @c true, если элемент имеет хотя бы один дочерний элемент.\n\t */\n\tbool hasChildren() const;\n\n\t/**\n\t * Возвращает дочерний элемент на позиции @p row.\n\t */\n\tDerived* childAt(int row) const;\n\n\t/**\n\t * Возвращает первый дочерний элемент или @c nullptr, в случае его отсутствия.\n\t */\n\tDerived* firstChild() const;\n\n\t/**\n\t * Возвращает последний дочерний элемент или @c nullptr, в случае его отсутствия.\n\t */\n\tDerived* lastChild() const;\n\n\t/**\n\t * Возвращает следующий смежный элемент или @c nullptr, в случае его отсутствия.\n\t */\n\tDerived* nextSibling() const;\n\n\t/**\n\t * Возвращает предыдущий смежный элемент или @c nullptr, в случае его отсутствия.\n\t */\n\tDerived* previousSibling() const;\n\n\t/**\n\t * Производит вставку элемента @p item в дочерние элементы на позицию @p row. Если @p\n\t * row меньше нуля или больше количества дочерних элементов, то элемент @p item\n\t * помещается на последниюю позицию. Право владения элементом @p item передается\n\t * текущему элементу.\n\t *\n\t * @param item вставляемый элемент.\n\t * @param row позиция вставки.\n\t */\n\tvoid insertChild(Derived* item, int row = -1);\n\n\t/**\n\t * Производит перемещение элемента @p item в дочерние элементы на позицию @p row.\n\t * Если @c row меньше нуля или больше количества дочерних элементов, то элемент @p\n\t * item помещается на последнюю позицию. Если @p item является вышестоящим элементом\n\t * или относится к другому экземпляру модели, то перемещения не происходит и функция\n\t * возвращает @c false.\n\t *\n\t * @param item перемещаемый элемент.\n\t * @param row позиция, на которую требуется переместить @p item.\n\t * @return @c true в случае успеха.\n\t */\n\tbool moveChild(Derived* item, int row = -1);\n\n\t/**\n\t * Производит извлечение дочернего элемента с позиции @p row и возвращает этот\n\t * элемент. Если @p row меньше нуля или больше количества дочерних элементов, то\n\t * возвращается @c nullptr. Право владения элементом передается вызывающему коду.\n\t *\n\t * @param row позиция извлекаемого элемента.\n\t * @return извлеченный элемент или @c nullptr.\n\t */\n\tDerived* takeChild(int row);\n\n\t/**\n\t * Производит извлечение дочернего элемента на позиции @p row с последующем его\n\t * уничтожением.\n\t *\n\t * @param row позиция удаляемого элемента.\n\t */\n\tvoid removeChild(int row);\n\n\t/**\n\t * Рекурсивно уничтожает все дочерние элементы.\n\t */\n\tvoid removeChildren();\n\n\tDerived* takeFromParent();\n\n\t/**\n\t * Возвращает позицию элемента в списке дочерних элементов владельца.\n\t */\n\tint row() const;\n\n\t/**\n\t * Возвращает модель, к которой относится элемент, или @c nullptr, если элемент не\n\t * относится ни к одной модели.\n\t */\n\tGenericTreeModel<Derived>* model() const;\n\n\tvoid sortChildren(LessThanProc lessThan);\n\n\t/**\n\t * Возвращает @c true, если информация о дочерних элементах не была получена.\n\t */\n\tbool canFetchMore() const;\n\nprotected:\n\t/**\n\t * Устанавливает признак возможности получения информации о дочерних элементах.\n\t */\n\tvoid setCanFetchMore(bool canFetchMore);\n\n\t/**\n\t * Получает информацию о дочерних элементах. Базовая реализация просто устанавливает\n\t * @p canFetchMore в @c false. При необходимости выполнять отложенную загрузку\n\t * потомки должны переопределить эту функцию и реализовать загрузку в ней.\n\t */\n\tvirtual void fetchMore();\n\n\t/**\n\t * Производит уведомление модели о необходимости обновить данные элемента. Функция\n\t * должна вызываться потомками после любого изменения данных элемента.\n\t */\n\tvoid updateData();\n\n\t/**\n\t * Вызывается при установке элементу новой модели. При необходимости выполнить\n\t * дополнительные действия во время помещения элемента в модель, потомки должны\n\t * переопределить эту функцию и выполнить эти действия в ней. В переопределенной\n\t * функции запрещается производить действия над элементами модели, связанные со\n\t * вставкой, удалением или перемещением, т.к. это может привести к рекурсии или\n\t * непредсказуемому результату.\n\t */\n\tvirtual void attached() { }\n\n\t/**\n\t * Вызывается при извлечении элемента из модели. При необходимости выполнить\n\t * дополнительные действия во время извлечения элемента из модели, потомки должны\n\t * переопределить эту функцию и выполнить эти действия в ней. В переопределенной\n\t * функции запрещается производить действия над элементами модели, связанные со\n\t * вставкой, удалением или перемещением, т.к. это может привести к рекурсии или\n\t * непредсказуемому результату.\n\t */\n\tvirtual void detached() { }\n\n\t/**\n\t * Вызывается при изменении положения элемента. При необходимости выполнить\n\t * дополнительные действия по этому событию (например обновить parent id элемента в\n\t * БД) потомки должны переопределить эту функцию и выполнить требуемые действия в\n\t * ней. В переопределенной функции запрещается производить действия над элементами\n\t * модели, связанные со вставкой, удалением или перемещением, т.к. это может привести\n\t * к рекурсии или непредсказуемому результату.\n\t */\n\tvirtual void reattached() { }\n\n\t/**\n\t * Вызывается сразу после вставки дочернего элемента @p item. Потомки могут\n\t * переобпределить эту функцию, чтобы получать уведомления о событии. В\n\t * переопределенной функции запрещается производить действия над элементами модели,\n\t * связанные с вставкой, удалением или перемещением, т.к. это может привести к\n\t * рекурсии или непредсказуемому результату.\n\t *\n\t * @param item новый дочерний элемент.\n\t */\n\tvirtual void childInserted(Derived* item) { Q_UNUSED(item); }\n\n\t/**\n\t * Вызывается сразу после извлечения дочернего элемента @p item. Потомки могут\n\t * переобпределить эту функцию, чтобы получать уведомления о событии. В\n\t * переопределенной функции запрещается производить действия над элементами модели,\n\t * связанные с вставкой, удалением или перемещением, т.к. это может привести к\n\t * рекурсии или непредсказуемому результату.\n\t *\n\t * @param item извлеченный дочерний элемент.\n\t */\n\tvirtual void childRemoved(Derived* item) { Q_UNUSED(item); }\n\nprivate:\n\tfriend class GenericTreeModel<Derived>;\n\n\tDerived* parent_;\n\tQList<Derived*> children_;\n\tint row_;\n\tGenericTreeModel<Derived>* model_;\n\tbool canFetchMore_;\n\n\n\tvoid attach();\n\tvoid detach();\n};\n\n\ntemplate<typename T>\nclass GenericTreeModel : public QAbstractItemModel {\npublic:\n\ttypedef T ItemType;\n\n\t/**\n\t * Конструирует модель и устанавливает root в качестве фиктивного корневого элемента.\n\t */\n\tGenericTreeModel(T* root = new T(), QObject* parent = nullptr);\n\n\tvirtual ~GenericTreeModel();\n\n\t/**\n\t * Уничтожает все элементы модели.\n\t */\n\tvoid clear();\n\n\t/**\n\t * Преобразует модельный индекс @p index в элемент. В случае невалидного индекса\n\t * возвращает @c rootItem().\n\t */\n\tT* indexToItem(const QModelIndex& index) const;\n\n\t/**\n\t * Преобразует элемент в модельный индекс. В случае, если @p item равен @c nullptr,\n\t * или @p item не принадлежит модели, или @p item равен @c rootItem(), возвращается\n\t * невалидный модельный индекс.\n\t *\n\t * @param item преобразуемый элемент.\n\t * @param column колонка в преобразуемом элементе.\n\t */\n\tQModelIndex itemToIndex(T* item, int column = 0) const;\n\n\t/**\n\t * Возвращает фиктивный корневой элемент модели. Данный элемент является невидимым\n\t * для всех работающих с моделью представлений и служит для унификации интерфейса\n\t * добавления/перемещения/удаления дочерних элементов.\n\t */\n\tT* root() const;\n\n\tvirtual QModelIndex index(int row, int column,\n\t\t\tconst QModelIndex& parent = QModelIndex()) const;\n\n\tvirtual QModelIndex parent(const QModelIndex& index = QModelIndex()) const;\n\n\tvirtual int rowCount(const QModelIndex& parent = QModelIndex()) const;\n\n\tvirtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const;\n\n\tvirtual bool canFetchMore(const QModelIndex& parent = QModelIndex()) const;\n\n\tvirtual void fetchMore(const QModelIndex& parent = QModelIndex());\n\nprotected:\n\t/**\n\t * Сбрасывает модель в ее первоначальное состояние. Базовая реализация просто\n\t * уничтожает все элементы модели, при этом вызов AbstractItemModel::reset() не\n\t * производится, поэтому функция является безопасной при использовании proxy моделей.\n\t */\n\tvoid reset();\n\n\tvoid updateData(T* item);\n\nprivate:\n\tfriend class GenericTreeItem<T>;\n\n\tT* rootItem_;\n\n\tvoid beginInsert(T* parentItem, int row, int count = 1);\n\tvoid endInsert();\n\n\tbool beginMove(T* srcParent, int srcRow, T* destParent, int destRow, int count = 1);\n\n\tvoid endMove();\n\n\tvoid beginRemove(T* parentItem, int row, int count = 1);\n\tvoid endRemove();\n\n\tQModelIndex createNewIndex(int row, int column);\n\n\tvoid changePersistentIndexes(const QModelIndexList& from,\n\t\t\tconst QModelIndexList& to);\n};\n\n\n//---------------------------------- GenericTreeItem -----------------------------------//\n\n\ntemplate<typename Derived>\nGenericTreeItem<Derived>::GenericTreeItem(bool canFetchMore) :\n\tparent_(nullptr),\n\trow_(0),\n\tmodel_(nullptr),\n\tcanFetchMore_(canFetchMore)\n{\n}\n\n\ntemplate<typename Derived>\nGenericTreeItem<Derived>::~GenericTreeItem()\n{\n\tqDeleteAll(children_);\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::parent() const\n{\n\treturn parent_;\n}\n\n\ntemplate<typename Derived>\nint GenericTreeItem<Derived>::childCount() const\n{\n\treturn children_.count();\n}\n\n\ntemplate<typename Derived>\nbool GenericTreeItem<Derived>::hasChild(Derived* item) const\n{\n\treturn (children_.indexOf(item) != -1);\n}\n\n\ntemplate<typename Derived>\nbool GenericTreeItem<Derived>::hasChildren() const\n{\n\treturn !children_.isEmpty();\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::childAt(int row) const\n{\n\treturn children_.at(row);\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::firstChild() const\n{\n\tif(children_.isEmpty())\n\t\treturn nullptr;\n\n\treturn children_.first();\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::lastChild() const\n{\n\tif(children_.isEmpty())\n\t\treturn nullptr;\n\n\treturn children_.last();\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::nextSibling() const\n{\n\tif(!parent_ || row_ + 1 == parent_->children_.count())\n\t\treturn nullptr;\n\n\treturn parent_->children_.at(row_ + 1);\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::previousSibling() const\n{\n\tif(!parent_ || row_ == 0)\n\t\treturn nullptr;\n\n\treturn parent_->children_.at(row_ - 1);\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::attach()\n{\n\tmodel_ = parent_->model_;\n\n\tDerived* item = static_cast<Derived*>(this);\n\titem = item->firstChild();\n\n\twhile(item) {\n\t\titem->attach();\n\t\titem = item->nextSibling();\n\t}\n\n\tattached();\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::detach()\n{\n\tmodel_ = nullptr;\n\n\tDerived* item = static_cast<Derived*>(this);\n\titem = item->firstChild();\n\n\twhile(item) {\n\t\titem->detach();\n\t\titem = item->nextSibling();\n\t}\n\n\tdetached();\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::insertChild(Derived* item, int row)\n{\n\tQ_ASSERT(item);\n\n\tif(item->parent_)\n\t\titem->parent_->takeChild(item->row());\n\n\titem->parent_ = static_cast<Derived*>(this);\n\n\tif((row < 0) || (row > children_.count()))\n\t\titem->row_ = children_.count();\n\telse\n\t\titem->row_ = row;\n\n\tif(model_)\n\t\tmodel_->beginInsert(item->parent_, item->row_);\n\n\tfor(int i = item->row_; i < children_.count(); ++i)\n\t\tchildren_[i]->row_++;\n\n\tchildren_.insert(item->row_, item);\n\n\tif(model_) {\n\t\titem->attach();\n\t\tmodel_->endInsert();\n\t}\n\n\tchildInserted(item);\n}\n\n\ntemplate<typename Derived>\nbool GenericTreeItem<Derived>::moveChild(Derived* item, int row)\n{\n\tQ_ASSERT(item);\n\n\t// Перемещать элементы можно только в пределах одной модели\n\tif(item->model_ != model_)\n\t\treturn false;\n\n\tif((row < 0) || (row > children_.count()))\n\t\trow = children_.count();\n\n\tint modelRow = row;\n\n\tDerived* oldParent = item->parent_;\n\tDerived* newParent = static_cast<Derived*>(this);\n\n\tif(oldParent == newParent) {\n\t\t// Если перемещение идет на свое собственно место, дополнительные действия не\n\t\t// требуются\n\t\tif(item->row_ == row)\n\t\t\treturn true;\n\n\t\t// Перемещение вниз\n\t\tif(item->row_ < row)\n\t\t\t++modelRow;\n\t}\n\n\t// Нельзя перемещать элемент в один из его дочерних элементов\n\tDerived* temp = static_cast<Derived*>(this);\n\twhile(temp) {\n\t\tif(item == temp)\n\t\t\treturn false;\n\n\t\ttemp = temp->parent_;\n\t}\n\n\tif(model_ && !model_->beginMove(oldParent, item->row_, newParent, modelRow))\n\t\treturn false;\n\n\tif(oldParent == newParent) {\n\t\tif(row > item->row_) {\n\t\t\tfor(int i = item->row_ + 1; i < row; ++i)\n\t\t\t\tchildren_[i]->row_--;\n\n\t\t\tchildren_.move(item->row_, row - 1);\n\t\t\titem->row_ = row - 1;\n\t\t}\n\t\telse if(row < item->row_) {\n\t\t\tfor(int i = row; i < item->row_; ++i)\n\t\t\t\tchildren_[i]->row_++;\n\n\t\t\tchildren_.move(item->row_, row);\n\t\t\titem->row_ = row;\n\t\t}\n\t}\n\telse {\n\t\toldParent->children_.takeAt(item->row_);\n\n\t\tfor(int i = item->row_; i < oldParent->children_.count(); ++i)\n\t\t\toldParent->children_[i]->row_--;\n\n\t\titem->row_ = row;\n\t\titem->parent_ = newParent;\n\n\t\tfor(int i = item->row_; i < newParent->children_.count(); ++i)\n\t\t\tnewParent->children_[i]->row_++;\n\n\t\tchildren_.insert(row, item);\n\t}\n\n\tif(model_)\n\t\tmodel_->endMove();\n\n\tif(oldParent != newParent) {\n\t\toldParent->childRemoved(item);\n\t\tnewParent->childInserted(item);\n\t}\n\n\titem->reattached();\n\treturn true;\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::takeChild(int row)\n{\n\tif(model_)\n\t\tmodel_->beginRemove(static_cast<Derived*>(this), row);\n\n\tDerived* item = children_.takeAt(row);\n\n\tfor(int i = item->row_; i < children_.count(); ++i)\n\t\tchildren_[i]->row_--;\n\n\titem->parent_ = nullptr;\n\titem->row_ = 0;\n\titem->model_ = nullptr;\n\n\titem->detach();\n\n\tif(model_)\n\t\tmodel_->endRemove();\n\n\tchildRemoved(item);\n\treturn item;\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::removeChild(int row)\n{\n\tdelete takeChild(row);\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::removeChildren()\n{\n\tint count = children_.count();\n\n\t// Удаляем дочерние элементы от последнего к первому, чтобы избежать лишних\n\t// копирований элементов внутри children_.\n\twhile(count--)\n\t\tremoveChild(count);\n}\n\n\ntemplate<typename Derived>\nDerived* GenericTreeItem<Derived>::takeFromParent()\n{\n\tif(parent_)\n\t\treturn parent_->takeChild(row_);\n\n\treturn static_cast<Derived*>(this);\n}\n\n\ntemplate<typename Derived>\nint GenericTreeItem<Derived>::row() const\n{\n\treturn row_;\n}\n\n\ntemplate<typename Derived>\nbool GenericTreeItem<Derived>::canFetchMore() const\n{\n\treturn canFetchMore_;\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::setCanFetchMore(bool canFetchMore)\n{\n\tcanFetchMore_ = canFetchMore;\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::fetchMore()\n{\n\tcanFetchMore_ = false;\n}\n\n\ntemplate<typename Derived>\nGenericTreeModel<Derived>* GenericTreeItem<Derived>::model() const\n{\n\treturn model_;\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::updateData()\n{\n\tif(model_)\n\t\tmodel_->updateData(static_cast<Derived*>(this));\n}\n\n\ntemplate<typename Derived>\nvoid GenericTreeItem<Derived>::sortChildren(LessThanProc lessThan)\n{\n\tqSort(children_.begin(), children_.end(), lessThan);\n\n\tif(model_) {\n\t\tQModelIndexList fromIndexes;\n\t\tQModelIndexList toIndexes;\n\n\t\tfor(int row = 0; row < children_.count(); ++row) {\n\t\t\tDerived* item = children_[row];\n\n\t\t\tint oldRow = item->row_;\n\t\t\titem->row_ = row;\n\n\t\t\tfor(int i = 0; i < model()->columnCount(); ++i) {\n\t\t\t\tfromIndexes.append(model_->createNewIndex(oldRow, i));\n\t\t\t\ttoIndexes.append(model_->createNewIndex(row, i));\n\t\t\t}\n\t\t}\n\n\t\tmodel_->changePersistentIndexes(fromIndexes, toIndexes);\n\t}\n}\n\n\n//---------------------------------- GenericTreeModel ----------------------------------//\n\n\ntemplate<typename T>\nGenericTreeModel<T>::GenericTreeModel(T* root, QObject* parent) :\n\tQAbstractItemModel(parent),\n\trootItem_(root)\n{\n\tQ_ASSERT(root);\n\trootItem_->model_ = this;\n\n\tif(rootItem_->hasChildren()) {\n\t\tbeginInsert(rootItem_, 0, rootItem_->childCount());\n\n\t\tforeach(T* item, rootItem_->children_)\n\t\t\titem->model_ = this;\n\n\t\tendInsert();\n\t}\n}\n\n\ntemplate<typename T>\nGenericTreeModel<T>::~GenericTreeModel()\n{\n\tdelete rootItem_;\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::clear()\n{\n\tbeginResetModel();\n\trootItem_->removeChildren();\n\tendResetModel();\n}\n\n\ntemplate<typename T>\nT* GenericTreeModel<T>::indexToItem(const QModelIndex& index) const\n{\n\tif(!index.isValid())\n\t\treturn rootItem_;\n\n\treturn static_cast<T*>(index.internalPointer());\n}\n\n\ntemplate<typename T>\nQModelIndex GenericTreeModel<T>::itemToIndex(T* item, int column) const\n{\n\tif(!item || item->model() != this || !item->parent())\n\t\treturn QModelIndex();\n\n\treturn createIndex(item->row(), column, item);\n}\n\n\ntemplate<typename T>\nT* GenericTreeModel<T>::root() const\n{\n\treturn rootItem_;\n}\n\n\ntemplate<typename T>\nQModelIndex GenericTreeModel<T>::index(int row, int column,\n\t\tconst QModelIndex& parent) const\n{\n\tQ_UNUSED(column);\n\n\tT* parentItem = indexToItem(parent);\n\tif(!parentItem || (row < 0) || (parentItem->childCount() <= row))\n\t\treturn QModelIndex();\n\n\treturn itemToIndex(parentItem->childAt(row), column);\n}\n\n\ntemplate<typename T>\nQModelIndex GenericTreeModel<T>::parent(const QModelIndex& index) const\n{\n\treturn itemToIndex(indexToItem(index)->parent());\n}\n\n\ntemplate<typename T>\nint GenericTreeModel<T>::rowCount(const QModelIndex& parent) const\n{\n\tT* parentItem = indexToItem(parent);\n\treturn parentItem->childCount();\n}\n\n\ntemplate<typename T>\nbool GenericTreeModel<T>::hasChildren(const QModelIndex& parent) const\n{\n\tT* item = indexToItem(parent);\n\tif(item->canFetchMore())\n\t\treturn true;\n\telse\n\t\treturn item->hasChildren();\n}\n\n\ntemplate<typename T>\nbool GenericTreeModel<T>::canFetchMore(const QModelIndex& parent) const\n{\n\tT* item = indexToItem(parent);\n\treturn item->canFetchMore();\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::fetchMore(const QModelIndex& parent)\n{\n\tT* item = indexToItem(parent);\n\tif(item->canFetchMore())\n\t\titem->fetchMore();\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::reset()\n{\n\tclear();\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::updateData(T* item)\n{\n\tQModelIndex from = itemToIndex(item);\n\tQModelIndex parent = from.parent();\n\tint last = qMax(columnCount() - 1, 0);\n\tQModelIndex to = index(from.row(), last, parent);\n\temit dataChanged(from, to);\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::beginInsert(T* parentItem, int row, int count)\n{\n\tbeginInsertRows(itemToIndex(parentItem), row, row + count - 1);\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::endInsert()\n{\n\tendInsertRows();\n}\n\n\ntemplate<typename T>\nbool GenericTreeModel<T>::beginMove(T* srcParent, int srcRow, T* destParent, int destRow,\n\t\tint count)\n{\n\treturn beginMoveRows(itemToIndex(srcParent), srcRow, srcRow + count - 1,\n\t\t\titemToIndex(destParent), destRow);\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::endMove()\n{\n\tendMoveRows();\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::beginRemove(T* parentItem, int row, int count)\n{\n\tbeginRemoveRows(itemToIndex(parentItem), row, row + count - 1);\n}\n\n\ntemplate<typename T>\nvoid GenericTreeModel<T>::endRemove()\n{\n\tendRemoveRows();\n}\n\n\ntemplate<typename T>\ninline QModelIndex GenericTreeModel<T>::createNewIndex(int row, int column)\n{\n\treturn createIndex(row, column);\n}\n\n\ntemplate<typename T>\ninline void GenericTreeModel<T>::changePersistentIndexes(\n\t\tconst QModelIndexList& from, const QModelIndexList& to)\n{\n\tchangePersistentIndexList(from, to);\n}\n\n\n#endif // MODELS_GENERICTREEMODEL_H\n"
  },
  {
    "path": "src/manager/models/linksmodel.cpp",
    "content": "#include \"linksmodel.h\"\n\n#include <QDir>\n#include <QIcon>\n#include \"core/application.h\"\n\n\nLinkItem::LinkItem(Storage::Link link) :\n\tlink_(link)\n{\n\tif(!link_.isNull()) {\n\t\tStorage* storage = qApp->storage();\n\t\tQString prefix = QString::fromStdString(storage->prefix(link_.prefix()).path());\n\n\t\tQFileInfo info(QDir(prefix), QString::fromStdString(link_.target()));\n\t\tarch_ = ModuleInfo::instance()->getArch(info.absoluteFilePath().toStdString());\n\t}\n}\n\n\nQString LinkItem::name() const\n{\n\tQString path = QString::fromStdString(link_.path());\n\tint pos = path.lastIndexOf('/');\n\tif(pos == -1)\n\t\treturn QString();\n\n\treturn path.mid(pos + 1, path.count() - pos - 4);\n}\n\n\nvoid LinkItem::setName(const QString& name)\n{\n\tQString path = QString::fromStdString(link_.path());\n\tint pos = path.lastIndexOf('/');\n\tif(pos == -1)\n\t\treturn;\n\n\tpath.truncate(pos + 1);\n\tpath += name + \".so\";\n\tif(link_.setPath(path.toStdString()))\n\t\tupdateData();\n}\n\n\nQString LinkItem::location() const\n{\n\tQString path = QString::fromStdString(link_.path());\n\tint pos = path.lastIndexOf('/');\n\tif(pos == -1)\n\t\treturn QString();\n\n\treturn path.left(pos);\n}\n\n\nvoid LinkItem::setLocation(const QString& path)\n{\n\tQString location = path + '/' + name() + \".so\";\n\tif(link_.setPath(location.toStdString()))\n\t\tupdateData();\n}\n\n\nModuleInfo::Arch LinkItem::arch() const\n{\n\treturn arch_;\n}\n\n\nQString LinkItem::prefix() const\n{\n\treturn QString::fromStdString(link_.prefix());\n}\n\n\nvoid LinkItem::setPrefix(const QString& prefix)\n{\n\tlink_.setPrefix(prefix.toStdString());\n\tupdateData();\n}\n\n\nQString LinkItem::loader() const\n{\n\treturn QString::fromStdString(link_.loader());\n}\n\n\nvoid LinkItem::setLoader(const QString& loader)\n{\n\tlink_.setLoader(loader.toStdString());\n\tupdateData();\n}\n\n\nQString LinkItem::target() const\n{\n\treturn QString::fromStdString(link_.target());\n}\n\n\nvoid LinkItem::setTarget(const QString& source)\n{\n\tlink_.setTarget(source.toStdString());\n\tupdateData();\n}\n\n\nQString LinkItem::path() const\n{\n\treturn QString::fromStdString(link_.path());\n}\n\n\nvoid LinkItem::setPath(const QString& path)\n{\n\tif(link_.setPath(path.toStdString()))\n\t\tupdateData();\n}\n\n\nLogLevel LinkItem::logLevel() const\n{\n\treturn link_.logLevel();\n}\n\n\nvoid LinkItem::setLogLevel(LogLevel level)\n{\n\tlink_.setLogLevel(level);\n\tupdateData();\n}\n\n\nLinksModel::LinksModel(QObject* parent) :\n\tGenericTreeModel<LinkItem>(new LinkItem(), parent)\n{\n\tupdate();\n}\n\n\nint LinksModel::columnCount(const QModelIndex& parent) const\n{\n\tQ_UNUSED(parent);\n\treturn 5;\n}\n\n\nQVariant LinksModel::data(const QModelIndex& index, int role) const\n{\n\tif(index.isValid()) {\n\t\tLinkItem* item = indexToItem(index);\n\n\t\tif(role == Qt::DisplayRole) {\n\t\t\tint column = index.column();\n\t\t\tif(column == 0) {\n\t\t\t\treturn item->name();\n\t\t\t}\n\t\t\telse if(column == 1) {\n\t\t\t\treturn logLevelString(item->logLevel());\n\t\t\t}\n\t\t\telse if(column == 2) {\n\t\t\t\treturn item->loader();\n\t\t\t}\n\t\t\telse if(column == 3) {\n\t\t\t\treturn item->prefix();\n\t\t\t}\n\t\t\telse if(column == 4) {\n\t\t\t\treturn item->target();\n\t\t\t}\n\t\t}\n\t\telse if(role == Qt::ToolTipRole) {\n\t\t\tint column = index.column();\n\t\t\tif(column == 0) {\n\t\t\t\treturn item->path();\n\t\t\t}\n\t\t\tif(column == 1) {\n\n\t\t\t}\n\t\t\tif(column == 2) {\n\t\t\t\tauto loader = qApp->storage()->loader(item->loader().toStdString());\n\t\t\t\treturn QString::fromStdString(loader.path());\n\t\t\t}\n\t\t\tif(column == 3) {\n\t\t\t\tauto prefix = qApp->storage()->prefix(item->prefix().toStdString());\n\t\t\t\treturn QString::fromStdString(prefix.path());\n\t\t\t}\n\t\t\tif(column == 4) {\n\t\t\t\treturn item->target();\n\t\t\t}\n\t\t}\n\t\telse if(role == Qt::DecorationRole) {\n\t\t\tint column = index.column();\n\t\t\tif(column == 0) {\n\t\t\t\tif(item->arch() == ModuleInfo::kArch32) {\n\t\t\t\t\treturn QIcon(\":/32bit.png\");\n\t\t\t\t}\n\t\t\t\telse if(item->arch() == ModuleInfo::kArch64) {\n\t\t\t\t\treturn QIcon(\":/64bit.png\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn QIcon(\":/unknown.png\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if(column == 1) {\n\t\t\t\tswitch(item->logLevel()) {\n\t\t\t\tcase LogLevel::kDefault:\n\t\t\t\t\treturn QIcon(\":/star.png\");\n\n\t\t\t\tcase LogLevel::kQuiet:\n\t\t\t\t\treturn QIcon(\":/mute.png\");\n\n\t\t\t\tcase LogLevel::kError:\n\t\t\t\t\treturn QIcon(\":/warning.png\");\n\n\t\t\t\tcase LogLevel::kTrace:\n\t\t\t\t\treturn QIcon(\":/trace.png\");\n\n\t\t\t\tcase LogLevel::kDebug:\n\t\t\t\t\treturn QIcon(\":/bug.png\");\n\n\t\t\t\tcase LogLevel::kFlood:\n\t\t\t\t\treturn QIcon(\":/scull.png\");\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn QIcon();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nQVariant LinksModel::headerData(int section, Qt::Orientation orientation,\n\t\tint role) const\n{\n\tQ_UNUSED(orientation);\n\n\tif(role == Qt::DisplayRole) {\n\t\tif(section == 0) {\n\t\t\treturn \"Name\";\n\t\t}\n\t\telse if(section == 1) {\n\t\t\treturn \"Log level\";\n\t\t}\n\t\telse if(section == 2) {\n\t\t\treturn \"Loader\";\n\t\t}\n\t\telse if(section == 3) {\n\t\t\treturn \"Prefix\";\n\t\t}\n\t\telse if(section == 4) {\n\t\t\treturn \"VST plugin path (relative to prefix)\";\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nLinkItem* LinksModel::createLink(const QString& name, const QString& location,\n\t\tconst QString& target, const QString& prefix, const QString& loader)\n{\n\tStorage* s = qApp->storage();\n\n\tQFileInfo info(QDir(location), name + \".so\");\n\tstd::string path = info.absoluteFilePath().toStdString();\n\n\tStorage::Link link = s->createLink(path, target.toStdString(), prefix.toStdString(),\n\t\t\tloader.toStdString());\n\n\tif(!link)\n\t\treturn nullptr;\n\n\tLinkItem* item = new LinkItem(link);\n\troot()->insertChild(item);\n\treturn item;\n}\n\n\nbool LinksModel::removeLink(LinkItem* item)\n{\n\tif(!item || item->model() != this)\n\t\treturn false;\n\n\tif(qApp->storage()->removeLink(item->link_)) {\n\t\tdelete item->takeFromParent();\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nvoid LinksModel::update()\n{\n\tclear();\n\n\tStorage* s = qApp->storage();\n\n\tauto link = s->link();\n\twhile(!link.isNull()) {\n\t\tLinkItem* item = new LinkItem(link);\n\t\troot()->insertChild(item);\n\t\tlink = link.next();\n\t}\n}\n\n\nQString LinksModel::logLevelString(LogLevel level) const\n{\n\tswitch(level) {\n\tdefault:\n\tcase LogLevel::kDefault:\n\t\treturn \"default\";\n\n\tcase LogLevel::kQuiet:\n\t\treturn \"quiet\";\n\n\tcase LogLevel::kError:\n\t\treturn \"error\";\n\n\tcase LogLevel::kTrace:\n\t\treturn \"trace\";\n\n\tcase LogLevel::kDebug:\n\t\treturn \"debug\";\n\n\tcase LogLevel::kFlood:\n\t\treturn \"flood\";\n\t}\n}\n"
  },
  {
    "path": "src/manager/models/linksmodel.h",
    "content": "#ifndef MODELS_LINKSMODEL_H\n#define MODELS_LINKSMODEL_H\n\n#include \"common/logger.h\"\n#include \"common/moduleinfo.h\"\n#include \"common/storage.h\"\n#include \"models/generictreemodel.h\"\n\n\nusing Airwave::Storage;\nusing Airwave::LogLevel;\n\n\nclass LinkItem : public GenericTreeItem<LinkItem> {\npublic:\n\tLinkItem(Storage::Link link = Storage::Link());\n\n\tQString name() const;\n\tvoid setName(const QString& name);\n\n\tQString location() const;\n\tvoid setLocation(const QString& path);\n\n\tModuleInfo::Arch arch() const;\n\n\tQString prefix() const;\n\tvoid setPrefix(const QString& prefix);\n\n\tQString loader() const;\n\tvoid setLoader(const QString& loader);\n\n\tQString target() const;\n\tvoid setTarget(const QString& source);\n\n\tQString path() const;\n\tvoid setPath(const QString& path);\n\n\tLogLevel logLevel() const;\n\tvoid setLogLevel(LogLevel level);\n\nprivate:\n\tfriend class LinksModel;\n\n\tStorage::Link link_;\n\tModuleInfo::Arch arch_;\n\tLogLevel level_;\n};\n\n\nclass LinksModel : public GenericTreeModel<LinkItem> {\n\tQ_OBJECT\npublic:\n\tLinksModel(QObject* parent = nullptr);\n\n\tint columnCount(const QModelIndex& parent = QModelIndex()) const;\n\tQVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;\n\n\tQVariant headerData(int section, Qt::Orientation orientation,\n\t\t\tint role = Qt::DisplayRole) const;\n\n\tLinkItem* createLink(const QString& name, const QString& location,\n\t\t\tconst QString& target, const QString& prefix, const QString& loader);\n\n\tbool removeLink(LinkItem* item);\n\n\tvoid update();\n\nprivate:\n\tQString logLevelString(LogLevel level) const;\n};\n\n\n#endif // MODELS_LINKSMODEL_H\n"
  },
  {
    "path": "src/manager/models/loadersmodel.cpp",
    "content": "#include \"loadersmodel.h\"\n\n#include <QIcon>\n#include \"core/application.h\"\n#include \"models/linksmodel.h\"\n\n\nLoaderItem::LoaderItem(Storage::Loader loader) :\n\tloader_(loader)\n{\n}\n\n\nQString LoaderItem::name() const\n{\n\treturn QString::fromStdString(loader_.name());\n}\n\n\nvoid LoaderItem::setName(const QString& name)\n{\n\tif(loader_.setName(name.toStdString())) {\n\t\tupdateData();\n\t\tqApp->links()->update();\n\t}\n}\n\n\nQString LoaderItem::path() const\n{\n\treturn QString::fromStdString(loader_.path());\n}\n\n\nvoid LoaderItem::setPath(const QString& path)\n{\n\tloader_.setPath(path.toStdString());\n\tupdateData();\n}\n\n\nLoadersModel::LoadersModel(QObject* parent) :\n\tGenericTreeModel<LoaderItem>(new LoaderItem(), parent)\n{\n\tStorage* s = qApp->storage();\n\n\tauto loader = s->loader();\n\twhile(!loader.isNull()) {\n\t\tLoaderItem* item = new LoaderItem(loader);\n\t\troot()->insertChild(item);\n\t\tloader = loader.next();\n\t}\n}\n\n\nint LoadersModel::columnCount(const QModelIndex& parent) const\n{\n\tQ_UNUSED(parent);\n\treturn 2;\n}\n\n\nQVariant LoadersModel::data(const QModelIndex& index, int role) const\n{\n\tif(index.isValid()) {\n\t\tLoaderItem* item = indexToItem(index);\n\n\t\tif(role == Qt::DisplayRole) {\n\t\t\tif(index.column() == 0) {\n\t\t\t\treturn item->name();\n\t\t\t}\n\t\t\telse if(index.column() == 1) {\n\t\t\t\treturn item->path();\n\t\t\t}\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nQVariant LoadersModel::headerData(int section, Qt::Orientation orientation,\n\t\tint role) const\n{\n\tQ_UNUSED(orientation);\n\n\tif(role == Qt::DisplayRole) {\n\t\tif(section == 0) {\n\t\t\treturn \"Name\";\n\t\t}\n\t\telse if(section == 1) {\n\t\t\treturn \"Path\";\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nLoaderItem* LoadersModel::createLoader(const QString& name, const QString& path)\n{\n\tStorage* s = qApp->storage();\n\tStorage::Loader loader = s->createLoader(name.toStdString(), path.toStdString());\n\tif(!loader)\n\t\treturn nullptr;\n\n\tLoaderItem* item = new LoaderItem(loader);\n\troot()->insertChild(item);\n\treturn item;\n}\n\n\nbool LoadersModel::removeLoader(LoaderItem* item)\n{\n\tif(!item || item == root())\n\t\treturn false;\n\n\tif(!qApp->storage()->removeLoader(item->loader_))\n\t\treturn false;\n\n\tdelete item->takeFromParent();\n\treturn true;\n}\n"
  },
  {
    "path": "src/manager/models/loadersmodel.h",
    "content": "#ifndef MODELS_LOADERSMODEL_H\n#define MODELS_LOADERSMODEL_H\n\n#include \"generictreemodel.h\"\n#include \"common/storage.h\"\n\n\nusing Airwave::Storage;\n\n\nclass LoaderItem : public GenericTreeItem<LoaderItem> {\npublic:\n\tLoaderItem(Storage::Loader loader = Storage::Loader());\n\n\tQString name() const;\n\tvoid setName(const QString& name);\n\n\tQString path() const;\n\tvoid setPath(const QString& path);\n\nprivate:\n\tfriend class LoadersModel;\n\n\tStorage::Loader loader_;\n};\n\n\nclass LoadersModel : public GenericTreeModel<LoaderItem> {\n\tQ_OBJECT\npublic:\n\tLoadersModel(QObject* parent = nullptr);\n\n\tint columnCount(const QModelIndex& parent = QModelIndex()) const;\n\n\tQVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;\n\n\tQVariant headerData(int section, Qt::Orientation orientation,\n\t\t\tint role = Qt::DisplayRole) const;\n\n\tLoaderItem* createLoader(const QString& name, const QString& path);\n\tbool removeLoader(LoaderItem* item);\n};\n\n\n#endif // MODELS_LOADERSMODEL_H\n"
  },
  {
    "path": "src/manager/models/prefixesmodel.cpp",
    "content": "#include \"prefixesmodel.h\"\n\n#include <QIcon>\n#include \"core/application.h\"\n#include \"models/linksmodel.h\"\n\n\nPrefixItem::PrefixItem(Storage::Prefix prefix) :\n\tprefix_(prefix)\n{\n}\n\n\nQString PrefixItem::name() const\n{\n\treturn QString::fromStdString(prefix_.name());\n}\n\n\nvoid PrefixItem::setName(const QString& name)\n{\n\tif(prefix_.setName(name.toStdString())) {\n\t\tupdateData();\n\t\tqApp->links()->update();\n\t}\n}\n\n\nQString PrefixItem::path() const\n{\n\treturn QString::fromStdString(prefix_.path());\n}\n\n\nvoid PrefixItem::setPath(const QString& path)\n{\n\tprefix_.setPath(path.toStdString());\n\tupdateData();\n}\n\n\nPrefixesModel::PrefixesModel(QObject* parent) :\n\tGenericTreeModel<PrefixItem>(new PrefixItem(), parent)\n{\n\tStorage* s = qApp->storage();\n\n\tauto prefix = s->prefix();\n\twhile(!prefix.isNull()) {\n\t\tPrefixItem* item = new PrefixItem(prefix);\n\t\troot()->insertChild(item);\n\t\tprefix = prefix.next();\n\t}\n}\n\n\nint PrefixesModel::columnCount(const QModelIndex& parent) const\n{\n\tQ_UNUSED(parent);\n\treturn 2;\n}\n\n\nQVariant PrefixesModel::data(const QModelIndex& index, int role) const\n{\n\tif(index.isValid()) {\n\t\tPrefixItem* item = indexToItem(index);\n\n\t\tif(role == Qt::DisplayRole) {\n\t\t\tif(index.column() == 0) {\n\t\t\t\treturn item->name();\n\t\t\t}\n\t\t\telse if(index.column() == 1) {\n\t\t\t\treturn item->path();\n\t\t\t}\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nQVariant PrefixesModel::headerData(int section, Qt::Orientation orientation,\n\t\tint role) const\n{\n\tQ_UNUSED(orientation);\n\n\tif(role == Qt::DisplayRole) {\n\t\tif(section == 0) {\n\t\t\treturn \"Name\";\n\t\t}\n\t\telse if(section == 1) {\n\t\t\treturn \"Path\";\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\n\nPrefixItem* PrefixesModel::createPrefix(const QString& name, const QString& path)\n{\n\tStorage* s = qApp->storage();\n\tStorage::Prefix prefix = s->createPrefix(name.toStdString(), path.toStdString());\n\tif(!prefix)\n\t\treturn nullptr;\n\n\tPrefixItem* item = new PrefixItem(prefix);\n\troot()->insertChild(item);\n\treturn item;\n}\n\n\nbool PrefixesModel::removePrefix(PrefixItem* item)\n{\n\tif(!item || item == root())\n\t\treturn false;\n\n\tif(!qApp->storage()->removePrefix(item->prefix_))\n\t\treturn false;\n\n\tdelete item->takeFromParent();\n\treturn true;\n}\n"
  },
  {
    "path": "src/manager/models/prefixesmodel.h",
    "content": "#ifndef MODELS_PREFIXMODEL_H\n#define MODELS_PREFIXMODEL_H\n\n#include \"generictreemodel.h\"\n#include \"common/storage.h\"\n\n\nusing Airwave::Storage;\n\n\nclass PrefixItem : public GenericTreeItem<PrefixItem> {\npublic:\n\tPrefixItem(Storage::Prefix prefix = Storage::Prefix());\n\n\tQString name() const;\n\tvoid setName(const QString& name);\n\n\tQString path() const;\n\tvoid setPath(const QString& path);\n\nprivate:\n\tfriend class PrefixesModel;\n\n\tStorage::Prefix prefix_;\n};\n\n\nclass PrefixesModel : public GenericTreeModel<PrefixItem> {\n\tQ_OBJECT\npublic:\n\tPrefixesModel(QObject* parent = nullptr);\n\n\tint columnCount(const QModelIndex& parent = QModelIndex()) const;\n\n\tQVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;\n\n\tQVariant headerData(int section, Qt::Orientation orientation,\n\t\t\tint role = Qt::DisplayRole) const;\n\n\tPrefixItem* createPrefix(const QString& name, const QString& path);\n\tbool removePrefix(PrefixItem* item);\n};\n\n\n#endif // MODELS_PREFIXMODEL_H\n"
  },
  {
    "path": "src/manager/resources/resources.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>airwave-manager.png</file>\n        <file>create_link.png</file>\n        <file>edit.png</file>\n        <file>update.png</file>\n        <file>remove.png</file>\n        <file>open_in_browser.png</file>\n        <file>outline.png</file>\n        <file>download.png</file>\n        <file>draw_line.png</file>\n        <file>erase.png</file>\n        <file>settings.png</file>\n        <file>32bit.png</file>\n        <file>64bit.png</file>\n        <file>unknown.png</file>\n        <file>go_up.png</file>\n        <file>create_folder.png</file>\n        <file>show.png</file>\n        <file>open.png</file>\n        <file>save.png</file>\n        <file>mute.png</file>\n        <file>bug.png</file>\n        <file>scull.png</file>\n        <file>star.png</file>\n        <file>trace.png</file>\n        <file>warning.png</file>\n        <file>about.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/manager/widgets/directoryview.cpp",
    "content": "#include \"directoryview.h\"\n\n#include <QHeaderView>\n#include \"nofocusdelegate.h\"\n\n\nDirectoryView::DirectoryView(QWidget* parent) :\n\tGenericTreeView<DirectoryModel>(parent)\n{\n\tsetAutoClearSelection(true);\n\tsetRootIsDecorated(false);\n\n\tNoFocusDelegate* delegate = new NoFocusDelegate(this);\n//\tdelegate->setExtraHeight(4);\n\tsetItemDelegate(delegate);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n}\n\n\nvoid DirectoryView::setModel(DirectoryModel* model)\n{\n\tGenericTreeView<DirectoryModel>::setModel(model);\n\n\tif(model) {\n\t\tQHeaderView* header = this->header();\n\t\theader->setStretchLastSection(false);\n\t\theader->setSectionResizeMode(0, QHeaderView::Stretch);\n\t\theader->setSectionResizeMode(1, QHeaderView::Interactive);\n\t\theader->setSectionResizeMode(2, QHeaderView::Interactive);\n\t}\n}\n\n\nvoid DirectoryView::currentChangeEvent(DirectoryItem* current, DirectoryItem* previous)\n{\n\temit currentItemChanged(current, previous);\n}\n\n\nvoid DirectoryView::selectionChangeEvent(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\temit itemSelectionChanged(selected, deselected);\n}\n\n\nvoid DirectoryView::mouseDoubleClickEvent(QMouseEvent* event)\n{\n\tQModelIndex index = indexAt(event->pos());\n\tDirectoryItem* item = model()->indexToItem(index);\n\tif(item)\n\t\temit itemDoubleClicked(item);\n}\n"
  },
  {
    "path": "src/manager/widgets/directoryview.h",
    "content": "#ifndef WIDGETS_DIRECTORYVIEW_H\n#define WIDGETS_DIRECTORYVIEW_H\n\n#include \"models/directorymodel.h\"\n#include \"widgets/generictreeview.h\"\n\n\nclass DirectoryView : public GenericTreeView<DirectoryModel> {\n\tQ_OBJECT\npublic:\n\tDirectoryView(QWidget* parent = nullptr);\n\npublic slots:\n\tvoid setModel(DirectoryModel* model);\n\nsignals:\n\tvoid currentItemChanged(DirectoryItem* current, DirectoryItem* previous);\n\n\tvoid itemSelectionChanged(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\n\tvoid itemDoubleClicked(DirectoryItem* current);\n\nprotected:\n\tvoid currentChangeEvent(DirectoryItem* current, DirectoryItem* previous);\n\n\tvoid selectionChangeEvent(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\n\tvoid mouseDoubleClickEvent(QMouseEvent* event);\n};\n\n\n#endif // WIDGETS_DIRECTORYVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/generictreeview.h",
    "content": "#ifndef WIDGETS_GENERICTREEVIEW_H\n#define WIDGETS_GENERICTREEVIEW_H\n\n#include <QMouseEvent>\n#include <QTreeView>\n\n\ntemplate<typename Model>\nclass GenericTreeView : public QTreeView {\npublic:\n\ttypedef typename Model::ItemType ItemType;\n\ttypedef QList<ItemType> ItemList;\n\n\tGenericTreeView(QWidget* parent = nullptr);\n\n\tModel* model() const;\n\tvoid setModel(Model* model);\n\n\tItemType* rootItem() const;\n\n\tvoid setAutoClearSelection(bool enabled);\n\tbool isAutoClearSelection() const;\n\n\tvoid setAutoExpanding(bool enabled);\n\tbool isAutoExpanding() const;\n\n\tbool hasSelection() const;\n\tvoid clearSelection();\n\n\tbool hasCurrentItem() const;\n\tItemType* currentItem() const;\n\tvoid clearCurrentItem();\n\n\tItemList selectedItems() const;\n\tbool isSelected(ItemType* item) const;\n\tvoid toggleSelection(ItemType* item);\n\tvoid setSelected(ItemType* item, bool selected);\n\n\tItemType* itemAt(const QPoint& point) const;\n\nprotected:\n\tvirtual void currentChangeEvent(ItemType* current, ItemType* previous);\n\n\tvirtual void selectionChangeEvent(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\nprivate:\n\tbool isAutoClearSelection_;\n\tbool isAutoExpanding_;\n\n\tvoid mousePressEvent(QMouseEvent* event);\n\tvoid rowsInserted(const QModelIndex& parent, int start, int end);\n\n\tvoid currentChanged(const QModelIndex& current,\tconst QModelIndex& previous);\n\n\tvoid selectionChanged(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n};\n\n\ntemplate<typename Model>\nGenericTreeView<Model>::GenericTreeView(QWidget* parent) :\n\tQTreeView(parent),\n\tisAutoClearSelection_(false),\n\tisAutoExpanding_(false)\n{\n\tsetSelectionMode(SingleSelection);\n\tsetSelectionBehavior(SelectRows);\n\tsetDragEnabled(true);\n\tsetDragDropMode(QAbstractItemView::InternalMove);\n}\n\n\ntemplate<typename Model>\nModel* GenericTreeView<Model>::model() const\n{\n\treturn static_cast<Model*>(QTreeView::model());\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::setModel(Model* model)\n{\n\tQTreeView::setModel(model);\n\tcurrentChangeEvent(nullptr, nullptr);\n}\n\n\ntemplate<typename Model>\ntypename GenericTreeView<Model>::ItemType* GenericTreeView<Model>::rootItem() const\n{\n\tif(model())\n\t\treturn model()->root();\n\n\treturn nullptr;\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::setAutoClearSelection(bool enabled)\n{\n\tisAutoClearSelection_ = enabled;\n}\n\n\ntemplate<typename Model>\nbool GenericTreeView<Model>::isAutoClearSelection() const\n{\n\treturn isAutoClearSelection_;\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::setAutoExpanding(bool enabled)\n{\n\tisAutoExpanding_ = enabled;\n}\n\n\ntemplate<typename Model>\nbool GenericTreeView<Model>::isAutoExpanding() const\n{\n\treturn isAutoExpanding_;\n}\n\n\ntemplate<typename Model>\nbool GenericTreeView<Model>::hasSelection() const\n{\n\tif(selectionModel())\n\t\treturn selectionModel()->hasSelection();\n\n\treturn false;\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::clearSelection()\n{\n\tif(selectionModel()) {\n\t\tselectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::NoUpdate);\n\t\tselectionModel()->clear();\n\t}\n}\n\n\ntemplate<typename Model>\nbool GenericTreeView<Model>::hasCurrentItem() const\n{\n\treturn currentIndex().isValid();\n}\n\n\ntemplate<typename Model>\ntypename GenericTreeView<Model>::ItemType* GenericTreeView<Model>::currentItem() const\n{\n\tif(!model())\n\t\treturn nullptr;\n\n\tItemType* item = model()->indexToItem(currentIndex());\n\tif(item == rootItem())\n\t\treturn nullptr;\n\n\treturn item;\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::clearCurrentItem()\n{\n\tif(selectionModel())\n\t\tselectionModel()->clearCurrentIndex();\n}\n\n\n\ntemplate<typename Model>\ntypename GenericTreeView<Model>::ItemList GenericTreeView<Model>::selectedItems() const\n{\n\tif(!selectionModel())\n\t\treturn ItemList();\n\n\tItemList result;\n\tforeach(const QModelIndex& index, selectionModel()->selectedRows())\n\t\tresult += model()->indexToItem(index);\n\n\treturn result;\n}\n\n\ntemplate<typename Model>\nbool GenericTreeView<Model>::isSelected(ItemType* item) const\n{\n\tif(!selectionModel() || !item)\n\t\treturn false;\n\n\tQModelIndex index = model()->itemToIndex(item->parent());\n\tint row = item->row();\n\treturn selectionModel()->isRowSelected(row, index);\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::toggleSelection(ItemType* item)\n{\n\tif(!selectionModel() || !item)\n\t\treturn;\n\n\tQModelIndex index = model()->itemToIndex(item);\n\tif(index.isValid()) {\n\t\tQItemSelectionModel::SelectionFlags flags;\n\t\tflags = QItemSelectionModel::Toggle | QItemSelectionModel::Rows;\n\t\tselectionModel()->select(index, flags);\n\t}\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::setSelected(ItemType* item, bool selected)\n{\n\tif(!selectionModel() || !item)\n\t\treturn;\n\n\tQModelIndex index = model()->itemToIndex(item);\n\tif(index.isValid()) {\n\t\tQItemSelectionModel::SelectionFlags flags;\n\t\tflags = QItemSelectionModel::Rows;\n\n\t\tif(selected) {\n\t\t\tflags |= QItemSelectionModel::Select;\n\t\t}\n\t\telse {\n\t\t\tflags |= QItemSelectionModel::Deselect;\n\t\t}\n\n\t\tselectionModel()->select(index, flags);\n\t}\n}\n\n\ntemplate<typename Model>\ntypename GenericTreeView<Model>::ItemType* GenericTreeView<Model>::itemAt(\n\t\tconst QPoint& point) const\n{\n\tif(!model())\n\t\treturn nullptr;\n\n\tItemType* item = model()->indexToItem(indexAt(point));\n\tif(item == rootItem())\n\t\treturn nullptr;\n\n\treturn item;\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::mousePressEvent(QMouseEvent* event)\n{\n\tif(isAutoClearSelection_) {\n\t\tQModelIndex index = indexAt(event->pos());\n\t\tif(!index.isValid())\n\t\t\tclearSelection();\n\t}\n\n\tQTreeView::mousePressEvent(event);\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::rowsInserted(const QModelIndex& parent, int start, int end)\n{\n\tQTreeView::rowsInserted(parent, start, end);\n\n\t// При добавлении первого элемента, он делается текущим в QAbstractItemView. Чтобы\n\t// синхронизовать текущий элемент с выделением (которого после создания первого\n\t// элемента нет), мы делаем текущий элемент невалидным.\n\tif(rootItem()->childCount() == 1)\n\t\tsetCurrentIndex(QModelIndex());\n\n\tif(isAutoExpanding_) {\n\t\tfor(int i = start; i <= end; ++i) {\n\t\t\tQModelIndex index = model()->index(i, 0, parent);\n\t\t\tsetExpanded(index, true);\n\t\t}\n\t}\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::currentChangeEvent(ItemType* current, ItemType* previous)\n{\n\tQ_UNUSED(current);\n\tQ_UNUSED(previous);\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::selectionChangeEvent(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\tQ_UNUSED(selected);\n\tQ_UNUSED(deselected);\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::currentChanged(const QModelIndex& current,\n\t\tconst QModelIndex& previous)\n{\n\tQTreeView::currentChanged(current, previous);\n\n\tItemType* currentItem = model()->indexToItem(current);\n\tif(currentItem == rootItem())\n\t\tcurrentItem = nullptr;\n\n\tItemType* previousItem = model()->indexToItem(previous);\n\tif(previousItem == rootItem())\n\t\tpreviousItem = nullptr;\n\n\tcurrentChangeEvent(currentItem, previousItem);\n}\n\n\ntemplate<typename Model>\nvoid GenericTreeView<Model>::selectionChanged(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\tQTreeView::selectionChanged(selected, deselected);\n\tselectionChangeEvent(selected, deselected);\n}\n\n\n#endif // WIDGETS_GENERICTREEVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/lineedit.cpp",
    "content": "#include <QApplication>\n#include <QMouseEvent>\n#include <QPainter>\n#include <QStyle>\n#include <QStyleOptionFrameV2>\n#include <QToolButton>\n#include <QtCore/qcoreapplication.h>\n#include <QtWidgets/qcolordialog.h>\n#include \"lineedit.h\"\n\n\nLineEdit::LineEdit(QWidget* parent) :\n\tQLineEdit(parent),\n\thasButton_(false),\n\tbutton_(new QToolButton(this)),\n\tbuttonStyle_(kNormal),\n\tisAutoClearMode_(false),\n\tprefixColor_(qApp->palette().text().color()),\n\tsuffixColor_(qApp->palette().text().color())\n{\n\tbutton_->hide();\n\tbutton_->setCursor(Qt::ArrowCursor);\n\tbutton_->setToolButtonStyle(Qt::ToolButtonIconOnly);\n\tbutton_->setFocusPolicy(Qt::NoFocus);\n\n\tconnect(button_,\n\t\t\tSIGNAL(clicked()),\n\t\t\tSLOT(onButtonClicked()));\n\n\tconnect(this,\n\t\t\tSIGNAL(textChanged(QString)),\n\t\t\tSLOT(onTextChanged()));\n\n\ttimer_.setInterval(200);\n\ttimer_.setSingleShot(true);\n\n\tconnect(&timer_,\n\t\t\tSIGNAL(timeout()),\n\t\t\tSLOT(onTimeout()));\n}\n\n\nbool LineEdit::hasButton() const\n{\n\treturn hasButton_;\n}\n\n\nvoid LineEdit::setButtonEnabled(bool enabled)\n{\n\tif(enabled != hasButton_) {\n\t\thasButton_ = enabled;\n\t\tbutton_->setVisible(enabled);\n\t\tupdateLayout();\n\t\tupdate();\n\t}\n}\n\n\nLineEdit::ButtonStyle LineEdit::buttonStyle() const\n{\n\treturn buttonStyle_;\n}\n\n\nvoid LineEdit::setButtonStyle(ButtonStyle style)\n{\n\tQColor col = palette().toolTipBase().color();\n\tQString sheet = \"QToolButton { background-color: rgba(255, 255, 255, %1);} \"\n\t\t\t\t\t\"QToolButton QWidget { background-color: rgb(%2, %3, %4);}\";\n\tswitch(style) {\n\tcase kNormal:\n\t\tbutton_->setAutoRaise(false);\n\t\tbutton_->setStyleSheet(\"\");\n\t\tbreak;\n\n\tcase kAutoRaise:\n\t\tbutton_->setAutoRaise(true);\n\t\tbutton_->setStyleSheet(\"\");\n\t\tbreak;\n\n\tcase kLightAutoRaise:\n\t\tbutton_->setAutoRaise(true);\n\t\tsheet = sheet.arg(80).arg(col.red()).arg(col.green()).arg(col.blue());\n\t\tbutton_->setStyleSheet(sheet);\n\t\tbreak;\n\n\tcase kTransparent:\n\t\tbutton_->setAutoRaise(true);\n\t\tsheet = sheet.arg(0).arg(col.red()).arg(col.green()).arg(col.blue());\n\t\tbutton_->setStyleSheet(sheet);\n\t\tbreak;\n\t}\n}\n\n\nbool LineEdit::isAutoClearMode() const\n{\n\treturn isAutoClearMode_;\n}\n\n\nvoid LineEdit::setAutoClearMode(bool enabled)\n{\n\tisAutoClearMode_ = enabled;\n\tbutton_->setToolTip(tr(\"Clear\"));\n\tQIcon icon = style()->standardIcon(QStyle::SP_DockWidgetCloseButton);\n\tbutton_->setIcon(icon);\n}\n\n\nQIcon LineEdit::buttonIcon() const\n{\n\treturn button_->icon();\n}\n\n\nQString LineEdit::buttonToolTip() const\n{\n\treturn button_->toolTip();\n}\n\n\nvoid LineEdit::setButtonToolTip(const QString& toolTip)\n{\n\tbutton_->setToolTip(toolTip);\n}\n\n\nvoid LineEdit::setButtonIcon(const QIcon& icon)\n{\n\tbutton_->setIcon(icon);\n}\n\n\nuint LineEdit::editTimeout() const\n{\n\treturn timer_.interval();\n}\n\n\nvoid LineEdit::setEditTimeout(uint msecs)\n{\n\ttimer_.setInterval(msecs);\n}\n\n\nbool LineEdit::hasIcon() const\n{\n\treturn !icon_.isNull();\n}\n\n\nvoid LineEdit::setIcon(const QIcon& icon)\n{\n\ticon_ = icon.pixmap(16, 16);\n\tupdateLayout();\n\tupdate();\n}\n\n\nvoid LineEdit::updateLayout()\n{\n\tint size = height();\n\tint leftMargin = 0;\n\tint rightMargin = 0;\n\n\tif(hasButton()) {\n\t\trightMargin = size;\n\t\tbutton_->setGeometry(width() - size + 1, 1, size - 2, size - 2);\n\t}\n\n\tif(hasIcon())\n\t\tleftMargin = 16;\n\n\tif(!prefix_.isEmpty()) {\n\t\tQFontMetrics fm(font());\n\t\tleftMargin += fm.width(prefix_);\n\t}\n\n\tif(!suffix_.isEmpty()) {\n\t\tQFontMetrics fm(font());\n\t\trightMargin += fm.width(suffix_);\n\t}\n\n\tsetTextMargins(leftMargin, 0, rightMargin, 0);\n}\n\n\nvoid LineEdit::paintEvent(QPaintEvent* event)\n{\n    QLineEdit::paintEvent(event);\n\n\tif(hasIcon()) {\n\t\tQPainter painter(this);\n\t\tpainter.drawPixmap(3, 2, icon_);\n\t}\n\n\tQPainter painter(this);\n\tQFontMetrics fm(font());\n\tQMargins margins = textMargins();\n\n\tint half = (height() - fm.height()) / 2;\n\n\tif(!prefix_.isEmpty()) {\n\t\tint x = margins.left() - fm.width(prefix_) + 6;\n\t\tint y = margins.top() + fm.ascent() + half;\n\n\t\tpainter.setPen(prefixColor_);\n\t\tpainter.drawText(x, y, prefix_);\n\t}\n\n\tif(!suffix_.isEmpty()) {\n\t\tint x = width() - margins.right();\n\t\tint y = margins.top() + fm.ascent() + half;\n\n\t\tpainter.setPen(suffixColor_);\n\t\tpainter.drawText(x, y, suffix_);\n\t}\n}\n\n\nvoid LineEdit::resizeEvent(QResizeEvent* event)\n{\n\tQLineEdit::resizeEvent(event);\n\tupdateLayout();\n}\n\n\nvoid LineEdit::keyPressEvent(QKeyEvent* event)\n{\n\tif(event->key() == Qt::Key_Escape && isAutoClearMode()) {\n\t\tclear();\n\t}\n\telse {\n\t\tQLineEdit::keyPressEvent(event);\n\t}\n}\n\n\nvoid LineEdit::mouseMoveEvent(QMouseEvent* event)\n{\n\tQLineEdit::mouseMoveEvent(event);\n\n\tif(hasIcon()) {\n\t\tif(event->x() < 16 + 2) {\n\t\t\tsetCursor(Qt::PointingHandCursor);\n\t\t}\n\t\telse {\n\t\t\tsetCursor(Qt::IBeamCursor);\n\t\t}\n\t}\n}\n\n\nvoid LineEdit::onButtonClicked()\n{\n\tif(isAutoClearMode_)\n\t\tclear();\n\n\temit buttonClicked();\n}\n\n\nvoid LineEdit::onTextChanged()\n{\n\ttimer_.start();\n}\n\n\nvoid LineEdit::onTimeout()\n{\n\temit textEditTimeout(text());\n}\n\n\nQString LineEdit::prefix() const\n{\n\treturn prefix_;\n}\n\n\nvoid LineEdit::setPrefix(const QString& prefix)\n{\n\tprefix_ = prefix;\n\tupdateLayout();\n\tupdate();\n}\n\n\nQColor LineEdit::prefixColor() const\n{\n\treturn prefixColor_;\n}\n\n\nvoid LineEdit::setPrefixColor(const QColor& color)\n{\n\tprefixColor_ = color;\n\tupdate();\n}\n\n\nQString LineEdit::suffix() const\n{\n\treturn prefix_;\n}\n\n\nvoid LineEdit::setSuffix(const QString& suffix)\n{\n\tsuffix_ = suffix;\n\tupdateLayout();\n\tupdate();\n}\n\n\nQColor LineEdit::suffixColor() const\n{\n\treturn suffixColor_;\n}\n\n\nvoid LineEdit::setSuffixColor(const QColor& color)\n{\n\tsuffixColor_ = color;\n\tupdate();\n}\n"
  },
  {
    "path": "src/manager/widgets/lineedit.h",
    "content": "#ifndef WIDGETS_LINEEDIT_H\n#define WIDGETS_LINEEDIT_H\n\n#include <QLineEdit>\n#include <QTimer>\n\n\nclass QToolButton;\n\n\nclass LineEdit: public QLineEdit {\n\tQ_OBJECT\npublic:\n\tenum ButtonStyle {\n\t\tkNormal,\n\t\tkAutoRaise,\n\t\tkLightAutoRaise,\n\t\tkTransparent\n\t};\n\n\tLineEdit(QWidget* parent = nullptr);\n\n\tbool hasButton() const;\n\tvoid setButtonEnabled(bool enabled);\n\n\tButtonStyle buttonStyle() const;\n\tvoid setButtonStyle(ButtonStyle style);\n\n\tbool isAutoClearMode() const;\n\tvoid setAutoClearMode(bool enabled);\n\n\tQString buttonToolTip() const;\n\tvoid setButtonToolTip(const QString& toolTip);\n\n\tQIcon buttonIcon() const;\n\tvoid setButtonIcon(const QIcon& icon);\n\n\tuint editTimeout() const;\n\tvoid setEditTimeout(uint msecs);\n\n\tbool hasIcon() const;\n\tvoid setIcon(const QIcon& icon);\n\n\tQString prefix() const;\n\tvoid setPrefix(const QString& prefix);\n\n\tQColor prefixColor() const;\n\tvoid setPrefixColor(const QColor& color);\n\n\tQString suffix() const;\n\tvoid setSuffix(const QString& suffix);\n\n\tQColor suffixColor() const;\n\tvoid setSuffixColor(const QColor& color);\n\nsignals:\n\tvoid buttonClicked();\n\tvoid textEditTimeout(const QString& text);\n\nprotected:\n\tvirtual void paintEvent(QPaintEvent* event);\n\tvirtual void resizeEvent(QResizeEvent* event);\n\tvirtual void keyPressEvent(QKeyEvent* event);\n\tvirtual void mouseMoveEvent(QMouseEvent* event);\n\nprivate:\n\tbool hasButton_;\n\tQToolButton* button_;\n\tButtonStyle buttonStyle_;\n\tbool isAutoClearMode_;\n\tQTimer timer_;\n\tQPixmap icon_;\n\tQString prefix_;\n\tQString suffix_;\n\tQColor prefixColor_;\n\tQColor suffixColor_;\n\n\tvoid updateLayout();\n\nprivate slots:\n\tvoid onButtonClicked();\n\tvoid onTextChanged();\n\tvoid onTimeout();\n};\n\n\n#endif // WIDGETS_LINEEDIT_H\n"
  },
  {
    "path": "src/manager/widgets/linksview.cpp",
    "content": "#include \"linksview.h\"\n\n#include <QHeaderView>\n#include \"widgets/nofocusdelegate.h\"\n\n\nLinksView::LinksView(QWidget* parent) :\n\tGenericTreeView<LinksModel>(parent)\n{\n\tsetAutoClearSelection(true);\n\tsetRootIsDecorated(false);\n\n\tNoFocusDelegate* delegate = new NoFocusDelegate(this);\n//\tdelegate->setExtraHeight(4);\n\tsetItemDelegate(delegate);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n\n\tconnect(this,\n\t\t\tSIGNAL(doubleClicked(QModelIndex)),\n\t\t\tSLOT(onItemDoubleClicked(QModelIndex)));\n}\n\n\nvoid LinksView::setModel(LinksModel* model)\n{\n\tGenericTreeView<LinksModel>::setModel(model);\n\n//\tif(model) {\n//\t\tQHeaderView* header = this->header();\n//\t\theader->setStretchLastSection(false);\n//\t\theader->setSectionResizeMode(0, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(1, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(2, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(3, QHeaderView::Stretch);\n//\t}\n}\n\n\nvoid LinksView::currentChangeEvent(LinkItem* current, LinkItem* previous)\n{\n\temit currentItemChanged(current, previous);\n}\n\n\nvoid LinksView::selectionChangeEvent(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\temit itemSelectionChanged(selected, deselected);\n}\n\n\nvoid LinksView::onItemDoubleClicked(const QModelIndex& index)\n{\n\temit itemDoubleClicked(model()->indexToItem(index));\n}\n"
  },
  {
    "path": "src/manager/widgets/linksview.h",
    "content": "#ifndef WIDGETS_LINKSVIEW_H\n#define WIDGETS_LINKSVIEW_H\n\n#include \"models/linksmodel.h\"\n#include \"widgets/generictreeview.h\"\n\n\nclass LinksView : public GenericTreeView<LinksModel> {\n\tQ_OBJECT\npublic:\n\tLinksView(QWidget* parent = nullptr);\n\npublic slots:\n\tvoid setModel(LinksModel* model);\n\nsignals:\n\tvoid currentItemChanged(LinkItem* current, LinkItem* previous);\n\n\tvoid itemSelectionChanged(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\n\tvoid itemDoubleClicked(LinkItem* item);\n\nprotected:\n\tvoid currentChangeEvent(LinkItem* current, LinkItem* previous);\n\n\tvoid selectionChangeEvent(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\nprivate slots:\n\tvoid onItemDoubleClicked(const QModelIndex& index);\n};\n\n\n#endif // WIDGETS_LINKSVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/loadersview.cpp",
    "content": "#include \"loadersview.h\"\n\n#include <QHeaderView>\n#include \"nofocusdelegate.h\"\n\n\nLoadersView::LoadersView(QWidget* parent) :\n\tGenericTreeView<LoadersModel>(parent)\n{\n\tsetAutoClearSelection(true);\n\tsetRootIsDecorated(false);\n\n\tNoFocusDelegate* delegate = new NoFocusDelegate(this);\n//\tdelegate->setExtraHeight(4);\n\tsetItemDelegate(delegate);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n}\n\n\nvoid LoadersView::setModel(LoadersModel* model)\n{\n\tGenericTreeView<LoadersModel>::setModel(model);\n\n//\tif(model) {\n//\t\tQHeaderView* header = this->header();\n//\t\theader->setStretchLastSection(false);\n//\t\theader->setSectionResizeMode(0, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(1, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(2, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(3, QHeaderView::Stretch);\n//\t}\n}\n\n\nvoid LoadersView::currentChangeEvent(LoaderItem* current, LoaderItem* previous)\n{\n\temit currentItemChanged(current, previous);\n}\n\n\nvoid LoadersView::selectionChangeEvent(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\temit itemSelectionChanged(selected, deselected);\n}\n"
  },
  {
    "path": "src/manager/widgets/loadersview.h",
    "content": "#ifndef WIDGETS_LOADERSVIEW_H\n#define WIDGETS_LOADERSVIEW_H\n\n#include \"generictreeview.h\"\n#include \"models/loadersmodel.h\"\n\n\nclass LoadersView : public GenericTreeView<LoadersModel> {\n\tQ_OBJECT\npublic:\n\tLoadersView(QWidget* parent = nullptr);\n\npublic slots:\n\tvoid setModel(LoadersModel* model);\n\nsignals:\n\tvoid currentItemChanged(LoaderItem* current, LoaderItem* previous);\n\n\tvoid itemSelectionChanged(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\nprotected:\n\tvoid currentChangeEvent(LoaderItem* current, LoaderItem* previous);\n\n\tvoid selectionChangeEvent(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n};\n\n\n#endif // WIDGETS_LOADERSVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/logview.cpp",
    "content": "#include <QScrollBar>\n#include <QStringBuilder>\n#include <QTime>\n#include \"logview.h\"\n#include \"common/config.h\"\n\n\nLogView::LogView(QWidget* parent) :\n\tQTextEdit(parent),\n\tisAutoScroll_(true),\n\tisWordWrap_(true)\n{\n\tsetReadOnly(true);\n\n\tQFont font(\"Monospace\");\n\tfont.setStyleHint(QFont::TypeWriter);\n\tsetFont(font);\n\n\tsetWordWrap(isWordWrap_);\n}\n\n\nbool LogView::isAutoScroll() const\n{\n\treturn isAutoScroll_;\n}\n\n\nbool LogView::isWordWrap() const\n{\n\treturn isWordWrap_;\n}\n\n\nvoid LogView::setAutoScroll(bool enabled)\n{\n\tisAutoScroll_ = enabled;\n}\n\n\nvoid LogView::setWordWrap(bool enabled)\n{\n\tisWordWrap_ = enabled;\n\n\tif(isWordWrap_) {\n\t\tsetWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);\n\t}\n\telse {\n\t\tsetWordWrapMode(QTextOption::NoWrap);\n\t}\n}\n\n\nvoid LogView::addMessage(quint64 time, const QString& sender, const QString& text)\n{\n\tsetTextColor(QColor(0x909090));\n\tinsertPlainText(QString::number(time >> 32));\n\tinsertPlainText(\".\");\n\tinsertPlainText(QString::number(time & 0xFFFFFFFF).rightJustified(9, '0'));\n\tinsertPlainText(\" \");\n\n\tif(sender == HOST_BASENAME || sender.endsWith(\".dll\")) {\n\t\tsetTextColor(QColor(0x804000));\n\t}\n\telse {\n\t\tsetTextColor(QColor(0x004080));\n\t}\n\n\tinsertPlainText(sender.rightJustified(20, ' ', true));\n\n\tinsertPlainText(\" : \");\n\n\tsetTextColor(0x222222);\n\tinsertPlainText(text);\n\tinsertPlainText(\"\\n\");\n\n\tif(isAutoScroll_) {\n\t\tint maximum = verticalScrollBar()->maximum();\n\t\tverticalScrollBar()->setValue(maximum);\n\t}\n}\n\n\nvoid LogView::addSeparator()\n{\n\tinsertHtml(\"<hr><br>\");\n\tif(isAutoScroll_) {\n\t\tint maximum = verticalScrollBar()->maximum();\n\t\tverticalScrollBar()->setValue(maximum);\n\t}\n}\n"
  },
  {
    "path": "src/manager/widgets/logview.h",
    "content": "#ifndef WIDGETS_LOGVIEW_H\n#define WIDGETS_LOGVIEW_H\n\n#include <QTextEdit>\n\n\nclass LogView : public QTextEdit {\n\tQ_OBJECT\npublic:\n\tLogView(QWidget* parent = nullptr);\n\n\tbool isAutoScroll() const;\n\tbool isWordWrap() const;\n\npublic slots:\n\tvoid setAutoScroll(bool enabled);\n\tvoid setWordWrap(bool enabled);\n\tvoid addMessage(quint64 time, const QString& sender, const QString& text);\n\tvoid addSeparator();\n\nprivate:\n\tbool isAutoScroll_;\n\tbool isWordWrap_;\n};\n\n\n#endif // WIDGETS_LOGVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/nofocusdelegate.cpp",
    "content": "#include \"nofocusdelegate.h\"\r\n\r\n\r\nNoFocusDelegate::NoFocusDelegate(QWidget* parent) :\r\n\tQStyledItemDelegate(parent),\r\n\textraHeight_(0)\r\n{\r\n}\r\n\r\n\r\nvoid NoFocusDelegate::paint(QPainter* painter,\r\n\t\t\t\t\t\t\tconst QStyleOptionViewItem& option,\r\n\t\t\t\t\t\t\tconst QModelIndex& index) const\r\n{\r\n\tQStyleOptionViewItemV4 opt = option;\r\n\topt.state &= ~QStyle::State_HasFocus;\r\n\r\n\tQStyledItemDelegate::paint(painter, opt, index);\r\n}\r\n\r\n\r\nQSize NoFocusDelegate::sizeHint(const QStyleOptionViewItem& option,\r\n\t\t\t\t\t\t\t\tconst QModelIndex& index) const\r\n{\r\n\tQSize size = QStyledItemDelegate::sizeHint(option, index);\r\n\tsize.setHeight(size.height() + extraHeight_);\r\n\r\n\treturn size;\r\n}\r\n\r\n\r\nvoid NoFocusDelegate::setExtraHeight(int value)\r\n{\r\n\textraHeight_ = qMax(0, value);\r\n}\r\n"
  },
  {
    "path": "src/manager/widgets/nofocusdelegate.h",
    "content": "#ifndef WIDGETS_NOFOCUSDELEGATE_H\r\n#define WIDGETS_NOFOCUSDELEGATE_H\r\n\r\n#include <QStyledItemDelegate>\r\n\r\n\r\nclass NoFocusDelegate : public QStyledItemDelegate {\r\n\tQ_OBJECT\r\npublic:\r\n\texplicit NoFocusDelegate(QWidget* parent = nullptr);\r\n\r\n\tvoid paint(QPainter* painter, const QStyleOptionViewItem& option,\r\n\t\t\t   const QModelIndex& index) const;\r\n\r\n\tQSize sizeHint(const QStyleOptionViewItem& option,\r\n\t\t\t\t   const QModelIndex& index) const;\r\n\r\n\tvoid setExtraHeight(int value);\r\n\r\nprivate:\r\n\tint extraHeight_;\r\n};\r\n\r\n\r\n#endif // WIDGETS_NOFOCUSDELEGATE_H\r\n"
  },
  {
    "path": "src/manager/widgets/prefixesview.cpp",
    "content": "#include \"prefixesview.h\"\n\n#include <QHeaderView>\n#include \"nofocusdelegate.h\"\n\n\nPrefixesView::PrefixesView(QWidget* parent) :\n\tGenericTreeView<PrefixesModel>(parent)\n{\n\tsetAutoClearSelection(true);\n\tsetRootIsDecorated(false);\n\n\tNoFocusDelegate* delegate = new NoFocusDelegate(this);\n//\tdelegate->setExtraHeight(4);\n\tsetItemDelegate(delegate);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n}\n\n\nvoid PrefixesView::setModel(PrefixesModel* model)\n{\n\tGenericTreeView<PrefixesModel>::setModel(model);\n\n//\tif(model) {\n//\t\tQHeaderView* header = this->header();\n//\t\theader->setStretchLastSection(false);\n//\t\theader->setSectionResizeMode(0, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(1, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(2, QHeaderView::ResizeToContents);\n//\t\theader->setSectionResizeMode(3, QHeaderView::Stretch);\n//\t}\n}\n\n\nvoid PrefixesView::currentChangeEvent(PrefixItem* current, PrefixItem* previous)\n{\n\temit currentItemChanged(current, previous);\n}\n\n\nvoid PrefixesView::selectionChangeEvent(const QItemSelection& selected,\n\t\tconst QItemSelection& deselected)\n{\n\temit itemSelectionChanged(selected, deselected);\n}\n"
  },
  {
    "path": "src/manager/widgets/prefixesview.h",
    "content": "#ifndef WIDGETS_PREFIXESVIEW_H\n#define WIDGETS_PREFIXESVIEW_H\n\n#include \"generictreeview.h\"\n#include \"models/prefixesmodel.h\"\n\n\nclass PrefixesView : public GenericTreeView<PrefixesModel> {\n\tQ_OBJECT\npublic:\n\tPrefixesView(QWidget* parent = nullptr);\n\npublic slots:\n\tvoid setModel(PrefixesModel* model);\n\nsignals:\n\tvoid currentItemChanged(PrefixItem* current, PrefixItem* previous);\n\n\tvoid itemSelectionChanged(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n\nprotected:\n\tvoid currentChangeEvent(PrefixItem* current, PrefixItem* previous);\n\n\tvoid selectionChangeEvent(const QItemSelection& selected,\n\t\t\tconst QItemSelection& deselected);\n};\n\n\n#endif // WIDGETS_PREFIXESVIEW_H\n"
  },
  {
    "path": "src/manager/widgets/separatorlabel.cpp",
    "content": "#include \"separatorlabel.h\"\n\n#include <QFrame>\n#include <QHBoxLayout>\n#include <QLabel>\n\n\nSeparatorLabel::SeparatorLabel(const QString& text, QWidget* parent) :\n\tQWidget(parent)\n{\n\tlabel_ = new QLabel(text);\n\n\tQFont font;\n\tfont.setBold(true);\n\tlabel_->setFont(font);\n\n\tQFrame* line = new QFrame;\n\tline->setFrameShape(QFrame::HLine);\n\tline->setFrameShadow(QFrame::Sunken);\n\tline->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);\n\n\tQHBoxLayout* layout = new QHBoxLayout;\n\tlayout->setMargin(0);\n\tlayout->addWidget(label_);\n\tlayout->addWidget(line);\n\n\tsetLayout(layout);\n}\n"
  },
  {
    "path": "src/manager/widgets/separatorlabel.h",
    "content": "#ifndef WIDGET_SEPARATORLABEL_H_\n#define WIDGET_SEPARATORLABEL_H_\n\n#include <QWidget>\n\n\nclass QLabel;\n\n\nclass SeparatorLabel : public QWidget {\n\tQ_OBJECT\npublic:\n\tSeparatorLabel(const QString& text = QString(), QWidget* parent = nullptr);\n\nprivate:\n\tQLabel* label_;\n};\n\n\n#endif //WIDGET_SEPARATORLABEL_H_\n"
  },
  {
    "path": "src/plugin/CMakeLists.txt",
    "content": "set(TARGET_NAME ${PLUGIN_BASENAME})\n\nproject(${TARGET_NAME})\n\nfind_package(LibDl REQUIRED)\nfind_package(LibMagic REQUIRED)\nfind_package(Threads REQUIRED)\nfind_package(X11 REQUIRED)\n\ninclude_directories(\n\t${CMAKE_CURRENT_BINARY_DIR}\n\t${CMAKE_CURRENT_SOURCE_DIR}\n\t${LIBDL_INCLUDE_DIR}\n\t${X11_INCLUDE_DIR}\n\t${VSTSDK_INCLUDE_DIR}\n)\n\n# Workaround for VST 2.4 SDK on Linux\nadd_definitions(-D__cdecl=)\n\nif(DEBUG_BINARY_DIR)\n\tset(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${DEBUG_BINARY_DIR})\n\tset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${DEBUG_BINARY_DIR})\nendif()\n\n# Common sources\nset(SOURCES\n\tmain.cpp\n\tplugin.cpp\n\t../common/dataport.cpp\n\t../common/event.cpp\n\t../common/filesystem.cpp\n\t../common/json.cpp\n\t../common/logger.cpp\n\t../common/moduleinfo.cpp\n\t../common/storage.cpp\n\t../common/vsteventkeeper.cpp\n)\n\nset(HEADERS\n\t../common/json.h\n\t../common/protocol.h\n\t../common/vst24.h\n)\n\n# Configure library base name\nset(CMAKE_SHARED_LIBRARY_PREFIX \"\")\n\n# Set target\nadd_library(${TARGET_NAME} SHARED ${SOURCES})\n\n# Link with libraries\ntarget_link_libraries(${TARGET_NAME}\n\t${LIBDL_LIBRARIES}\n\t${CMAKE_THREAD_LIBS_INIT}\n\t${X11_X11_LIB}\n\t${LIBMAGIC_LIBRARY}\n)\n\ninstall(TARGETS ${TARGET_NAME} LIBRARY DESTINATION bin)\n"
  },
  {
    "path": "src/plugin/main.cpp",
    "content": "#include <string>\n#include <dlfcn.h>\n#include <signal.h>\n#include \"plugin.h\"\n#include \"common/config.h\"\n#include \"common/filesystem.h\"\n#include \"common/logger.h\"\n#include \"common/moduleinfo.h\"\n#include \"common/storage.h\"\n\n\nusing namespace Airwave;\n\n\nextern \"C\" {\n\nAEffect* VSTPluginMain(AudioMasterProc audioMasterProc);\nAEffect* mainStub(AudioMasterProc audioMasterProc) asm (\"main\");\n\n}\n\n\nvoid signalHandler(int signum)\n{\n\tif(signum == SIGCHLD) {\n\t\tTRACE(\"Child process terminated\");\n\t}\n\telse {\n\t\tTRACE(\"Received signal %d\", signum);\n\t}\n}\n\n\nAEffect* VSTPluginMain(AudioMasterProc audioMasterProc)\n{\n\t// FIXME Without this signal handler the Renoise tracker is unable to start the child\n\t// winelib application.\n\tsignal(SIGCHLD, signalHandler);\n\n\tStorage storage;\n\tloggerInit(storage.logSocketPath(), PLUGIN_BASENAME);\n\n\t// Get path to own binary\n\tDl_info info;\n\tif(dladdr(reinterpret_cast<void*>(VSTPluginMain), &info) == 0) {\n\t\tERROR(\"Unable to get library filename\");\n\t\treturn nullptr;\n\t}\n\n\tstd::string selfPath = FileSystem::realPath(info.dli_fname);\n\tif(selfPath.empty()) {\n\t\tERROR(\"Unable to get an absolute path of the plugin binary\", selfPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\t// Get path of the linked VST plugin binary\n\tStorage::Link link = storage.link(selfPath);\n\tif(!link) {\n\t\tERROR(\"Link '%s' is corrupted\", selfPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\tLogLevel level = link.logLevel();\n\tif(level == LogLevel::kDefault)\n\t\tlevel = storage.defaultLogLevel();\n\n\tloggerSetSenderId(FileSystem::baseName(info.dli_fname));\n\tloggerSetLogLevel(level);\n\n\tTRACE(\"Initializing plugin endpoint %s\", VERSION_STRING);\n\tTRACE(\"Plugin binary: %s\", selfPath.c_str());\n\n\tstd::string winePrefix = link.prefix();\n\tStorage::Prefix prefix = storage.prefix(winePrefix);\n\tif(!prefix) {\n\t\tERROR(\"Invalid WINE prefix '%s'\", winePrefix.c_str());\n\t\treturn nullptr;\n\t}\n\n\tstd::string prefixPath = FileSystem::realPath(prefix.path());\n\tif(!FileSystem::isDirExists(prefixPath)) {\n\t\tERROR(\"WINE prefix directory '%s' doesn't exists\", prefixPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\tTRACE(\"WINE prefix:   %s\", prefixPath.c_str());\n\n\tstd::string wineLoader = link.loader();\n\tStorage::Loader loader = storage.loader(wineLoader);\n\tif(!loader) {\n\t\tERROR(\"Invalid WINE loader '%s'\", wineLoader.c_str());\n\t\treturn nullptr;\n\t}\n\n\tstd::string loaderPath = FileSystem::realPath(loader.path());\n\tif(!FileSystem::isFileExists(loaderPath)) {\n\t\tERROR(\"WINE loader binary '%s' doesn't exists\", loaderPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\tTRACE(\"WINE loader:   %s\", loaderPath.c_str());\n\n\tstd::string vstPath = prefixPath + '/' + link.target();\n\tif(!FileSystem::isFileExists(vstPath)) {\n\t\tERROR(\"VST binary '%s' doesn't exists\", vstPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\tTRACE(\"VST binary:    %s\", vstPath.c_str());\n\n\t// Find host binary path\n\tModuleInfo::Arch arch = ModuleInfo::instance()->getArch(vstPath);\n\n\tstd::string hostName;\n\tif(arch == ModuleInfo::kArch64) {\n\t\thostName = HOST_BASENAME \"-64.exe\";\n\t}\n\telse if(arch == ModuleInfo::kArch32) {\n\t\thostName = HOST_BASENAME \"-32.exe\";\n\t}\n\telse {\n\t\tERROR(\"Unable to determine VST plugin architecture\");\n\t\treturn nullptr;\n\t}\n\n\tstd::string hostPath = FileSystem::realPath(storage.binariesPath() + '/' + hostName);\n\tif(!FileSystem::isFileExists(hostPath)) {\n\t\tERROR(\"Host binary '%s' doesn't exists\", hostPath.c_str());\n\t\treturn nullptr;\n\t}\n\n\tTRACE(\"Host binary:   %s\", hostPath.c_str());\n\n\t// We process only two cases, because messages with log levels lower than 'trace'\n\t// wouldn't be logged anyway.\n\tif(level == LogLevel::kTrace) {\n\t\tTRACE(\"Log level:     trace\");\n\t}\n\telse if(level == LogLevel::kDebug) {\n\t\tTRACE(\"Log level:     debug\");\n\t}\n\n\t// Initialize plugin endpoint\n\tPlugin* plugin;\n\tplugin = new Plugin(vstPath, hostPath, prefixPath, loaderPath,\n\t\t\tstorage.logSocketPath(), audioMasterProc);\n\tif(!plugin->effect()) {\n\t\tERROR(\"Unable to initialize plugin endpoint\");\n\t\treturn nullptr;\n\t}\n\n\tTRACE(\"Plugin endpoint is initialized\");\n\treturn plugin->effect();\n}\n\n\n// Deprecated main() stub which is still used by some hosts\nAEffect* mainStub(AudioMasterProc audioMasterProc)\n{\n\treturn VSTPluginMain(audioMasterProc);\n}\n"
  },
  {
    "path": "src/plugin/plugin.cpp",
    "content": "#include \"plugin.h\"\n\n#include <cstring>\n#include <unistd.h>\n#include <sys/wait.h>\n#include \"common/logger.h\"\n#include \"common/protocol.h\"\n\n\n#define XEMBED_EMBEDDED_NOTIFY\t0\n#define XEMBED_FOCUS_OUT\t\t5\n#define kVstExtMaxParamStrLen\t24\n\nnamespace Airwave {\n\n\nPlugin::Plugin(const std::string& vstPath, const std::string& hostPath,\n\t\tconst std::string& prefixPath, const std::string& loaderPath,\n\t\tconst std::string& logSocketPath, AudioMasterProc masterProc) :\n\tmasterProc_(masterProc),\n\teffect_(nullptr),\n\tdata_(nullptr),\n\tdataLength_(0),\n\tchildPid_(-1),\n\tprocessCallbacks_(ATOMIC_FLAG_INIT),\n\tmainThreadId_(std::this_thread::get_id()),\n\tlastIndex_(-1),\n\tlastValue_(0)\n{\n\t// The constructor will return early when error occurs. In this case the effect()\n\t// fucntion will be returning nullptr, indicating the error.\n\n\tDEBUG(\"Main thread id: %p\", mainThreadId_);\n\n\t// FIXME: frame size should be verified.\n\tif(!controlPort_.create(65536)) {\n\t\tERROR(\"Unable to create control port\");\n\t\treturn;\n\t}\n\n\t// FIXME: frame size should be verified.\n\tif(!callbackPort_.create(1024)) {\n\t\tERROR(\"Unable to create callback port\");\n\t\tcontrolPort_.disconnect();\n\t\treturn;\n\t}\n\n\t// Start the host endpoint's process.\n\tchildPid_ = fork();\n\tif(childPid_ == -1) {\n\t\tERROR(\"fork() call failed\");\n\t\tcontrolPort_.disconnect();\n\t\tcallbackPort_.disconnect();\n\t\treturn;\n\t}\n\telse if(childPid_ == 0) {\n\t\tsetenv(\"WINEPREFIX\", prefixPath.c_str(), 1);\n\t\tsetenv(\"WINELOADER\", loaderPath.c_str(), 1);\n\n\t\tstd::string id = std::to_string(controlPort_.id());\n\t\tstd::string level = std::to_string(static_cast<int>(loggerLogLevel()));\n\n\t\texecl(\"/bin/sh\", \"/bin/sh\", hostPath.c_str(), vstPath.c_str(), id.c_str(),\n\t\t\t\tlevel.c_str(), logSocketPath.c_str(), nullptr);\n\n\t\t// We should never reach this point on success child execution.\n\t\tERROR(\"execl() call failed\");\n\t\treturn;\n\t}\n\n\tDEBUG(\"Child process started, pid=%d\", childPid_);\n\n\tstd::memset(&rect_, 0, sizeof(ERect));\n\n\tprocessCallbacks_.test_and_set();\n\tcallbackThread_ = std::thread(&Plugin::callbackThread, this);\n\n\tcondition_.wait();\n\n\t// Send host info to the host endpoint.\n\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\tframe->command = Command::HostInfo;\n\tframe->opcode = callbackPort_.id();\n\tcontrolPort_.sendRequest();\n\n\tTRACE(\"Waiting response from host endpoint...\");\n\n\t// Wait for the host endpoint initialization.\n\tif(!controlPort_.waitResponse()) {\n\t\tERROR(\"Host endpoint is not responding\");\n\t\tkill(childPid_, SIGKILL);\n\t\tcontrolPort_.disconnect();\n\t\tcallbackPort_.disconnect();\n\t\tchildPid_ = -1;\n\t\treturn;\n\t}\n\n\tPluginInfo* info = reinterpret_cast<PluginInfo*>(frame->data);\n\teffect_ = new AEffect;\n\tstd::memset(effect_, 0, sizeof(AEffect));\n\n\teffect_->magic                  = kEffectMagic;\n\teffect_->object                 = this;\n\teffect_->dispatcher             = dispatchProc;\n\teffect_->getParameter           = getParameterProc;\n\teffect_->setParameter           = setParameterProc;\n\teffect_->__processDeprecated    = nullptr;\n\teffect_->processReplacing       = processReplacingProc;\n\teffect_->processDoubleReplacing = processDoubleReplacingProc;\n\teffect_->flags                  = info->flags;\n\teffect_->numPrograms            = info->programCount;\n\teffect_->numParams              = info->paramCount;\n\teffect_->numInputs              = info->inputCount;\n\teffect_->numOutputs             = info->outputCount;\n\teffect_->initialDelay           = info->initialDelay;\n\teffect_->uniqueID               = info->uniqueId;\n\teffect_->version                = info->version;\n\n\tDEBUG(\"VST plugin summary:\");\n\tDEBUG(\"  flags:         0x%08X\", effect_->flags);\n\tDEBUG(\"  program count: %d\",     effect_->numPrograms);\n\tDEBUG(\"  param count:   %d\",     effect_->numParams);\n\tDEBUG(\"  input count:   %d\",     effect_->numInputs);\n\tDEBUG(\"  output count:  %d\",     effect_->numOutputs);\n\tDEBUG(\"  initial delay: %d\",     effect_->initialDelay);\n\tDEBUG(\"  unique ID:     0x%08X\", effect_->uniqueID);\n\tDEBUG(\"  version:       %d\",     effect_->version);\n}\n\n\nPlugin::~Plugin()\n{\n\tTRACE(\"Waiting for callback thread termination...\");\n\n\tprocessCallbacks_.clear();\n\tif(callbackThread_.joinable())\n\t\tcallbackThread_.join();\n\n\tcontrolPort_.disconnect();\n\tcallbackPort_.disconnect();\n\taudioPort_.disconnect();\n\n\tTRACE(\"Waiting for child process termination...\");\n\n\tint status;\n\twaitpid(childPid_, &status, 0);\n\n\tif(effect_)\n\t\tdelete effect_;\n\n\tTRACE(\"Plugin endpoint terminated\");\n}\n\n\nAEffect* Plugin::effect()\n{\n\treturn effect_;\n}\n\n\nvoid Plugin::callbackThread()\n{\n\tTRACE(\"Callback thread started\");\n\n\tcondition_.post();\n\n\twhile(processCallbacks_.test_and_set()) {\n\t\tif(callbackPort_.waitRequest(100)) {\n\t\t\tDataFrame* frame = callbackPort_.frame<DataFrame>();\n\t\t\tframe->value = handleAudioMaster();\n\t\t\tcallbackPort_.sendResponse();\n\t\t}\n\t}\n\n\tTRACE(\"Callback thread terminated\");\n}\n\n\nintptr_t Plugin::setBlockSize(DataPort* port, intptr_t frames)\n{\n\tsize_t frameSize = sizeof(DataFrame) + sizeof(double) *\n\t\t\t(frames * effect_->numInputs + frames * effect_->numOutputs);\n\n\tif(audioPort_.frameSize() < frameSize) {\n\t\tDEBUG(\"Setting block size to %d frames\", frames);\n\t\taudioPort_.disconnect();\n\n\t\tif(!audioPort_.create(frameSize)) {\n\t\t\tERROR(\"Unable to create audio port\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tDataFrame* frame = controlPort_.frame<DataFrame>();\n\t\tframe->command = Command::Dispatch;\n\t\tframe->opcode = effSetBlockSize;\n\t\tframe->index = audioPort_.id();\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value;\n\t}\n\n\treturn 1;\n}\n\n\nintptr_t Plugin::handleAudioMaster()\n{\n\tDataFrame* frame = callbackPort_.frame<DataFrame>();\n\n\tif(frame->opcode != audioMasterGetTime && frame->opcode != audioMasterIdle) {\n\t\tFLOOD(\"(%p) handleAudioMaster(opcode: %s, index: %d, value: %d, opt: %g)\",\n\t\t\t\tstd::this_thread::get_id(), kAudioMasterEvents[frame->opcode],\n\t\t\t\tframe->index, frame->value, frame->opt);\n\t}\n\n\tswitch(frame->opcode) {\n\tcase audioMasterVersion:\n\tcase __audioMasterWantMidiDeprecated:\n\tcase audioMasterIdle:\n\tcase audioMasterBeginEdit:\n\tcase audioMasterEndEdit:\n\tcase audioMasterUpdateDisplay:\n\tcase audioMasterGetVendorVersion:\n\tcase audioMasterSizeWindow:\n\tcase audioMasterGetInputLatency:\n\tcase audioMasterGetOutputLatency:\n\tcase audioMasterGetCurrentProcessLevel:\n\tcase audioMasterGetAutomationState:\n\tcase audioMasterCurrentId:\n\tcase audioMasterGetSampleRate:\n\t\treturn masterProc_(effect_, frame->opcode, frame->index, frame->value, nullptr,\n\t\t\t\tframe->opt);\n\n\tcase audioMasterAutomate: {\n\t\tlastThreadId_ = std::this_thread::get_id();\n\t\tlastIndex_ = frame->index;\n\t\tlastValue_ = frame->value;\n\n\t\tintptr_t result = masterProc_(effect_, frame->opcode, frame->index, frame->value,\n\t\t\t\tnullptr, frame->opt);\n\n\t\tlastIndex_ = -1;\n\t\treturn result;\n\t}\n\n\tcase audioMasterIOChanged: {\n\t\tPluginInfo* info = reinterpret_cast<PluginInfo*>(frame->data);\n\t\teffect_->flags        = info->flags;\n\t\teffect_->numPrograms  = info->programCount;\n\t\teffect_->numParams    = info->paramCount;\n\t\teffect_->numInputs    = info->inputCount;\n\t\teffect_->numOutputs   = info->outputCount;\n\t\teffect_->initialDelay = info->initialDelay;\n\t\teffect_->uniqueID     = info->uniqueId;\n\t\teffect_->version      = info->version;\n\n\t\treturn masterProc_(effect_, frame->opcode, frame->index, frame->value, nullptr,\n\t\t\t\tframe->opt); }\n\n\tcase audioMasterGetVendorString:\n\tcase audioMasterGetProductString:\n\tcase audioMasterCanDo:\n\t\treturn masterProc_(effect_, frame->opcode, frame->index, frame->value,\n\t\t\t\tframe->data, frame->opt);\n\n\tcase audioMasterGetTime: {\n\t\tintptr_t value = masterProc_(effect_, frame->opcode, frame->index, frame->value,\n\t\t\t\tnullptr, frame->opt);\n\n\t\tVstTimeInfo* timeInfo = reinterpret_cast<VstTimeInfo*>(value);\n\t\tif(timeInfo) {\n\t\t\tstd::memcpy(frame->data, timeInfo, sizeof(VstTimeInfo));\n\t\t\treturn 1;\n\t\t}\n\n\t\treturn 0; }\n\n\tcase audioMasterProcessEvents: {\n\t\tVstEvent* events = reinterpret_cast<VstEvent*>(frame->data);\n\t\tevents_.reload(frame->index, events);\n\t\tVstEvents* e = events_.events();\n\n\t\treturn masterProc_(effect_, frame->opcode, 0, 0, e, 0.0f); }\n\t}\n\n\tERROR(\"Unhandled audio master event: %s %d\", kAudioMasterEvents[frame->opcode],\n\t\t\tframe->opcode);\n\n\treturn 0;\n}\n\n\nintptr_t Plugin::dispatch(DataPort* port, i32 opcode, i32 index, intptr_t value,\n\t\tvoid* ptr, float opt)\n{\n\tif(opcode != effEditIdle && opcode)\n\t\tFLOOD(\"(%p) dispatch: %s\", std::this_thread::get_id(), kDispatchEvents[opcode]);\n\n\tDataFrame* frame = port->frame<DataFrame>();\n\tframe->command = Command::Dispatch;\n\tframe->opcode  = opcode;\n\tframe->index   = index;\n\tframe->value   = value;\n\tframe->opt     = opt;\n\n\tswitch(opcode) {\n\n\t// We will not transmit effEditIdle event because the host endpoint processes window\n\t// events continuously in its main thread.\n\tcase effEditIdle:\n\t\treturn 1;\n\n\tcase effOpen: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\tint result = frame->value;\n\n\t\tsetBlockSize(port, 256);\n\t\treturn result; }\n\n\tcase effGetVstVersion:\n\tcase effGetPlugCategory:\n\tcase effSetSampleRate:\n\tcase effGetVendorVersion:\n\tcase effEditClose:\n\tcase effMainsChanged:\n\tcase effCanBeAutomated:\n\tcase effGetProgram:\n\tcase effStartProcess:\n\tcase effSetProgram:\n\tcase effBeginSetProgram:\n\tcase effEndSetProgram:\n\tcase effStopProcess:\n\tcase effGetNumMidiInputChannels:\n\tcase effGetNumMidiOutputChannels:\n\tcase effSetPanLaw:\n\tcase effGetTailSize:\n\tcase effSetEditKnobMode:\n\tcase __effConnectInputDeprecated:\n\tcase __effConnectOutputDeprecated:\n\tcase __effKeysRequiredDeprecated:\n\tcase __effIdentifyDeprecated:\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value;\n\n\tcase effClose:\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tTRACE(\"Closing plugin\");\n\t\tdelete this;\n\t\tloggerFree();\n\t\treturn 1;\n\n\tcase effSetBlockSize:\n\t\treturn setBlockSize(port, value);\n\n\tcase effEditOpen: {\n\t\tDisplay* display = XOpenDisplay(nullptr);\n\t\tWindow parent = reinterpret_cast<Window>(ptr);\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tunion Cast {\n\t\t\tu8* data;\n\t\t\tERect* rect;\n\t\t};\n\n\t\tCast cast;\n\t\tcast.data = frame->data;\n\t\trect_ = *cast.rect;\n\n\t\tint width = rect_.right - rect_.left;\n\t\tint height = rect_.bottom - rect_.top;\n\n\t\tDEBUG(\"Requested window size: %dx%d\", width, height);\n\n\t\tXResizeWindow(display, parent, width, height);\n\t\tXSync(display, false);\n\n\t\t// FIXME without this delay, the VST window sometimes stays black.\n\t\tusleep(100000);\n\n\t\tWindow child = frame->value;\n\t\tXReparentWindow(display, child, parent, 0, 0);\n\n\t\tsendXembedMessage(display, child, XEMBED_EMBEDDED_NOTIFY, 0, parent, 0);\n\t\tsendXembedMessage(display, child, XEMBED_FOCUS_OUT, 0, 0, 0);\n\n\t\tframe->command = Command::ShowWindow;\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\t// FIXME without this delay, the VST window sometimes stays black.\n\t\tusleep(100000);\n\n\t\tXMapWindow(display, child);\n\t\tXSync(display, false);\n\n\t\tXCloseDisplay(display);\n\n\t\treturn frame->value; }\n\n\tcase effEditGetRect: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tunion Cast {\n\t\t\tu8* data;\n\t\t\tERect* rect;\n\t\t};\n\n\t\tCast cast;\n\t\tcast.data = frame->data;\n\t\trect_ = *cast.rect;\n\n\t\tERect** rectPtr = static_cast<ERect**>(ptr);\n\t\t*rectPtr = &rect_;\n\t\treturn frame->value; }\n\n\tcase effCanDo: {\n\t\tconst char* source = static_cast<const char*>(ptr);\n\t\tchar* dest         = reinterpret_cast<char*>(frame->data);\n\t\tsize_t maxLength   = port->frameSize() - sizeof(DataFrame);\n\n\t\tvst_strncpy(dest, source, maxLength);\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value; }\n\n\tcase effGetProgramName: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tvst_strncpy(dest, source, kVstMaxProgNameLen);\n\t\treturn frame->value; }\n\n\tcase effSetProgramName: {\n\t\tconst char* source = static_cast<const char*>(ptr);\n\t\tchar* dest         = reinterpret_cast<char*>(frame->data);\n\n\t\tvst_strncpy(dest, source, kVstMaxProgNameLen);\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value; }\n\n\tcase effGetVendorString:\n\tcase effGetProductString:\n\tcase effShellGetNextPlugin: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tvst_strncpy(dest, source, kVstMaxVendorStrLen);\n\t\treturn frame->value; }\n\n\tcase effGetParamName:\n\tcase effGetParamLabel:\n\tcase effGetParamDisplay: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n//\t\tvst_strncpy(dest, source, kVstMaxParamStrLen);\n//\t\tvst_strncpy(dest, source, kVstExtMaxParamStrLen);\n\n\t\t// Workaround for Variety of Sound plugins bug (non-printable characters)\n\t\tint i;\n\t\tfor(i = 0; i < kVstExtMaxParamStrLen - 1; ++i) {\n\t\t\tif(!isprint(source[i]))\n\t\t\t\tbreak;\n\n\t\t\tdest[i] = source[i];\n\t\t}\n\n\t\tdest[i] = '\\0';\n\t\treturn frame->value; }\n\n\tcase effGetEffectName: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tvst_strncpy(dest, source, kVstMaxEffectNameLen);\n\t\treturn frame->value; }\n\n\tcase effGetParameterProperties:\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tstd::memcpy(ptr, frame->data, sizeof(VstParameterProperties));\n\t\treturn frame->value;\n\n\tcase effGetOutputProperties:\n\tcase effGetInputProperties:\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tstd::memcpy(ptr, frame->data, sizeof(VstPinProperties));\n\t\treturn frame->value;\n\n\tcase effGetProgramNameIndexed: {\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tconst char* source = reinterpret_cast<const char*>(frame->data);\n\t\tchar* dest         = static_cast<char*>(ptr);\n\n\t\tvst_strncpy(dest, source, kVstMaxProgNameLen);\n\t\treturn frame->value; }\n\n\tcase effGetMidiKeyName:\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tstd::memcpy(ptr, frame->data, sizeof(MidiKeyName));\n\t\treturn frame->value;\n\n\tcase effProcessEvents: {\n\t\tVstEvents* events = static_cast<VstEvents*>(ptr);\n\t\tVstEvent* event = reinterpret_cast<VstEvent*>(frame->data);\n\t\tframe->index = events->numEvents;\n\n\t\tfor(int i = 0; i < events->numEvents; ++i)\n\t\t\tevent[i] = *events->events[i];\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value; }\n\n\tcase effGetChunk: {\n\t\tDEBUG(\"effGetChunk\");\n\n\t\t// Tell the block size to the host endpoint.\n\t\tptrdiff_t blockSize = port->frameSize() - sizeof(DataFrame);\n\t\tframe->value = blockSize;\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tDEBUG(\"effGetChunk: chunk size %d bytes\", frame->value);\n\n\t\t// If VST plugin supports the effGetChunk event, it has placed first data block\n\t\t// (or even the entire chunk) in the frame buffer.\n\t\tsize_t chunkSize = frame->value;\n\t\tsize_t count = frame->index;\n\n\t\tif(chunkSize == 0 || count == 0) {\n\t\t\tERROR(\"effGetChunk is unsupported by the VST plugin\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tchunk_.resize(chunkSize);\n\n\t\tauto it = chunk_.begin();\n\t\tit = std::copy(frame->data, frame->data + count, it);\n\n\t\twhile(it != chunk_.end()) {\n\t\t\tframe->command = Command::GetDataBlock;\n\t\t\tframe->index = std::min(blockSize, chunk_.end() - it);\n\n\t\t\tDEBUG(\"effGetChunk: requesting next %d bytes\", frame->index);\n\n\t\t\tport->sendRequest();\n\t\t\tport->waitResponse();\n\n\t\t\tsize_t count = frame->index;\n\t\t\tif(count == 0) {\n\t\t\t\tERROR(\"effGetChunk: premature end of data transmission\");\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tit = std::copy(frame->data, frame->data + count, it);\n\t\t}\n\n\t\tDEBUG(\"effGetChunk: received %d bytes\", chunkSize);\n\n\t\tvoid** chunk = static_cast<void**>(ptr);\n\t\t*chunk = static_cast<void*>(chunk_.data());\n\t\treturn chunkSize; }\n\n\tcase effSetChunk: {\n\t\tDEBUG(\"effSetChunk: %d bytes\", frame->value);\n\n\t\tsize_t chunkSize = frame->value;\n\t\tbool isPreset = frame->index;\n\t\tdata_ = static_cast<uint8_t*>(ptr);\n\t\tdataLength_ = frame->value;\n\t\tsize_t blockSize = port->frameSize() - sizeof(DataFrame);\n\n\t\twhile(dataLength_) {\n\t\t\tframe->command = Command::SetDataBlock;\n\t\t\tsize_t count = std::min(blockSize, dataLength_);\n\t\t\tframe->index = count;\n\t\t\tstd::memcpy(frame->data, data_, count);\n\n\t\t\tDEBUG(\"effSetChunk: sending next %d bytes\", count);\n\n\t\t\tport->sendRequest();\n\t\t\tport->waitResponse();\n\n\t\t\tdata_ += count;\n\t\t\tdataLength_ -= count;\n\t\t}\n\n\t\tframe->command = Command::Dispatch;\n\t\tframe->opcode = effSetChunk;\n\t\tframe->index = isPreset;\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\n\t\tDEBUG(\"effSetChunk: sent %d bytes\", chunkSize);\n\n\t\treturn frame->value; }\n\n\tcase effBeginLoadBank:\n\tcase effBeginLoadProgram:\n\t\tstd::memcpy(frame->data, ptr, sizeof(VstPatchChunkInfo));\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value;\n\n\tcase effSetSpeakerArrangement: {\n\t\tvoid* pluginInput = reinterpret_cast<void*>(value);\n\t\tvoid* pluginOutput = ptr;\n\n\t\tu8* data = frame->data;\n\t\tstd::memcpy(data, pluginInput, sizeof(VstSpeakerArrangement));\n\n\t\tdata += sizeof(VstSpeakerArrangement);\n\t\tstd::memcpy(data, pluginOutput, sizeof(VstSpeakerArrangement));\n\n\t\tport->sendRequest();\n\t\tport->waitResponse();\n\t\treturn frame->value; }\n\t}\n\n\tERROR(\"Unhandled dispatch event: %s\", kDispatchEvents[opcode]);\n\treturn 0;\n}\n\n\nvoid Plugin::sendXembedMessage(Display* display, Window window, long message, long detail,\n\t\tlong data1, long data2)\n{\n\tXEvent event;\n\n\tmemset(&event, 0, sizeof(event));\n\tevent.xclient.type = ClientMessage;\n\tevent.xclient.window = window;\n\tevent.xclient.message_type = XInternAtom(display, \"_XEMBED\", false);\n\tevent.xclient.format = 32;\n\tevent.xclient.data.l[0] = CurrentTime;\n\tevent.xclient.data.l[1] = message;\n\tevent.xclient.data.l[2] = detail;\n\tevent.xclient.data.l[3] = data1;\n\tevent.xclient.data.l[4] = data2;\n\n\tXSendEvent(display, window, false, NoEventMask, &event);\n\tXSync(display, false);\n}\n\n\nfloat Plugin::getParameter(i32 index)\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\tframe->command = Command::GetParameter;\n\tframe->index = index;\n\n\taudioPort_.sendRequest();\n\taudioPort_.waitResponse();\n\treturn frame->opt;\n}\n\n\nvoid Plugin::setParameter(i32 index, float value)\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\tframe->command = Command::SetParameter;\n\tframe->index = index;\n\tframe->opt = value;\n\n\taudioPort_.sendRequest();\n\taudioPort_.waitResponse();\n}\n\n\nvoid Plugin::processReplacing(float** inputs, float** outputs, i32 count)\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\tframe->command = Command::ProcessSingle;\n\tframe->value = count;\n\tfloat* data = reinterpret_cast<float*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numInputs; ++i) {\n\t\tstd::memcpy(data, inputs[i], sizeof(float) * count);\n\t\tdata += count;\n\t}\n\n\taudioPort_.sendRequest();\n\taudioPort_.waitResponse();\n\n\tdata = reinterpret_cast<float*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numOutputs; ++i) {\n\t\tstd::memcpy(outputs[i], data, sizeof(float) * count);\n\t\tdata += count;\n\t}\n}\n\n\nvoid Plugin::processDoubleReplacing(double** inputs, double** outputs, i32 count)\n{\n\tDataFrame* frame = audioPort_.frame<DataFrame>();\n\tframe->command = Command::ProcessDouble;\n\tframe->value = count;\n\tdouble* data = reinterpret_cast<double*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numInputs; ++i)\n\t\tdata = std::copy(inputs[i], inputs[i] + count, data);\n\n\taudioPort_.sendRequest();\n\taudioPort_.waitResponse();\n\n\tdata = reinterpret_cast<double*>(frame->data);\n\n\tfor(int i = 0; i < effect_->numOutputs; ++i)\n\t\tdata = std::copy(outputs[i], outputs[i] + count, data);\n}\n\n\nintptr_t Plugin::dispatchProc(AEffect* effect, i32 opcode, i32 index, intptr_t value,\n\t\tvoid* ptr, float opt)\n{\n\t// Most of VST hosts send some dispatch events in separate threads. So, if the\n\t// current thread is different than the main thread, we will send this event through\n\t// the audio port for processing it inside the dedicated audio thread by the host\n\t// endpoint.\n\n\tPlugin* plugin = static_cast<Plugin*>(effect->object);\n\tDataPort* port;\n\tRecursiveMutex* guard;\n\n\t// Ardour seems to be sending effEditOpen on something else besides the main thread.\n\t// However, we do want to send it to the control port, since that's where our\n\t// bridge expects it.\n\tif(opcode == effEditOpen || std::this_thread::get_id() == plugin->mainThreadId_) {\n\t\tport = &plugin->controlPort_;\n\t\tguard = &plugin->guard_;\n\t}\n\telse {\n\t\tport = &plugin->audioPort_;\n\t\tguard = &plugin->audioGuard_;\n\t}\n\n\tguard->lock();\n\tint result = plugin->dispatch(port, opcode, index, value, ptr, opt);\n\n\t// If opcode equals to effClose, then plugin will be destroyed inside of\n\t// plugin->dispatch() call, thus we don't need to unlock the mutex and can't\n\t// dereference the guard pointer here\n\tif(opcode != effClose)\n\t\tguard->unlock();\n\n\treturn result;\n}\n\n\nfloat Plugin::getParameterProc(AEffect* effect, i32 index)\n{\n\tPlugin* plugin = static_cast<Plugin*>(effect->object);\n\n\tif(plugin->lastIndex_ != -1 && std::this_thread::get_id() == plugin->lastThreadId_) {\n\t\tif(plugin->lastIndex_ != index) {\n\t\t\tERROR(\"Unable to get parameter (%d!=%d)\", plugin->lastIndex_, index);\n\t\t\treturn 0.0f;\n\t\t}\n\n\t\treturn plugin->lastValue_;\n\t}\n\n\tRecursiveLock lock(plugin->audioGuard_);\n\treturn plugin->getParameter(index);\n}\n\n\nvoid Plugin::setParameterProc(AEffect* effect, i32 index, float value)\n{\n\tPlugin* plugin = static_cast<Plugin*>(effect->object);\n\tRecursiveLock lock(plugin->audioGuard_);\n\tplugin->setParameter(index, value);\n}\n\n\nvoid Plugin::processReplacingProc(AEffect* effect, float** inputs, float** outputs,\n\t\ti32 sampleCount)\n{\n\tPlugin* plugin = static_cast<Plugin*>(effect->object);\n\tRecursiveLock lock(plugin->audioGuard_);\n\tplugin->processReplacing(inputs, outputs, sampleCount);\n}\n\n\nvoid Plugin::processDoubleReplacingProc(AEffect* effect, double** inputs,\n\t\tdouble** outputs, i32 sampleCount)\n{\n\tPlugin* plugin = static_cast<Plugin*>(effect->object);\n\tRecursiveLock lock(plugin->audioGuard_);\n\tplugin->processDoubleReplacing(inputs, outputs, sampleCount);\n}\n\n\n} // namespace Airwave\n"
  },
  {
    "path": "src/plugin/plugin.h",
    "content": "#ifndef PLUGIN_PLUGIN_H\n#define PLUGIN_PLUGIN_H\n\n#include <atomic>\n#include <mutex>\n#include <string>\n#include <thread>\n#include <vector>\n#include <X11/Xlib.h>\n#include \"common/dataport.h\"\n#include \"common/event.h\"\n#include \"common/vst24.h\"\n#include \"common/vsteventkeeper.h\"\n\n\nnamespace Airwave {\n\n\nusing RecursiveMutex = std::recursive_mutex;\nusing RecursiveLock = std::lock_guard<RecursiveMutex>;\n\n\nclass Plugin {\npublic:\n\tPlugin(const std::string& vstPath, const std::string& hostPath,\n\t\t   const std::string& prefixPath, const std::string& loaderPath,\n\t\t   const std::string& logSocketPath, AudioMasterProc masterProc);\n\n\t~Plugin();\n\n\tAEffect* effect();\n\nprivate:\n\tAudioMasterProc masterProc_;\n\tAEffect* effect_;\n\tERect rect_;\n\tVstEventKeeper events_;\n\n\tuint8_t* data_;\n\tsize_t dataLength_;\n\tstd::vector<uint8_t> chunk_;\n\n\tRecursiveMutex guard_;\n\tRecursiveMutex audioGuard_;\n\n\tDataPort controlPort_;\n\tDataPort callbackPort_;\n\tDataPort audioPort_;\n\n\tEvent condition_;\n\n\tint childPid_;\n\n\tstd::thread callbackThread_;\n\tstd::atomic_flag processCallbacks_;\n\tstd::thread::id mainThreadId_;\n\n\t// When handling audioMasterAutomate, Ardour calls back into the plugin using\n\t// getParameter(). It's unclear whether that's VST-legal or not, but at the moment it\n\t// seems to deadlock when processReplacing() is going on at the same time.\n\t// We will remember the parameter's value and feed it to the host when it will call\n\t// getParameter() from audioMasterAutomate handler.\n\ti32 lastIndex_;\n\tfloat lastValue_;\n\tstd::thread::id lastThreadId_;\n\n\tvoid callbackThread();\n\n\tintptr_t setBlockSize(DataPort* port, intptr_t frames);\n\n\tintptr_t handleAudioMaster();\n\n\tintptr_t dispatch(DataPort* port, i32 opcode, i32 index, intptr_t value, void* ptr,\n\t\t\tfloat opt);\n\n\tvoid sendXembedMessage(Display* display, Window window, long message, long detail,\n\t\t\tlong data1, long data2);\n\n\tfloat getParameter(i32 index);\n\tvoid setParameter(i32 index, float value);\n\n\tvoid processReplacing(float** inputs, float** outputs, i32 count);\n\tvoid processDoubleReplacing(double** inputs, double** outputs, i32 count);\n\n\tstatic intptr_t dispatchProc(AEffect* effect, i32 opcode, i32 index, intptr_t value,\n\t\t\tvoid* ptr, float opt);\n\n\tstatic float getParameterProc(AEffect* effect, i32 index);\n\tstatic void setParameterProc(AEffect* effect, i32 index, float value);\n\n\tstatic void processReplacingProc(AEffect* effect, float** inputs, float** outputs,\n\t\t\ti32 sampleCount);\n\n\tstatic void processDoubleReplacingProc(AEffect* effect, double** inputs,\n\t\t\tdouble** outputs, i32 sampleCount);\n};\n\n\n} // namespace Airwave\n\n\n#endif // PLUGIN_PLUGIN_H\n"
  }
]