-/*\r
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
-*\r
-* This file is part of CasparCG (www.casparcg.com).\r
-*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
-*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-* GNU General Public License for more details.\r
-*\r
-* You should have received a copy of the GNU General Public License\r
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
-*\r
-* Author: Nicklas P Andersson\r
-*/\r
-\r
- \r
-#include "../StdAfx.h"\r
-\r
-#include "AMCPProtocolStrategy.h"\r
-\r
-#include "../util/AsyncEventServer.h"\r
-#include "AMCPCommandsImpl.h"\r
-\r
-#include <stdio.h>\r
-#include <crtdbg.h>\r
-#include <string.h>\r
-#include <algorithm>\r
-#include <cctype>\r
-\r
-#include <boost/algorithm/string/trim.hpp>\r
-#include <boost/algorithm/string/split.hpp>\r
-#include <boost/algorithm/string/replace.hpp>\r
-#include <boost/lexical_cast.hpp>\r
-\r
-#if defined(_MSC_VER)\r
-#pragma warning (push, 1) // TODO: Legacy code, just disable warnings\r
-#endif\r
-\r
-namespace caspar { namespace protocol { namespace amcp {\r
-\r
-using IO::ClientInfoPtr;\r
-\r
-const std::wstring AMCPProtocolStrategy::MessageDelimiter = TEXT("\r\n");\r
-\r
-inline std::shared_ptr<core::video_channel> GetChannelSafe(unsigned int index, const std::vector<spl::shared_ptr<core::video_channel>>& channels)\r
-{\r
- return index < channels.size() ? std::shared_ptr<core::video_channel>(channels[index]) : nullptr;\r
-}\r
-\r
-AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : channels_(channels) {\r
- AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());\r
- commandQueues_.push_back(pGeneralCommandQueue);\r
-\r
-\r
- std::shared_ptr<core::video_channel> pChannel;\r
- unsigned int index = -1;\r
- //Create a commandpump for each video_channel\r
- while((pChannel = GetChannelSafe(++index, channels_)) != 0) {\r
- AMCPCommandQueuePtr pChannelCommandQueue(new AMCPCommandQueue());\r
- std::wstring title = TEXT("video_channel ");\r
-\r
- //HACK: Perform real conversion from int to string\r
- TCHAR num = TEXT('1')+static_cast<TCHAR>(index);\r
- title += num;\r
- \r
- commandQueues_.push_back(pChannelCommandQueue);\r
- }\r
-}\r
-\r
-AMCPProtocolStrategy::~AMCPProtocolStrategy() {\r
-}\r
-\r
-void AMCPProtocolStrategy::Parse(const TCHAR* pData, int charCount, ClientInfoPtr pClientInfo)\r
-{\r
- size_t pos;\r
- std::wstring recvData(pData, charCount);\r
- std::wstring availibleData = (pClientInfo != nullptr ? pClientInfo->currentMessage_ : L"") + recvData;\r
-\r
- while(true) {\r
- pos = availibleData.find(MessageDelimiter);\r
- if(pos != std::wstring::npos)\r
- {\r
- std::wstring message = availibleData.substr(0,pos);\r
-\r
- //This is where a complete message gets taken care of\r
- if(message.length() > 0) {\r
- ProcessMessage(message, pClientInfo);\r
- }\r
-\r
- std::size_t nextStartPos = pos + MessageDelimiter.length();\r
- if(nextStartPos < availibleData.length())\r
- availibleData = availibleData.substr(nextStartPos);\r
- else {\r
- availibleData.clear();\r
- break;\r
- }\r
- }\r
- else\r
- {\r
- break;\r
- }\r
- }\r
- if(pClientInfo)\r
- pClientInfo->currentMessage_ = availibleData;\r
-}\r
-\r
-void AMCPProtocolStrategy::ProcessMessage(const std::wstring& message, ClientInfoPtr& pClientInfo)\r
-{ \r
- CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";\r
- \r
- bool bError = true;\r
- MessageParserState state = New;\r
-\r
- AMCPCommandPtr pCommand;\r
-\r
- pCommand = InterpretCommandString(message, &state);\r
-\r
- if(pCommand != 0) {\r
- pCommand->SetClientInfo(pClientInfo); \r
- if(QueueCommand(pCommand))\r
- bError = false;\r
- else\r
- state = GetChannel;\r
- }\r
-\r
- if(bError == true) {\r
- std::wstringstream answer;\r
- switch(state)\r
- {\r
- case GetCommand:\r
- answer << TEXT("400 ERROR\r\n") + message << "\r\n";\r
- break;\r
- case GetChannel:\r
- answer << TEXT("401 ERROR\r\n");\r
- break;\r
- case GetParameters:\r
- answer << TEXT("402 ERROR\r\n");\r
- break;\r
- default:\r
- answer << TEXT("500 FAILED\r\n");\r
- break;\r
- }\r
- pClientInfo->Send(answer.str());\r
- }\r
-}\r
-\r
-AMCPCommandPtr AMCPProtocolStrategy::InterpretCommandString(const std::wstring& message, MessageParserState* pOutState)\r
-{\r
- std::vector<std::wstring> tokens;\r
- unsigned int currentToken = 0;\r
- std::wstring commandSwitch;\r
-\r
- AMCPCommandPtr pCommand;\r
- MessageParserState state = New;\r
-\r
- std::size_t tokensInMessage = TokenizeMessage(message, &tokens);\r
-\r
- //parse the message one token at the time\r
- while(currentToken < tokensInMessage)\r
- {\r
- switch(state)\r
- {\r
- case New:\r
- if(tokens[currentToken][0] == TEXT('/'))\r
- state = GetSwitch;\r
- else\r
- state = GetCommand;\r
- break;\r
-\r
- case GetSwitch:\r
- commandSwitch = tokens[currentToken];\r
- state = GetCommand;\r
- ++currentToken;\r
- break;\r
-\r
- case GetCommand:\r
- pCommand = CommandFactory(tokens[currentToken]);\r
- if(pCommand == 0) {\r
- goto ParseFinnished;\r
- }\r
- else\r
- {\r
- pCommand->SetChannels(channels_);\r
- //Set scheduling\r
- if(commandSwitch.size() > 0) {\r
- transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);\r
-\r
- //if(commandSwitch == TEXT("/APP"))\r
- // pCommand->SetScheduling(AddToQueue);\r
- //else if(commandSwitch == TEXT("/IMMF"))\r
- // pCommand->SetScheduling(ImmediatelyAndClear);\r
- }\r
-\r
- if(pCommand->NeedChannel())\r
- state = GetChannel;\r
- else\r
- state = GetParameters;\r
- }\r
- ++currentToken;\r
- break;\r
-\r
- case GetParameters:\r
- {\r
- _ASSERTE(pCommand != 0);\r
- int parameterCount=0;\r
- while(currentToken<tokensInMessage)\r
- {\r
- pCommand->AddParameter(tokens[currentToken++]);\r
- ++parameterCount;\r
- }\r
-\r
- if(parameterCount < pCommand->GetMinimumParameters()) {\r
- goto ParseFinnished;\r
- }\r
-\r
- state = Done;\r
- break;\r
- }\r
-\r
- case GetChannel:\r
- {\r
-// assert(pCommand != 0);\r
-\r
- std::wstring str = boost::trim_copy(tokens[currentToken]);\r
- std::vector<std::wstring> split;\r
- boost::split(split, str, boost::is_any_of("-"));\r
- \r
- int channelIndex = -1;\r
- int layerIndex = -1;\r
- try\r
- {\r
- channelIndex = boost::lexical_cast<int>(split[0]) - 1;\r
-\r
- if(split.size() > 1)\r
- layerIndex = boost::lexical_cast<int>(split[1]);\r
- }\r
- catch(...)\r
- {\r
- goto ParseFinnished;\r
- }\r
-\r
- std::shared_ptr<core::video_channel> pChannel = GetChannelSafe(channelIndex, channels_);\r
- if(pChannel == 0) {\r
- goto ParseFinnished;\r
- }\r
-\r
- pCommand->SetChannel(pChannel);\r
- pCommand->SetChannels(channels_);\r
- pCommand->SetChannelIndex(channelIndex);\r
- pCommand->SetLayerIntex(layerIndex);\r
-\r
- state = GetParameters;\r
- ++currentToken;\r
- break;\r
- }\r
-\r
- default: //Done and unexpected\r
- goto ParseFinnished;\r
- }\r
- }\r
-\r
-ParseFinnished:\r
- if(state == GetParameters && pCommand->GetMinimumParameters()==0)\r
- state = Done;\r
-\r
- if(state != Done) {\r
- pCommand.reset();\r
- }\r
-\r
- if(pOutState != 0) {\r
- *pOutState = state;\r
- }\r
-\r
- return pCommand;\r
-}\r
-\r
-bool AMCPProtocolStrategy::QueueCommand(AMCPCommandPtr pCommand) {\r
- if(pCommand->NeedChannel()) {\r
- unsigned int channelIndex = pCommand->GetChannelIndex() + 1;\r
- if(commandQueues_.size() > channelIndex) {\r
- commandQueues_[channelIndex]->AddCommand(pCommand);\r
- }\r
- else\r
- return false;\r
- }\r
- else {\r
- commandQueues_[0]->AddCommand(pCommand);\r
- }\r
- return true;\r
-}\r
-\r
-AMCPCommandPtr AMCPProtocolStrategy::CommandFactory(const std::wstring& str)\r
-{\r
- std::wstring s = str;\r
- transform(s.begin(), s.end(), s.begin(), toupper);\r
- \r
- if (s == TEXT("MIXER")) return std::make_shared<MixerCommand>();\r
- else if(s == TEXT("DIAG")) return std::make_shared<DiagnosticsCommand>();\r
- else if(s == TEXT("CHANNEL_GRID")) return std::make_shared<ChannelGridCommand>();\r
- else if(s == TEXT("CALL")) return std::make_shared<CallCommand>();\r
- else if(s == TEXT("SWAP")) return std::make_shared<SwapCommand>();\r
- else if(s == TEXT("LOAD")) return std::make_shared<LoadCommand>();\r
- else if(s == TEXT("LOADBG")) return std::make_shared<LoadbgCommand>();\r
- else if(s == TEXT("ADD")) return std::make_shared<AddCommand>();\r
- else if(s == TEXT("REMOVE")) return std::make_shared<RemoveCommand>();\r
- else if(s == TEXT("PAUSE")) return std::make_shared<PauseCommand>();\r
- else if(s == TEXT("PLAY")) return std::make_shared<PlayCommand>();\r
- else if(s == TEXT("STOP")) return std::make_shared<StopCommand>();\r
- else if(s == TEXT("CLEAR")) return std::make_shared<ClearCommand>();\r
- else if(s == TEXT("PRINT")) return std::make_shared<PrintCommand>();\r
- else if(s == TEXT("LOG")) return std::make_shared<LogCommand>();\r
- else if(s == TEXT("CG")) return std::make_shared<CGCommand>();\r
- else if(s == TEXT("DATA")) return std::make_shared<DataCommand>();\r
- else if(s == TEXT("CINF")) return std::make_shared<CinfCommand>();\r
- else if(s == TEXT("INFO")) return std::make_shared<InfoCommand>(channels_);\r
- else if(s == TEXT("CLS")) return std::make_shared<ClsCommand>();\r
- else if(s == TEXT("TLS")) return std::make_shared<TlsCommand>();\r
- else if(s == TEXT("VERSION")) return std::make_shared<VersionCommand>();\r
- else if(s == TEXT("BYE")) return std::make_shared<ByeCommand>();\r
- else if(s == TEXT("SET")) return std::make_shared<SetCommand>();\r
- //else if(s == TEXT("MONITOR"))\r
- //{\r
- // result = AMCPCommandPtr(new MonitorCommand());\r
- //}\r
- //else if(s == TEXT("KILL"))\r
- //{\r
- // result = AMCPCommandPtr(new KillCommand());\r
- //}\r
- return nullptr;\r
-}\r
-\r
-std::size_t AMCPProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)\r
-{\r
- //split on whitespace but keep strings within quotationmarks\r
- //treat \ as the start of an escape-sequence: the following char will indicate what to actually put in the string\r
-\r
- std::wstring currentToken;\r
-\r
- char inQuote = 0;\r
- bool getSpecialCode = false;\r
-\r
- for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex)\r
- {\r
- if(getSpecialCode)\r
- {\r
- //insert code-handling here\r
- switch(message[charIndex])\r
- {\r
- case TEXT('\\'):\r
- currentToken += TEXT("\\");\r
- break;\r
- case TEXT('\"'):\r
- currentToken += TEXT("\"");\r
- break;\r
- case TEXT('n'):\r
- currentToken += TEXT("\n");\r
- break;\r
- default:\r
- break;\r
- };\r
- getSpecialCode = false;\r
- continue;\r
- }\r
-\r
- if(message[charIndex]==TEXT('\\'))\r
- {\r
- getSpecialCode = true;\r
- continue;\r
- }\r
-\r
- if(message[charIndex]==' ' && inQuote==false)\r
- {\r
- if(currentToken.size()>0)\r
- {\r
- pTokenVector->push_back(currentToken);\r
- currentToken.clear();\r
- }\r
- continue;\r
- }\r
-\r
- if(message[charIndex]==TEXT('\"'))\r
- {\r
- inQuote ^= 1;\r
-\r
- if(currentToken.size()>0)\r
- {\r
- pTokenVector->push_back(currentToken);\r
- currentToken.clear();\r
- }\r
- continue;\r
- }\r
-\r
- currentToken += message[charIndex];\r
- }\r
-\r
- if(currentToken.size()>0)\r
- {\r
- pTokenVector->push_back(currentToken);\r
- currentToken.clear();\r
- }\r
-\r
- return pTokenVector->size();\r
-}\r
-\r
-} //namespace amcp\r
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Nicklas P Andersson
+*/
+
+
+#include "../StdAfx.h"
+
+#include "AMCPProtocolStrategy.h"
+
+#include "../util/AsyncEventServer.h"
+#include "AMCPCommandsImpl.h"
+
+#include <stdio.h>
+#include <crtdbg.h>
+#include <string.h>
+#include <algorithm>
+#include <cctype>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/lexical_cast.hpp>
+
+#if defined(_MSC_VER)
+#pragma warning (push, 1) // TODO: Legacy code, just disable warnings
+#endif
+
+namespace caspar { namespace protocol { namespace amcp {
+
+using IO::ClientInfoPtr;
+
+const std::wstring AMCPProtocolStrategy::MessageDelimiter = TEXT("\r\n");
+
+inline std::shared_ptr<core::video_channel> GetChannelSafe(unsigned int index, const std::vector<spl::shared_ptr<core::video_channel>>& channels)
+{
+ return index < channels.size() ? std::shared_ptr<core::video_channel>(channels[index]) : nullptr;
+}
+
+AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) : channels_(channels) {
+ AMCPCommandQueuePtr pGeneralCommandQueue(new AMCPCommandQueue());
+ commandQueues_.push_back(pGeneralCommandQueue);
+
+
+ std::shared_ptr<core::video_channel> pChannel;
+ unsigned int index = -1;
+ //Create a commandpump for each video_channel
+ while((pChannel = GetChannelSafe(++index, channels_)) != 0) {
+ AMCPCommandQueuePtr pChannelCommandQueue(new AMCPCommandQueue());
+ std::wstring title = TEXT("video_channel ");
+
+ //HACK: Perform real conversion from int to string
+ TCHAR num = TEXT('1')+static_cast<TCHAR>(index);
+ title += num;
+
+ commandQueues_.push_back(pChannelCommandQueue);
+ }
+}
+
+AMCPProtocolStrategy::~AMCPProtocolStrategy() {
+}
+
+void AMCPProtocolStrategy::Parse(const TCHAR* pData, int charCount, ClientInfoPtr pClientInfo)
+{
+ size_t pos;
+ std::wstring recvData(pData, charCount);
+ std::wstring availibleData = (pClientInfo != nullptr ? pClientInfo->currentMessage_ : L"") + recvData;
+
+ while(true) {
+ pos = availibleData.find(MessageDelimiter);
+ if(pos != std::wstring::npos)
+ {
+ std::wstring message = availibleData.substr(0,pos);
+
+ //This is where a complete message gets taken care of
+ if(message.length() > 0) {
+ ProcessMessage(message, pClientInfo);
+ }
+
+ std::size_t nextStartPos = pos + MessageDelimiter.length();
+ if(nextStartPos < availibleData.length())
+ availibleData = availibleData.substr(nextStartPos);
+ else {
+ availibleData.clear();
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ if(pClientInfo)
+ pClientInfo->currentMessage_ = availibleData;
+}
+
+void AMCPProtocolStrategy::ProcessMessage(const std::wstring& message, ClientInfoPtr& pClientInfo)
+{
+ CASPAR_LOG(info) << L"Received message from " << pClientInfo->print() << ": " << message << L"\\r\\n";
+
+ bool bError = true;
+ MessageParserState state = New;
+
+ AMCPCommandPtr pCommand;
+
+ pCommand = InterpretCommandString(message, &state);
+
+ if(pCommand != 0) {
+ pCommand->SetClientInfo(pClientInfo);
+ if(QueueCommand(pCommand))
+ bError = false;
+ else
+ state = GetChannel;
+ }
+
+ if(bError == true) {
+ std::wstringstream answer;
+ switch(state)
+ {
+ case GetCommand:
+ answer << TEXT("400 ERROR\r\n") + message << "\r\n";
+ break;
+ case GetChannel:
+ answer << TEXT("401 ERROR\r\n");
+ break;
+ case GetParameters:
+ answer << TEXT("402 ERROR\r\n");
+ break;
+ default:
+ answer << TEXT("500 FAILED\r\n");
+ break;
+ }
+ pClientInfo->Send(answer.str());
+ }
+}
+
+AMCPCommandPtr AMCPProtocolStrategy::InterpretCommandString(const std::wstring& message, MessageParserState* pOutState)
+{
+ std::vector<std::wstring> tokens;
+ unsigned int currentToken = 0;
+ std::wstring commandSwitch;
+
+ AMCPCommandPtr pCommand;
+ MessageParserState state = New;
+
+ std::size_t tokensInMessage = TokenizeMessage(message, &tokens);
+
+ //parse the message one token at the time
+ while(currentToken < tokensInMessage)
+ {
+ switch(state)
+ {
+ case New:
+ if(tokens[currentToken][0] == TEXT('/'))
+ state = GetSwitch;
+ else
+ state = GetCommand;
+ break;
+
+ case GetSwitch:
+ commandSwitch = tokens[currentToken];
+ state = GetCommand;
+ ++currentToken;
+ break;
+
+ case GetCommand:
+ pCommand = CommandFactory(tokens[currentToken]);
+ if(pCommand == 0) {
+ goto ParseFinnished;
+ }
+ else
+ {
+ pCommand->SetChannels(channels_);
+ //Set scheduling
+ if(commandSwitch.size() > 0) {
+ transform(commandSwitch.begin(), commandSwitch.end(), commandSwitch.begin(), toupper);
+
+ //if(commandSwitch == TEXT("/APP"))
+ // pCommand->SetScheduling(AddToQueue);
+ //else if(commandSwitch == TEXT("/IMMF"))
+ // pCommand->SetScheduling(ImmediatelyAndClear);
+ }
+
+ if(pCommand->NeedChannel())
+ state = GetChannel;
+ else
+ state = GetParameters;
+ }
+ ++currentToken;
+ break;
+
+ case GetParameters:
+ {
+ _ASSERTE(pCommand != 0);
+ int parameterCount=0;
+ while(currentToken<tokensInMessage)
+ {
+ pCommand->AddParameter(tokens[currentToken++]);
+ ++parameterCount;
+ }
+
+ if(parameterCount < pCommand->GetMinimumParameters()) {
+ goto ParseFinnished;
+ }
+
+ state = Done;
+ break;
+ }
+
+ case GetChannel:
+ {
+// assert(pCommand != 0);
+
+ std::wstring str = boost::trim_copy(tokens[currentToken]);
+ std::vector<std::wstring> split;
+ boost::split(split, str, boost::is_any_of("-"));
+
+ int channelIndex = -1;
+ int layerIndex = -1;
+ try
+ {
+ channelIndex = boost::lexical_cast<int>(split[0]) - 1;
+
+ if(split.size() > 1)
+ layerIndex = boost::lexical_cast<int>(split[1]);
+ }
+ catch(...)
+ {
+ goto ParseFinnished;
+ }
+
+ std::shared_ptr<core::video_channel> pChannel = GetChannelSafe(channelIndex, channels_);
+ if(pChannel == 0) {
+ goto ParseFinnished;
+ }
+
+ pCommand->SetChannel(pChannel);
+ pCommand->SetChannels(channels_);
+ pCommand->SetChannelIndex(channelIndex);
+ pCommand->SetLayerIntex(layerIndex);
+
+ state = GetParameters;
+ ++currentToken;
+ break;
+ }
+
+ default: //Done and unexpected
+ goto ParseFinnished;
+ }
+ }
+
+ParseFinnished:
+ if(state == GetParameters && pCommand->GetMinimumParameters()==0)
+ state = Done;
+
+ if(state != Done) {
+ pCommand.reset();
+ }
+
+ if(pOutState != 0) {
+ *pOutState = state;
+ }
+
+ return pCommand;
+}
+
+bool AMCPProtocolStrategy::QueueCommand(AMCPCommandPtr pCommand) {
+ if(pCommand->NeedChannel()) {
+ unsigned int channelIndex = pCommand->GetChannelIndex() + 1;
+ if(commandQueues_.size() > channelIndex) {
+ commandQueues_[channelIndex]->AddCommand(pCommand);
+ }
+ else
+ return false;
+ }
+ else {
+ commandQueues_[0]->AddCommand(pCommand);
+ }
+ return true;
+}
+
+AMCPCommandPtr AMCPProtocolStrategy::CommandFactory(const std::wstring& str)
+{
+ std::wstring s = str;
+ transform(s.begin(), s.end(), s.begin(), toupper);
+
+ if (s == TEXT("MIXER")) return std::make_shared<MixerCommand>();
+ else if(s == TEXT("DIAG")) return std::make_shared<DiagnosticsCommand>();
+ else if(s == TEXT("CHANNEL_GRID")) return std::make_shared<ChannelGridCommand>();
+ else if(s == TEXT("CALL")) return std::make_shared<CallCommand>();
+ else if(s == TEXT("SWAP")) return std::make_shared<SwapCommand>();
+ else if(s == TEXT("LOAD")) return std::make_shared<LoadCommand>();
+ else if(s == TEXT("LOADBG")) return std::make_shared<LoadbgCommand>();
+ else if(s == TEXT("ADD")) return std::make_shared<AddCommand>();
+ else if(s == TEXT("REMOVE")) return std::make_shared<RemoveCommand>();
+ else if(s == TEXT("PAUSE")) return std::make_shared<PauseCommand>();
+ else if(s == TEXT("PLAY")) return std::make_shared<PlayCommand>();
+ else if(s == TEXT("STOP")) return std::make_shared<StopCommand>();
+ else if(s == TEXT("CLEAR")) return std::make_shared<ClearCommand>();
+ else if(s == TEXT("PRINT")) return std::make_shared<PrintCommand>();
+ else if(s == TEXT("LOG")) return std::make_shared<LogCommand>();
+ else if(s == TEXT("CG")) return std::make_shared<CGCommand>();
+ else if(s == TEXT("DATA")) return std::make_shared<DataCommand>();
+ else if(s == TEXT("CINF")) return std::make_shared<CinfCommand>();
+ else if(s == TEXT("INFO")) return std::make_shared<InfoCommand>(channels_);
+ else if(s == TEXT("CLS")) return std::make_shared<ClsCommand>();
+ else if(s == TEXT("TLS")) return std::make_shared<TlsCommand>();
+ else if(s == TEXT("VERSION")) return std::make_shared<VersionCommand>();
+ else if(s == TEXT("BYE")) return std::make_shared<ByeCommand>();
+ else if(s == TEXT("SET")) return std::make_shared<SetCommand>();
+ //else if(s == TEXT("MONITOR"))
+ //{
+ // result = AMCPCommandPtr(new MonitorCommand());
+ //}
+ //else if(s == TEXT("KILL"))
+ //{
+ // result = AMCPCommandPtr(new KillCommand());
+ //}
+ return nullptr;
+}
+
+std::size_t AMCPProtocolStrategy::TokenizeMessage(const std::wstring& message, std::vector<std::wstring>* pTokenVector)
+{
+ //split on whitespace but keep strings within quotationmarks
+ //treat \ as the start of an escape-sequence: the following char will indicate what to actually put in the string
+
+ std::wstring currentToken;
+
+ char inQuote = 0;
+ bool getSpecialCode = false;
+
+ for(unsigned int charIndex=0; charIndex<message.size(); ++charIndex)
+ {
+ if(getSpecialCode)
+ {
+ //insert code-handling here
+ switch(message[charIndex])
+ {
+ case TEXT('\\'):
+ currentToken += TEXT("\\");
+ break;
+ case TEXT('\"'):
+ currentToken += TEXT("\"");
+ break;
+ case TEXT('n'):
+ currentToken += TEXT("\n");
+ break;
+ default:
+ break;
+ };
+ getSpecialCode = false;
+ continue;
+ }
+
+ if(message[charIndex]==TEXT('\\'))
+ {
+ getSpecialCode = true;
+ continue;
+ }
+
+ if(message[charIndex]==' ' && inQuote==false)
+ {
+ if(currentToken.size()>0)
+ {
+ pTokenVector->push_back(currentToken);
+ currentToken.clear();
+ }
+ continue;
+ }
+
+ if(message[charIndex]==TEXT('\"'))
+ {
+ inQuote ^= 1;
+
+ if(currentToken.size()>0)
+ {
+ pTokenVector->push_back(currentToken);
+ currentToken.clear();
+ }
+ continue;
+ }
+
+ currentToken += message[charIndex];
+ }
+
+ if(currentToken.size()>0)
+ {
+ pTokenVector->push_back(currentToken);
+ currentToken.clear();
+ }
+
+ return pTokenVector->size();
+}
+
+} //namespace amcp
}} //namespace caspar
\ No newline at end of file