diff --git a/src/CMakeData-arch.cmake b/src/CMakeData-arch.cmake index 62e2260d37..3d695d8397 100644 --- a/src/CMakeData-arch.cmake +++ b/src/CMakeData-arch.cmake @@ -206,6 +206,7 @@ list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver.h" list(APPEND SMDATA_ARCH_LIGHTS_SRC "arch/Lights/LightsDriver_SextetStream.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver_SextetStream.h") +list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/SextetUtils.h") # TODO: Confirm if Apple can use the export. if(NOT APPLE) @@ -214,10 +215,12 @@ if(NOT APPLE) if(WIN32) list(APPEND SMDATA_ARCH_LIGHTS_SRC + "arch/Lights/LightsDriver_Win32Serial.cpp" "arch/Lights/LightsDriver_Win32Parallel.cpp" "arch/Lights/LightsDriver_PacDrive.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver_Win32Parallel.h" + "arch/Lights/LightsDriver_Win32Serial.h" "arch/Lights/LightsDriver_PacDrive.cpp") if(WITH_MINIMAID) list(APPEND SMDATA_ARCH_LIGHTS_SRC diff --git a/src/Makefile.am b/src/Makefile.am index 67ca667b96..4d61e6c691 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -244,7 +244,8 @@ InputHandler += arch/InputHandler/InputHandler_MonkeyKeyboard.cpp arch/InputHand Lights += arch/Lights/LightsDriver_Export.cpp arch/Lights/LightsDriver_Export.h \ arch/Lights/LightsDriver_SystemMessage.cpp arch/Lights/LightsDriver_SystemMessage.h \ - arch/Lights/LightsDriver_SextetStream.cpp arch/Lights/LightsDriver_SextetStream.h + arch/Lights/LightsDriver_SextetStream.cpp arch/Lights/LightsDriver_SextetStream.h \ + arch/Lights/SextetUtils.h LoadingWindow += arch/LoadingWindow/LoadingWindow_Null.h @@ -395,6 +396,8 @@ if WINDOWS arch/InputHandler/InputHandler_Win32_Pump.cpp arch/InputHandler/InputHandler_Win32_Pump.h \ arch/InputHandler/InputHandler_Win32_RTIO.cpp arch/InputHandler/InputHandler_Win32_RTIO.h + Lights += arch/Lights/LightsDriver_Win32Serial.cpp arch/Lights/LightsDriver_Win32Serial.h + Sound += arch/Sound/RageSoundDriver_DSound_Software.cpp arch/Sound/RageSoundDriver_DSound_Software.h \ arch/Sound/DSoundHelpers.cpp arch/Sound/DSoundHelpers.h \ arch/Sound/RageSoundDriver_WaveOut.cpp arch/Sound/RageSoundDriver_WaveOut.h diff --git a/src/arch/Lights/LightsDriver_SextetStream.cpp b/src/arch/Lights/LightsDriver_SextetStream.cpp index 26d1e5e787..e1be922fd4 100644 --- a/src/arch/Lights/LightsDriver_SextetStream.cpp +++ b/src/arch/Lights/LightsDriver_SextetStream.cpp @@ -3,146 +3,12 @@ #include "PrefsManager.h" #include "RageLog.h" #include "RageUtil.h" +#include "SextetUtils.h" #include using namespace std; -// Number of printable characters used to encode lights -static const size_t CABINET_SEXTET_COUNT = 1; -static const size_t CONTROLLER_SEXTET_COUNT = 6; - -// Number of bytes to contain the full pack and a trailing LF -static const size_t FULL_SEXTET_COUNT = CABINET_SEXTET_COUNT + (NUM_GameController * CONTROLLER_SEXTET_COUNT) + 1; - - -// Serialization routines - -// Encodes the low 6 bits of a byte as a printable, non-space ASCII -// character (i.e., within the range 0x21-0x7E) such that the low 6 bits of -// the character are the same as the input. -inline uint8_t printableSextet(uint8_t data) -{ - // Maps the 6-bit value into the range 0x30-0x6F, wrapped in such a way - // that the low 6 bits of the result are the same as the data (so - // decoding is trivial). - // - // 00nnnn -> 0100nnnn (0x4n) - // 01nnnn -> 0101nnnn (0x5n) - // 10nnnn -> 0110nnnn (0x6n) - // 11nnnn -> 0011nnnn (0x3n) - - // Put another way, the top 4 bits H of the output are determined from - // the top two bits T of the input like so: - // H = ((T + 1) mod 4) + 3 - - return ((data + (uint8_t)0x10) & (uint8_t)0x3F) + (uint8_t)0x30; -} - -// Packs 6 booleans into a 6-bit value -inline uint8_t packPlainSextet(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5) -{ - return (uint8_t)( - (b0 ? 0x01 : 0) | - (b1 ? 0x02 : 0) | - (b2 ? 0x04 : 0) | - (b3 ? 0x08 : 0) | - (b4 ? 0x10 : 0) | - (b5 ? 0x20 : 0)); -} - -// Packs 6 booleans into a printable sextet -inline uint8_t packPrintableSextet(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5) -{ - return printableSextet(packPlainSextet(b0, b1, b2, b3, b4, b5)); -} - -// Packs the cabinet lights into a printable sextet and adds it to a buffer -inline size_t packCabinetLights(const LightsState *ls, uint8_t* buffer) -{ - buffer[0] = packPrintableSextet( - ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT], - ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT], - ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT], - ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT], - ls->m_bCabinetLights[LIGHT_BASS_LEFT], - ls->m_bCabinetLights[LIGHT_BASS_RIGHT]); - return CABINET_SEXTET_COUNT; -} - -// Packs the button lights for a controller into 6 printable sextets and -// adds them to a buffer -inline size_t packControllerLights(const LightsState *ls, GameController gc, uint8_t* buffer) -{ - // Menu buttons - buffer[0] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_MENULEFT], - ls->m_bGameButtonLights[gc][GAME_BUTTON_MENURIGHT], - ls->m_bGameButtonLights[gc][GAME_BUTTON_MENUUP], - ls->m_bGameButtonLights[gc][GAME_BUTTON_MENUDOWN], - ls->m_bGameButtonLights[gc][GAME_BUTTON_START], - ls->m_bGameButtonLights[gc][GAME_BUTTON_SELECT]); - - // Other non-sensors - buffer[1] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_BACK], - ls->m_bGameButtonLights[gc][GAME_BUTTON_COIN], - ls->m_bGameButtonLights[gc][GAME_BUTTON_OPERATOR], - ls->m_bGameButtonLights[gc][GAME_BUTTON_EFFECT_UP], - ls->m_bGameButtonLights[gc][GAME_BUTTON_EFFECT_DOWN], - false); - - // Sensors - buffer[2] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_01], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_02], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_03], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_04], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_05], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_06]); - buffer[3] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_07], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_08], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_09], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_10], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_11], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_12]); - buffer[4] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_13], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_14], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_15], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_16], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_17], - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_18]); - buffer[5] = packPrintableSextet( - ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_19], - false, - false, - false, - false, - false); - - return CONTROLLER_SEXTET_COUNT; -} - -inline size_t packLine(uint8_t * buffer, const LightsState* ls) -{ - size_t index = 0; - - index += packCabinetLights(ls, &(buffer[index])); - - FOREACH_ENUM(GameController, gc) - { - index += packControllerLights(ls, gc, &(buffer[index])); - } - - // Terminate with LF - buffer[index++] = 0xA; - - return index; -} - - // Private members/methods are kept out of the header using an opaque pointer `_impl`. // Google "pimpl idiom" for an explanation of what's going on and why it is (or might be) useful. diff --git a/src/arch/Lights/LightsDriver_Win32Serial.cpp b/src/arch/Lights/LightsDriver_Win32Serial.cpp new file mode 100644 index 0000000000..3f06a5b6d3 --- /dev/null +++ b/src/arch/Lights/LightsDriver_Win32Serial.cpp @@ -0,0 +1,110 @@ +#include "global.h" +#include "LightsDriver_Win32Serial.h" +#include "windows.h" +#include "RageUtil.h" + +REGISTER_LIGHTS_DRIVER_CLASS(Win32Serial); + +static Preference g_sLightsComPort("LightsComPort", "COM54"); + +HANDLE serialPort; + +LightsDriver_Win32Serial::LightsDriver_Win32Serial() +{ + // Ensure a non-match the first time + lastOutput[0] = 0; + + RString sComPort = g_sLightsComPort.Get(); + + serialPort = CreateFile(RString("\\\\.\\").append(sComPort).c_str(), + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (serialPort == INVALID_HANDLE_VALUE) { + MessageBox(nullptr, "Could not find a device on the configured COM port.", "ERROR", MB_OK); + return; + } + + DCB dcb_serial_params = { 0 }; + dcb_serial_params.DCBlength = sizeof(dcb_serial_params); + + if (GetCommState(serialPort, &dcb_serial_params) == 0) { + MessageBox(nullptr, "Could not connect to a device on the configured COM port.", "ERROR", MB_OK); + return; + } + + dcb_serial_params.BaudRate = CBR_115200; // Setting BaudRate = 115200 + dcb_serial_params.ByteSize = 8; // Setting ByteSize = 8 + dcb_serial_params.StopBits = ONESTOPBIT;// Setting StopBits = 1 + dcb_serial_params.Parity = NOPARITY; // Setting Parity = None + + if (SetCommState(serialPort, &dcb_serial_params) == 0) { + MessageBox(nullptr, "Could not setup the device parameters on the configured COM port.", "ERROR", MB_OK); + return; + } + + COMMTIMEOUTS timeouts = { 0 }; + timeouts.ReadIntervalTimeout = 50; // in milliseconds + timeouts.ReadTotalTimeoutConstant = 50; // in milliseconds + timeouts.ReadTotalTimeoutMultiplier = 10; // in milliseconds + timeouts.WriteTotalTimeoutConstant = 50; // in milliseconds + timeouts.WriteTotalTimeoutMultiplier = 10; // in milliseconds + + if (SetCommTimeouts(serialPort, &timeouts) == 0) { + MessageBox(nullptr, "Could not set the device timeouts on the configured COM port.", "ERROR", MB_OK); + return; + } +} + +LightsDriver_Win32Serial::~LightsDriver_Win32Serial() +{ + CloseHandle(serialPort); +} + +void LightsDriver_Win32Serial::Set(const LightsState* ls) +{ + if (serialPort != INVALID_HANDLE_VALUE) { + uint8_t buffer[FULL_SEXTET_COUNT]; + + packLine(buffer, ls); + + // Only write if the message has changed since the last write. + if (memcmp(buffer, lastOutput, FULL_SEXTET_COUNT) != 0) + { + DWORD bytesWritten = 0; + WriteFile(serialPort, buffer, FULL_SEXTET_COUNT, &bytesWritten, NULL); + + // Remember last message + memcpy(lastOutput, buffer, FULL_SEXTET_COUNT); + } + } +} + +/* + * (c) 2020 Skogaby + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/src/arch/Lights/LightsDriver_Win32Serial.h b/src/arch/Lights/LightsDriver_Win32Serial.h new file mode 100644 index 0000000000..427a4ec8ce --- /dev/null +++ b/src/arch/Lights/LightsDriver_Win32Serial.h @@ -0,0 +1,48 @@ +#ifndef LightsDriver_SextetStream_H +#define LightsDriver_SextetStream_H + +/* + * `LightsDriver_Win32Serial` (abstract): Streams the light data (in + * ASCII-safe sextets) to a pre-configure serial port. The protocol + * of the messages sent follows the same protocol as the SextetStream + * lights driver, but it sent to serial instead of a FIFO or pipe. + */ + +#include "LightsDriver.h" +#include "SextetUtils.h" + +class LightsDriver_Win32Serial : public LightsDriver +{ +protected: + uint8_t lastOutput[FULL_SEXTET_COUNT]; +public: + LightsDriver_Win32Serial(); + virtual ~LightsDriver_Win32Serial(); + + virtual void Set(const LightsState* ls); +}; + +#endif + +/* + * Copyright © 2020 Skogaby + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/arch/Lights/SextetUtils.h b/src/arch/Lights/SextetUtils.h new file mode 100644 index 0000000000..42d7bf7ea1 --- /dev/null +++ b/src/arch/Lights/SextetUtils.h @@ -0,0 +1,167 @@ +#ifndef SextetUtils_H +#define SextetUtils_H + +#include "LightsDriver.h" + +/* + * Utility functions that both `LightsDriver_Win32Serial` and `LightsDriver_SextetStream` + * take advantage of, in order to encode the lights data into a common format. + */ + + // Number of printable characters used to encode lights +static const size_t CABINET_SEXTET_COUNT = 1; +static const size_t CONTROLLER_SEXTET_COUNT = 6; + +// Number of bytes to contain the full pack and a trailing LF +static const size_t FULL_SEXTET_COUNT = CABINET_SEXTET_COUNT + (NUM_GameController * CONTROLLER_SEXTET_COUNT) + 1; + +// Serialization routines + +// Encodes the low 6 bits of a byte as a printable, non-space ASCII +// character (i.e., within the range 0x21-0x7E) such that the low 6 bits of +// the character are the same as the input. +inline uint8_t printableSextet(uint8_t data) +{ + // Maps the 6-bit value into the range 0x30-0x6F, wrapped in such a way + // that the low 6 bits of the result are the same as the data (so + // decoding is trivial). + // + // 00nnnn -> 0100nnnn (0x4n) + // 01nnnn -> 0101nnnn (0x5n) + // 10nnnn -> 0110nnnn (0x6n) + // 11nnnn -> 0011nnnn (0x3n) + + // Put another way, the top 4 bits H of the output are determined from + // the top two bits T of the input like so: + // H = ((T + 1) mod 4) + 3 + + return ((data + (uint8_t)0x10) & (uint8_t)0x3F) + (uint8_t)0x30; +} + +// Packs 6 booleans into a 6-bit value +inline uint8_t packPlainSextet(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5) +{ + return (uint8_t)( + (b0 ? 0x01 : 0) | + (b1 ? 0x02 : 0) | + (b2 ? 0x04 : 0) | + (b3 ? 0x08 : 0) | + (b4 ? 0x10 : 0) | + (b5 ? 0x20 : 0)); +} + +// Packs 6 booleans into a printable sextet +inline uint8_t packPrintableSextet(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5) +{ + return printableSextet(packPlainSextet(b0, b1, b2, b3, b4, b5)); +} + +// Packs the cabinet lights into a printable sextet and adds it to a buffer +inline size_t packCabinetLights(const LightsState* ls, uint8_t* buffer) +{ + buffer[0] = packPrintableSextet( + ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT], + ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT], + ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT], + ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT], + ls->m_bCabinetLights[LIGHT_BASS_LEFT], + ls->m_bCabinetLights[LIGHT_BASS_RIGHT]); + return CABINET_SEXTET_COUNT; +} + +// Packs the button lights for a controller into 6 printable sextets and +// adds them to a buffer +inline size_t packControllerLights(const LightsState* ls, GameController gc, uint8_t* buffer) +{ + // Menu buttons + buffer[0] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_MENULEFT], + ls->m_bGameButtonLights[gc][GAME_BUTTON_MENURIGHT], + ls->m_bGameButtonLights[gc][GAME_BUTTON_MENUUP], + ls->m_bGameButtonLights[gc][GAME_BUTTON_MENUDOWN], + ls->m_bGameButtonLights[gc][GAME_BUTTON_START], + ls->m_bGameButtonLights[gc][GAME_BUTTON_SELECT]); + + // Other non-sensors + buffer[1] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_BACK], + ls->m_bGameButtonLights[gc][GAME_BUTTON_COIN], + ls->m_bGameButtonLights[gc][GAME_BUTTON_OPERATOR], + ls->m_bGameButtonLights[gc][GAME_BUTTON_EFFECT_UP], + ls->m_bGameButtonLights[gc][GAME_BUTTON_EFFECT_DOWN], + false); + + // Sensors + buffer[2] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_01], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_02], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_03], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_04], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_05], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_06]); + buffer[3] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_07], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_08], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_09], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_10], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_11], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_12]); + buffer[4] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_13], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_14], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_15], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_16], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_17], + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_18]); + buffer[5] = packPrintableSextet( + ls->m_bGameButtonLights[gc][GAME_BUTTON_CUSTOM_19], + false, + false, + false, + false, + false); + + return CONTROLLER_SEXTET_COUNT; +} + +inline size_t packLine(uint8_t* buffer, const LightsState* ls) +{ + size_t index = 0; + + index += packCabinetLights(ls, &(buffer[index])); + + FOREACH_ENUM(GameController, gc) + { + index += packControllerLights(ls, gc, &(buffer[index])); + } + + // Terminate with LF + buffer[index++] = 0xA; + + return index; +} + +#endif + +/* + * Copyright © 2014 Peter S. May + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */