├── .gitattributes ├── .gitignore ├── IRCBot.cpp ├── IRCBot.h ├── IRCMsgThread.cpp ├── IRCMsgThread.h ├── LICENSE ├── Log.cpp ├── Log.h ├── NicoCommentPlugin.cpp ├── NicoCommentPlugin.h ├── NicoCommentPlugin.rc ├── NicoCommentPlugin.sln ├── NicoCommentPlugin.suo ├── NicoCommentPlugin.vcxproj ├── NicoCommentPlugin.vcxproj.filters ├── NicoCommentPlugin.vcxproj.user ├── README.md ├── README_zh-tw.md ├── SimpleSocket.cpp ├── SimpleSocket.h ├── SimpleThread.h ├── constants.h ├── locale ├── en.txt ├── ja.txt └── tw.txt ├── resource.h ├── wstringfunc.cpp └── wstringfunc.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must ends with two \r. 29 | Icon 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | -------------------------------------------------------------------------------- /IRCBot.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "constants.h" 23 | #include "Log.h" 24 | #include "wstringfunc.h" 25 | #include "SimpleSocket.h" 26 | #include "SimpleThread.h" 27 | #include "IRCMsgThread.h" 28 | #include "IRCBot.h" 29 | 30 | 31 | 32 | /******************** 33 | * BASIC IRC Actions * 34 | ********************/ 35 | 36 | bool IRCBot::connectTask(std::wstring server, std::wstring port, std::wstring nickname, std::wstring login, std::wstring password){ //true for connect 37 | // Procedure for Connecting to the IRC server. 38 | // return false for default, true only if successfully connected. 39 | iStatus=BOT_CONNECTING; 40 | //int iResult; 41 | if ( TheSocket->InitializeSocket(server,port) ) // Initialize Winsock //0 for success, 1 for error 42 | onDebugMsg(L"IRCBot.connectTask: Error in socket initialization"); 43 | 44 | if ( TheSocket->ConnectSocket() ) // Connect to server //0 for success, 1 for error 45 | onDebugMsg(L"IRCBot.connectTask: Error in establishing socket connection"); 46 | 47 | else { //Successfully Connected 48 | wchar_t buffer[256]=L""; 49 | swprintf_s(buffer,256,L"Connected to %ls(%ls):%ls",server.c_str(),TheSocket->SocketIP().c_str(),port.c_str()); 50 | onSysMsg(L"%ls",buffer); 51 | 52 | // Log on to the server, must sent before creating thread 53 | std::wstring msg=L""; 54 | if(password!=L""){ 55 | msg+=L"PASS " + password + L"\r\n"; 56 | log(L"PASS ******", LogType(SEND)); 57 | } 58 | if(nickname!=(L"")){ 59 | msg+=L"NICK " + nickname + L"\r\n"; 60 | log(L"NICK " + nickname, LogType(SEND)); 61 | } 62 | if(login!=(L"")){ 63 | msg+=L"USER " + login + L"r\n"; 64 | log(L"USER " + login, LogType(SEND)); 65 | } 66 | std::string narrowmsg = to_utf8(msg); 67 | TheSocket->send_data((char*)narrowmsg.c_str(),(unsigned int)narrowmsg.length()); //Sending Message without queue. 68 | loginSuccessful = false; //Not known yet; Initialize for further checking. 69 | 70 | //setup input and output thread 71 | threadRunning = true; 72 | ircmsgThread->iStatus=IRC_CLOSED; 73 | ircmsgThread->StartThread(); 74 | return true; //True for connect 75 | } 76 | return false; 77 | } 78 | 79 | void IRCBot::IRC_join(std::wstring channel) { ircmsgThread->sendRaw(L"JOIN "+channel); } 80 | void IRCBot::IRC_chat(std::wstring channel, std::wstring message) { ircmsgThread->sendRaw(L"PRIVMSG " + channel + L" :" + message); } 81 | 82 | IRCBot::IRCBot(){ 83 | iStatus=BOT_CLOSED; 84 | threadRunning = false; //not for reconnect Task 85 | loginSuccessful = false; 86 | 87 | TheSocket = new SimpleSocket; 88 | 89 | ircmsgThread = new IRCMsgThread; 90 | ircmsgThread->SetIRCBot(this); 91 | ircmsgThread->SetSocket(TheSocket); 92 | ircmsgThread->iStatus=IRC_CLOSED; 93 | //Timer Settings; 94 | // aliveCheckTask = new SimpleTimer ; 95 | // aliveCheckTask->TickFunc=std::bind(&IRCBot::AliveCheckTask,this); 96 | // aliveCheckTask->SetTickTime(5000); 97 | 98 | // reconnectTask = new SimpleTimer ; 99 | // reconnectTask->TickFunc=std::bind(&IRCBot::ReconnectTask,this); 100 | // reconnectTask->SetTickTime(5000); 101 | 102 | lastIRCServer = L""; 103 | lastIRCPort = L""; 104 | lastIRCServerPass = L""; 105 | lastIRCNickname = L""; 106 | lastIRCLogin = L""; 107 | lastIRCChannel = L""; 108 | } 109 | 110 | IRCBot::~IRCBot(){ 111 | close(); 112 | delete ircmsgThread; 113 | delete TheSocket ; 114 | } 115 | 116 | IRCBotStatus IRCBot::AliveCheckTask() { 117 | switch (ircmsgThread->iStatus) { 118 | case IRC_DISCONNECTED: //IRCMsgThread: Disconnect Unexpectly 119 | onAccidentDisconnection(); 120 | break; 121 | case IRC_WRONGLOGIN: //IRCMsgThread: Wrong Login Information 122 | onLoginFailed(); 123 | break; 124 | case IRC_CLOSED: //StopThread 125 | break; 126 | case IRC_NORMAL: //Still Running 127 | ircmsgThread->sendRaw(L"PING"); 128 | break; 129 | } 130 | if(iStatus==BOT_CONNECTED) LoginCheckTask(); 131 | return iStatus; 132 | } 133 | 134 | void IRCBot::LoginCheckTask() { 135 | if(!loginSuccessful){ 136 | onSysMsg(L"Incorrect login information"); 137 | onLoginFailed(); 138 | } 139 | } 140 | 141 | bool IRCBot::isConnected(){ 142 | if(TheSocket->isSocketInvalid()) return false; 143 | else if( TheSocket->isConnected() && ircmsgThread->iStatus==IRC_NORMAL ) return true; 144 | else return false; 145 | } 146 | 147 | void IRCBot::reconnect(){ 148 | bool bConnect=connectTask(lastIRCServer,lastIRCPort,lastIRCNickname,lastIRCLogin,lastIRCServerPass); 149 | if(bConnect) { //success 150 | onConnectSuccess(); 151 | } else { 152 | onSysMsg(L"Reconnect Failed; wait for next Tick"); 153 | iStatus=BOT_NEEDRECONNECT; 154 | } 155 | } 156 | 157 | void IRCBot::connect(std::wstring server, std::wstring port, std::wstring nickname, std::wstring login, std::wstring password, std::wstring channel){ 158 | if(iStatus!=BOT_CLOSED) close(); 159 | lastIRCServer = server; 160 | lastIRCPort = port; 161 | lastIRCServerPass = password; 162 | lastIRCNickname = nickname; 163 | lastIRCLogin = login; 164 | lastIRCChannel = channel; 165 | iStatus=BOT_NEEDRECONNECT; //reconnect(); 166 | } 167 | 168 | void IRCBot::close(){ 169 | iStatus=BOT_CLOSING; 170 | if(threadRunning) { 171 | threadRunning = false; 172 | //close threads 173 | if(ircmsgThread->iStatus==IRC_NORMAL) { 174 | onSysMsg(TEXT("IRCBot.close: Closing IRCBot-ircmsgThread")); 175 | ircmsgThread->sendRaw(L"QUIT :Leaving"); 176 | ircmsgThread->interruptSignal(); 177 | Sleep(100); 178 | TheSocket->CloseSocket(); 179 | ircmsgThread->StopThread(); 180 | } 181 | onSysMsg(L"IRCBot.close: All threads closed, IRCBot Stopped"); 182 | } 183 | iStatus=BOT_CLOSED; 184 | } 185 | 186 | 187 | void IRCBot::onLoginFailed(){ //Call from LoginCheckTask 188 | onSysMsg(L"Failed to Login"); 189 | close(); 190 | } 191 | 192 | void IRCBot::onLoginSuccess(){ //Call from IRCMsgThread 193 | onSysMsg(L"Login Successfully"); 194 | ircmsgThread->sendRaw(L"CAP REQ :twitch.tv/tags"); 195 | IRC_join(lastIRCChannel); 196 | } 197 | 198 | void IRCBot::onAccidentDisconnection(){ //the connection is closed by accident 199 | onSysMsg(L"Accidentally disconnected from server: Reconnecting..."); 200 | close(); 201 | iStatus=BOT_NEEDRECONNECT; 202 | } 203 | 204 | void IRCBot::onConnectSuccess(){ 205 | onSysMsg(L"Connect Tasks Successfully Finished"); 206 | //Sleep(500); 207 | //setup disconnection checking timer 208 | //if(ircmsgThread->iStatus!=IRC_NORMAL) { 209 | //ircmsgThread will drop very quickly if login failed 210 | //if connection dropped, the bot status should still be CONNECTING. 211 | // AliveCheckTask(); //Determine the message 212 | //} 213 | //else{ //ircmsgThread is running 214 | Sleep(500); 215 | iStatus=BOT_CONNECTED; 216 | //LoginCheckTask(); //Check for once 217 | //} 218 | } 219 | 220 | bool IRCBot::receiveMsg(TircMsg &ircmsg) {return ircmsgThread->receiveMsg(ircmsg);} 221 | bool IRCBot::QueueEmpty() {return ircmsgThread->QueueEmpty();} 222 | IRCBotStatus IRCBot::CheckIRCBotStatus(){ return iStatus;} -------------------------------------------------------------------------------- /IRCBot.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | enum IRCBotStatus {BOT_CLOSED,BOT_CONNECTING,BOT_CONNECTED,BOT_NEEDRECONNECT,BOT_CLOSING}; 20 | class IRCBot { 21 | friend class IRCMsgThread; 22 | IRCBotStatus iStatus; 23 | 24 | SimpleSocket* TheSocket; 25 | 26 | bool threadRunning; //not for reconnect Task 27 | bool loginSuccessful; 28 | 29 | IRCMsgThread* ircmsgThread; 30 | 31 | // SimpleTimer* aliveCheckTask; 32 | // SimpleTimer* reconnectTask; 33 | 34 | //latest connect info 35 | std::wstring lastIRCServer; 36 | std::wstring lastIRCPort; 37 | std::wstring lastIRCServerPass; 38 | std::wstring lastIRCNickname; 39 | std::wstring lastIRCLogin; 40 | std::wstring lastIRCChannel; 41 | 42 | /******************** 43 | * BASIC IRC Actions * 44 | ********************/ 45 | 46 | bool connectTask(std::wstring server, std::wstring port, std::wstring nickname, std::wstring login, std::wstring password); //true for connect 47 | 48 | public: 49 | IRCBot(); 50 | ~IRCBot(); 51 | void IRC_join(std::wstring channel); 52 | void IRC_chat(std::wstring channel, std::wstring message); 53 | IRCBotStatus AliveCheckTask(); 54 | void ReconnectTask(); 55 | void LoginCheckTask(); 56 | bool isConnected(); 57 | void reconnect(); 58 | void connect(std::wstring server, std::wstring port, std::wstring nickname, std::wstring login, std::wstring password, std::wstring channel); 59 | void close(); 60 | void onLoginFailed(); 61 | void onLoginSuccess(); 62 | void onAccidentDisconnection(); //the connection is closed by accident 63 | void onConnectSuccess(); 64 | bool receiveMsg(TircMsg &ircmsg); 65 | bool QueueEmpty(); 66 | IRCBotStatus CheckIRCBotStatus(); 67 | }; 68 | -------------------------------------------------------------------------------- /IRCMsgThread.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "constants.h" 24 | #include "Log.h" 25 | #include "wstringfunc.h" 26 | 27 | #include "SimpleSocket.h" 28 | #include "SimpleThread.h" 29 | #include "IRCMsgThread.h" 30 | #include "IRCBot.h" 31 | 32 | DWORD IRCMsgThread::Run() { 33 | onSysMsg(L"IRCMsgThread Start"); 34 | iStatus=IRC_NORMAL; 35 | IRCMsgQueue.clear(); 36 | unsigned int bufferlen; 37 | std::string recvstring,msg,umsg; //TwitchIRC use MultiByte 38 | while(!bKillThread ) { 39 | //Receive First 40 | bufferlen=0; 41 | memset(buffer, 0, DEFAULT_BUFLEN); 42 | if(TheSocketPtr->recv_data((char*)buffer, bufferlen)) { //Disconnected 43 | iStatus=IRC_DISCONNECTED; 44 | break; 45 | } 46 | recvstring=std::string(buffer,bufferlen); 47 | //onDebugMsg(L"%ls",from_utf8(recvstring).c_str()); 48 | if(recvstring.compare(":tmi.twitch.tv NOTICE * :Login unsuccessful\r\n")==0 || //Twitch 49 | recvstring.compare(":tmi.twitch.tv NOTICE * :Error encountered while attempting login\r\n")==0 ){ //Justin 50 | Sleep(100); //Wait for logging 51 | onSysMsg(L"Login unsuccessful"); 52 | iStatus=IRC_WRONGLOGIN; 53 | break; 54 | }//else if(recvstring.compare(":append!append@append.tmi.twitch.tv PRIVMSG #append :break\r\n")==0){TheSocketPtr->CloseSocket();} 55 | //Basic parsing: make it into a string, check for PING, if not-send for further parsing. 56 | std::stringstream recvstr(recvstring); 57 | while( recvstr.tellg() < bufferlen ) { 58 | std::getline(recvstr,msg,'\n'); 59 | //Old Style 60 | //:daziesel92!daziesel92@daziesel92.tmi.twitch.tv PRIVMSG #ptken :what song is this ? 61 | //PING LAG1967655232 62 | //int colon_pos = (int)msg.find(":"); 63 | //if (msg[0] != ':' && colon_pos > 0) msg=msg.substr(colon_pos); //if an irc message begins with " ", then trim the leading spaces 64 | // 65 | //New Style 66 | // @color=#FF4500;display-name=Gm470520;emotes=;mod=0;room-id=47281189;subscriber=1;turbo=0;user-id=24255667;user-type= :gm470520!gm470520@gm470520.tmi.twitch.tv PRIVMSG #tetristhegrandmaster3 :這片到底在沖三小w 67 | int at_pos = (int)msg.find("@"); 68 | if (msg[0] != '@' && at_pos > 0) msg=msg.substr(at_pos); //if an irc message begins with " ", then trim the leading spaces 69 | if (msg[msg.length()-1]=='\r') msg=msg.substr(0,msg.length()-1); //trim the CR: getline only trim the LF 70 | log(from_utf8(msg), LogType(RECEIVE)); //Although log file is in UTF8....using vswprintf 71 | //Ping Check 72 | umsg=msg.substr(0,5); 73 | ToUpperString(umsg); 74 | if (!umsg.compare("PING ")) sendRaw( L"PONG " + from_utf8(msg.substr(5)) ); // respond to PINGs //Reply Immediately 75 | //Then parse the message 76 | else parseMessage(from_utf8(msg)); 77 | } 78 | Sleep(100); 79 | } 80 | //Determine the reason to break the loop. 81 | if(!bKillThread) { 82 | if(iStatus==IRC_DISCONNECTED) onSysMsg(L"IRCMsgThread: Disconnect Unexpectedly"); 83 | if(iStatus==IRC_WRONGLOGIN) onSysMsg(L"IRCMsgThread: Wrong Login Information"); 84 | } 85 | else iStatus=IRC_CLOSED; 86 | //onDebugMsg(L"[Last Buffer]%ls",from_utf8(recvstring).c_str()); 87 | onSysMsg(L"IRCMsgThread Close"); 88 | thread=0; 89 | return 0; 90 | } 91 | 92 | std::wstring IRCMsgThread::getUsername(std::wstring sender,std::wstring backup){ 93 | //parse twitch username from twitch chats 94 | //Old Style: ex: user!user@user.tmi.twitch.tv -> user 95 | /*if(sender.find(L"!")!=std::wstring::npos){ 96 | return sender.substr(0,sender.find(L"!")); 97 | } else return sender;*/ 98 | //New Style: ex: color=#1E90FF;display-name=pikads;emotes=;mod=1;room-id=24805060;subscriber=0;turbo=0;user-id=25141849;user-type=mod 99 | //@badges=;color=#F6BFB5;display-name=F6BFB5;emotes=;mod=0;room-id=47281189;subscriber=1;turbo=0;user-id=23745737;user-type= :f6bfb5!f6bfb5@f6bfb5.tmi.twitch.tv PRIVMSG #tetristhegrandmaster3 :我以榮耀作戰 100 | 101 | //if the number of tokens is wrong, return Old Style username 102 | std::vector tag_parse = split(sender,L';',9); 103 | if(tag_parse.size()<9) return getBackupUsername(backup); 104 | 105 | std::wstring name; 106 | for(int i=0;i name_parse = split(tag_parse[1],L'=',2); 108 | //if(name_parse.size()<2) name = getBackupUsername(backup); 109 | 110 | std::vector name_parse = split(tag_parse[i],L'=',2); 111 | if(name_parse[0].compare(L"display-name")==0) { 112 | if(name_parse.size()<2) name = getBackupUsername(backup); //old-style 113 | else { //New style username 114 | name=name_parse[1]; //return name when displayname has something 115 | } 116 | } 117 | if(name_parse[0].compare(L"color")==0) { 118 | //set color when color has something 119 | //color: SetUserColor(std::wstring User,std::wstring Color), need lowercase 120 | //std::vector color_parse = split(tag_parse[0],L'=',2); 121 | //if(color_parse.size()==2) SetUserColor(ToLowerString(lower_name),ToLowerString(color_parse[1])); 122 | if(name_parse.size()==2) { 123 | std::wstring lower_name=getBackupUsername(backup); 124 | SetUserColor(ToLowerString(lower_name),ToLowerString(name_parse[1])); 125 | }; 126 | } 127 | } 128 | 129 | return name; 130 | } 131 | 132 | std::wstring IRCMsgThread::getBackupUsername(std::wstring backup){ 133 | if(backup.find(L"!")!=std::wstring::npos) {//Old Style 134 | onSysMsg(L"Old Style Name %ls",backup.substr(0,backup.find(L"!"))); 135 | return backup.substr(0,backup.find(L"!")); 136 | } 137 | else return backup; 138 | } 139 | 140 | void IRCMsgThread::onChatMsg(std::wstring channel, std::wstring nickname, bool isOp, std::wstring message){ 141 | //onSysMsg(L"[CHAT] %ls: %ls\n", nickname.c_str(), message.c_str()); 142 | std::wstring lower_name=nickname; 143 | ToLowerString(lower_name); 144 | if(lower_name.compare(L"nightbot")!=0&&lower_name.compare(L"moobot")!=0&&lower_name.compare(L"jtv")!=0) { 145 | TircMsg ircMsg; 146 | ircMsg.user=nickname; 147 | ircMsg.msg=message; 148 | ircMsg.usercolor=GetUserColor(lower_name); 149 | IRCMsgQueue.push(ircMsg); 150 | } 151 | } 152 | 153 | void IRCMsgThread::onChatAction(std::wstring channel, std::wstring nickname, std::wstring action){ 154 | /*onIRCMsg(L"[ACTION] %ls %ls\n", nickname.c_str(), action.c_str());*/ 155 | } 156 | 157 | void IRCMsgThread::onPrivateMsg(std::wstring nickname, std::wstring message){ //never used that in new style 158 | //onIRCMsg(L"[PRIVATE] %ls: %ls\n", nickname.c_str(), message.c_str()); 159 | //Parsing PRIVATEMSG 160 | //if(!nickname.compare(L"jtv")){ 161 | /*if(!message.substr(0,10).compare(L"USERCOLOR ")){ 162 | std::vector parse = split(message,L' ',3); 163 | SetUserColor(parse[1],ToLowerString(parse[2])); 164 | //onDebugMsg(L"[COLOR] %ls -> %ls\n", parse[1].c_str(), parse[2].c_str()); 165 | }*/ 166 | /*else if(!message.substr(0,10).compare(L"CLEARCHAT ")){ //message.matches("^CLEARCHAT .*") 167 | vector parse = split(message,L' ',2); 168 | onDebugMsg(L"[BAN OR TIMEOUT] %s has been banned or timeoutted\n",parse[1].c_str()); 169 | }else if(!message.compare(L"CLEARCHAT")){ 170 | onDebugMsg(L"[CLEARCHAT]\n"); 171 | }else if(!message.substr(0,30).compare(L"You are banned from talking in")){ 172 | onDebugMsg(L"[OOPS] %ls\n", message.c_str()); 173 | }*/ 174 | //} 175 | } 176 | 177 | void IRCMsgThread::parseMessage(std::wstring message){ 178 | //Old Style 179 | //:daziesel92!daziesel92@daziesel92.tmi.twitch.tv PRIVMSG #ptken :what song is this ? 180 | // 181 | //New Style 182 | //@color=#FF4500;display-name=Gm470520;emotes=;mod=0;room-id=47281189;subscriber=1;turbo=0;user-id=24255667;user-type= :gm470520!gm470520@gm470520.tmi.twitch.tv PRIVMSG #tetristhegrandmaster3 :這片到底在沖三小w 183 | //@color=#1E90FF;display-name=pikads;emotes=;mod=1;room-id=24805060;subscriber=0;turbo=0;user-id=25141849;user-type=mod :pikads!pikads@pikads.tmi.twitch.tv PRIVMSG #ptken :诶 184 | std::vector firstParse = split(message,L' ',4); 185 | if(firstParse.size()<3) return; //Do Nothing 186 | //parse[0]=@color=#1E90FF;display-name=pikads;emotes=;mod=1;room-id=24805060;subscriber=0;turbo=0;user-id=25141849;user-type=mod 187 | //parse[1]=:pikads!pikads@pikads.tmi.twitch.tv 188 | //parse[2]=PRIVMSG 189 | //parse[3]=#ptken 190 | //parse[4]=:诶 191 | if(!firstParse[2].compare(L"PRIVMSG")){ 192 | std::vector parse = split(message,L' ',5); 193 | if(parse.size() < 5) return; //discard this incomplete message 194 | /* 195 | * parse[1].substr(1): sender 196 | * parse[3]: target channel/user 197 | * parse[4].subString(1): message 198 | */ 199 | //std::wstring sender = getUsername(parse[1].substr(1)); Old Style 200 | std::wstring sender = getUsername(parse[0].substr(1),parse[1].substr(1)); 201 | std::wstring target = parse[3]; //maybe need .c_str(), but never used in the plugin 202 | std::wstring content = parse[4].substr(1); 203 | if(parse[3][0]==L'#'){ 204 | if(!content.substr(0,8).compare(L"ACTION ")){ 205 | //onChatAction(target, sender, replaceFirst(content.substr(8),L"", L"").c_str()); 206 | onChatAction(target, sender, content); 207 | }else{ 208 | onChatMsg(target, sender, false, content); 209 | } 210 | }else{ 211 | onPrivateMsg(sender, content); //not sure about the number of tokens, but for a plugin, never mind. 212 | } 213 | 214 | }else if(!firstParse[1].compare(L"JOIN")){ //JOIN: LOG to [SYS] 215 | std::vector parse = split(message,L' ',3); 216 | if(parse.size() < 3) return; //discard this incomplete message 217 | if(!(parse[2].compare(ToLowerString(ircbotPtr->lastIRCChannel)))){ //user succesffully joins a channel 218 | // onSysMsg(L"Joined %ls",parse[2]); 219 | } 220 | /* else { //other users join the channel 221 | onIRCMsg(L"%ls Joined %ls\n",getUsername(parse[0].substr(1)).c_str(),parse[2].c_str()); 222 | }*/ 223 | 224 | }else if(!firstParse[1].compare(L"001")){ //login successful 225 | ircbotPtr->loginSuccessful = true; 226 | ircbotPtr->onLoginSuccess(); 227 | } 228 | } 229 | 230 | void IRCMsgThread::SetUserColor(std::wstring User,std::wstring Color){ //already lowercase 231 | //std::map UserColorMap 232 | unsigned int iColor; 233 | std::wregex color_pattern(L"^#[0-9a-fA-F]{6}$"); 234 | //std::wsmatch base_match; 235 | if(std::regex_match(Color, color_pattern)){ 236 | int iErrno=swscanf_s(Color.substr(1).c_str(),L"%x",&iColor); 237 | } 238 | else { 239 | iColor=1<<24; 240 | if(Color.compare(L"red")==0) iColor=0x00FF0000; 241 | else if(Color.compare(L"blue")==0) iColor=0x000000FF; 242 | else if(Color.compare(L"green")==0) iColor=0x00008000; 243 | else if(Color.compare(L"firebrick")==0) iColor=0x00B22222; 244 | else if(Color.compare(L"coral")==0) iColor=0x00FF7F50; 245 | else if(Color.compare(L"yellowgreen")==0) iColor=0x009ACD32; 246 | else if(Color.compare(L"orangered")==0) iColor=0x00FF4500; 247 | else if(Color.compare(L"seagreen")==0) iColor=0x002E8B57; 248 | else if(Color.compare(L"goldenrod")==0) iColor=0x00D2691E; 249 | else if(Color.compare(L"cadetblue")==0) iColor=0x005F9EA0; 250 | else if(Color.compare(L"dodgerblue")==0) iColor=0x001E90FF; 251 | else if(Color.compare(L"hotpink")==0) iColor=0x00FF69B4; 252 | else if(Color.compare(L"blueviolet")==0) iColor=0x008A2BE2; 253 | else if(Color.compare(L"springgreen")==0) iColor=0x0000FF7F; 254 | else if(Color.compare(L"black")==0) iColor=0x00000000; 255 | else if(Color.compare(L"gray")==0) iColor=0x00808080; 256 | else if(Color.compare(L"darkred")==0) iColor=0x008B0000; 257 | else if(Color.compare(L"midnightblue")==0) iColor=0x00191970; 258 | else if(Color.compare(L"deeppink")==0) iColor=0x00FF1493; 259 | } 260 | //onDebugMsg(L"SetUserColor: %ls, %ls, 0X%08X\n",User.c_str(),Color.c_str(),iColor); 261 | std::pair::iterator,bool> ret=UserColorMap.insert( std::pair(User,iColor|0xFF000000) ); 262 | if (ret.second==false) ret.first->second=iColor|0xFF000000; 263 | } 264 | 265 | unsigned int IRCMsgThread::GetUserColor(std::wstring User){ 266 | std::map::iterator it=UserColorMap.find(User); 267 | if(it==UserColorMap.end()) { 268 | //onDebugMsg(L"GetUserColor: %ls, NOTFOUND\n",User.c_str()); 269 | return (unsigned int)0; 270 | } 271 | else { 272 | //onDebugMsg(L"GetUserColor: %ls, 0X%08X\n",User.c_str(),it->second); 273 | return it->second; 274 | } 275 | } 276 | 277 | void IRCMsgThread::sendRaw(std::wstring message){ 278 | std::string send_msg=to_utf8(message+L"\r\n"); 279 | TheSocketPtr->send_data((char*)send_msg.c_str(),(unsigned int)send_msg.length()); 280 | log(message, LogType(SEND)); 281 | } 282 | 283 | bool IRCMsgThread::receiveMsg(TircMsg &ircmsg) {return IRCMsgQueue.try_pop(ircmsg);} 284 | void IRCMsgThread::SetSocket(SimpleSocket* SocketPtr) {TheSocketPtr=SocketPtr;} 285 | void IRCMsgThread::SetIRCBot(IRCBot* Ptr){ircbotPtr=Ptr;}; 286 | bool IRCMsgThread::QueueEmpty() {return IRCMsgQueue.empty();} 287 | 288 | -------------------------------------------------------------------------------- /IRCMsgThread.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #pragma once 23 | enum IRCMsgThreadStatus {IRC_NORMAL,IRC_DISCONNECTED,IRC_WRONGLOGIN,IRC_CLOSED}; 24 | class IRCMsgThread : public SimpleThread { // Dealing with Received Message 25 | friend class IRCBot; 26 | IRCBot *ircbotPtr; 27 | std::wstring recv_msg; 28 | Concurrency::concurrent_queue IRCMsgQueue; 29 | std::map UserColorMap; 30 | virtual DWORD Run(); 31 | std::wstring getUsername(std::wstring sender,std::wstring backup); 32 | std::wstring getBackupUsername(std::wstring backup); 33 | void onChatMsg(std::wstring channel, std::wstring nickname, bool isOp, std::wstring message); 34 | void onChatAction(std::wstring channel, std::wstring nickname, std::wstring action); 35 | void onPrivateMsg(std::wstring nickname, std::wstring message); 36 | void parseMessage(std::wstring message); 37 | void SetUserColor(std::wstring User,std::wstring Color); 38 | unsigned int GetUserColor(std::wstring User); 39 | char buffer[DEFAULT_BUFLEN]; 40 | SimpleSocket* TheSocketPtr; 41 | public: 42 | IRCMsgThreadStatus iStatus; 43 | void sendRaw(std::wstring message); 44 | void SetSocket(SimpleSocket* SocketPtr); 45 | void SetIRCBot(IRCBot* Ptr); 46 | bool receiveMsg(TircMsg &ircmsg); 47 | bool QueueEmpty(); 48 | inline void interruptSignal(){bKillThread=true;} 49 | 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /Log.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include "OBSApi.h" 20 | #include 21 | #include 22 | #include "wstringfunc.h" 23 | #include "Log.h" 24 | 25 | /**************************** 26 | * Default Logging Functions * 27 | ****************************/ 28 | void onLog( const wchar_t * format, ... ){ 29 | /* wchar_t buffer[512]; 30 | va_list args; 31 | va_start ( args, format ); 32 | vswprintf_s ( buffer, 512, format, args ); 33 | va_end ( args ); 34 | Log(TEXT("[NICO][LOG]%ls\n"),buffer);*/ 35 | /* FILE* fp; 36 | errno_t err=_wfopen_s(&fp, LogFileName,L"a, ccs=UTF-8"); 37 | if(err==0){ 38 | fputws ( buffer, fp ); 39 | fclose(fp); 40 | }*/ 41 | 42 | } 43 | 44 | void onSysMsg( const wchar_t * format, ... ){ 45 | wchar_t buffer[512]; 46 | va_list args; 47 | va_start ( args, format ); 48 | vswprintf_s ( buffer, 512 , format, args ); 49 | va_end ( args ); 50 | Log(TEXT("[NICO][SYS]%ls"),buffer); 51 | /* FILE* fp; 52 | errno_t err=_wfopen_s(&fp, SysFileName,L"a, ccs=UTF-8"); 53 | if(err==0){ 54 | fputws ( buffer, fp ); 55 | fclose(fp); 56 | }*/ 57 | 58 | } 59 | 60 | void onDebugMsg( const wchar_t * format, ... ){ 61 | wchar_t buffer[512]; 62 | va_list args; 63 | va_start ( args, format ); 64 | vswprintf_s ( buffer, 512 , format, args ); 65 | va_end ( args ); 66 | Log(TEXT("[NICO][DEBUG]%ls"),buffer); 67 | /* FILE* fp; 68 | errno_t err=_wfopen_s(&fp, DebugFileName,L"a, ccs=UTF-8"); 69 | if(err==0){ 70 | fputws ( buffer, fp ); 71 | fclose(fp); 72 | }*/ 73 | 74 | } 75 | 76 | void onIRCMsg( const wchar_t * format, ... ){ 77 | /* wchar_t buffer[512]; 78 | va_list args; 79 | va_start ( args, format ); 80 | vswprintf_s ( buffer, 512 , format, args ); 81 | Log(TEXT("[NICO][IRC]%ls\n"),buffer);*/ 82 | /* FILE* fp; 83 | errno_t err=_wfopen_s(&fp, IRCFileName,L"a, ccs=UTF-8"); 84 | if(err==0){ 85 | fputws ( buffer, fp ); 86 | fclose(fp); 87 | }*/ 88 | 89 | } 90 | 91 | 92 | void log(std::wstring msg, LogType type ){/* 93 | // initialization 94 | std::wstring result=L""; 95 | 96 | 97 | // add prefix >>> and <<< for directions 98 | if(type == SEND) result+=L">>> "; 99 | else if(type == RECEIVE) result+=L"<<< "; 100 | 101 | //delete CRLF symbol 102 | while(msg.find(L"\r\n")!= std::wstring::npos ) replaceFirst(msg,L"\r\n",L""); 103 | result += msg; 104 | int len=result.length(); 105 | //discard PING sent by client and PONG by server 106 | if(len>=8) 107 | if( result.substr(0,8).compare(L">>> PING") && result.substr(0,8).compare(L"<<< PONG") ) 108 | onLog(L"%ls\n",result.c_str()); //NEED onLog(wstring msg);*/ 109 | } 110 | 111 | -------------------------------------------------------------------------------- /Log.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #pragma once 22 | 23 | /*extern const wchar_t* LogFileName; 24 | extern const wchar_t* SysFileName; 25 | extern const wchar_t* DebugFileName; 26 | extern const wchar_t* IRCFileName;*/ 27 | 28 | /**************************** 29 | * Default Logging Functions * 30 | ****************************/ 31 | 32 | void onLog( const wchar_t * format, ... ); 33 | void onSysMsg( const wchar_t * format, ... ); 34 | void onDebugMsg( const wchar_t * format, ... ); 35 | void onIRCMsg( const wchar_t * format, ... ); 36 | 37 | enum LogType{ SEND, RECEIVE }; 38 | void log(std::wstring msg, LogType type ); -------------------------------------------------------------------------------- /NicoCommentPlugin.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | 20 | //#include "Main.h" 21 | //#include "OBSApi.h" 22 | #include "NicoCommentPlugin.h" 23 | extern "C" __declspec(dllexport) bool LoadPlugin(); 24 | extern "C" __declspec(dllexport) void UnloadPlugin(); 25 | extern "C" __declspec(dllexport) CTSTR GetPluginName(); 26 | extern "C" __declspec(dllexport) CTSTR GetPluginDescription(); 27 | extern "C" __declspec(dllexport) void ConfigPlugin(HWND); 28 | 29 | LocaleStringLookup *pluginLocale = NULL; 30 | HINSTANCE hinstMain = NULL; 31 | 32 | ImageSource* STDCALL CreateTextSource(XElement *data) 33 | { 34 | if(!data) 35 | return NULL; 36 | 37 | return new NicoCommentPlugin(data); 38 | } 39 | 40 | int CALLBACK FontEnumProcThingy(ENUMLOGFONTEX *logicalData, NEWTEXTMETRICEX *physicalData, DWORD fontType, ConfigTextSourceInfo *configInfo) 41 | { 42 | if(fontType == TRUETYPE_FONTTYPE) //HomeWorld - GDI+ doesn't like anything other than truetype 43 | { 44 | configInfo->fontNames << logicalData->elfFullName; 45 | configInfo->fontFaces << logicalData->elfLogFont.lfFaceName; 46 | } 47 | 48 | return 1; 49 | } 50 | 51 | void DoCancelStuff(HWND hwnd) 52 | { 53 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 54 | ImageSource *source = API->GetSceneImageSource(configInfo->lpName); 55 | //XElement *data = configInfo->data; 56 | 57 | if(source) 58 | source->UpdateSettings(); 59 | } 60 | 61 | UINT FindFontFace(ConfigTextSourceInfo *configInfo, HWND hwndFontList, CTSTR lpFontFace) 62 | { 63 | UINT id = configInfo->fontFaces.FindValueIndexI(lpFontFace); 64 | if(id == INVALID) 65 | return INVALID; 66 | else 67 | { 68 | for(UINT i=0; ifontFaces.Num(); i++) 69 | { 70 | UINT targetID = (UINT)SendMessage(hwndFontList, CB_GETITEMDATA, i, 0); 71 | if(targetID == id) 72 | return i; 73 | } 74 | } 75 | 76 | return INVALID; 77 | } 78 | 79 | UINT FindFontName(ConfigTextSourceInfo *configInfo, HWND hwndFontList, CTSTR lpFontFace) 80 | { 81 | return configInfo->fontNames.FindValueIndexI(lpFontFace); 82 | } 83 | 84 | CTSTR GetFontFace(ConfigTextSourceInfo *configInfo, HWND hwndFontList) 85 | { 86 | UINT id = (UINT)SendMessage(hwndFontList, CB_GETCURSEL, 0, 0); 87 | if(id == CB_ERR) 88 | return NULL; 89 | 90 | UINT actualID = (UINT)SendMessage(hwndFontList, CB_GETITEMDATA, id, 0); 91 | return configInfo->fontFaces[actualID]; 92 | } 93 | 94 | INT_PTR CALLBACK ConfigureTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 95 | { 96 | static bool bInitializedDialog = false; 97 | 98 | switch(message) 99 | { 100 | case WM_INITDIALOG: 101 | { 102 | //Initialization 103 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)lParam; 104 | SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)configInfo); 105 | LocalizeWindow(hwnd, pluginLocale); 106 | XElement *data = configInfo->data; 107 | 108 | // Do Fonts stuff at first----------------------------------------- 109 | 110 | HDC hDCtest = GetDC(hwnd); 111 | LOGFONT lf; 112 | zero(&lf, sizeof(lf)); 113 | EnumFontFamiliesEx(hDCtest, &lf, (FONTENUMPROC)FontEnumProcThingy, (LPARAM)configInfo, 0); 114 | 115 | HWND hwndFonts = GetDlgItem(hwnd, IDC_FONT); 116 | HWND hwndNicknameFonts = GetDlgItem(hwnd, IDC_NICKNAMEFONT); 117 | for(UINT i=0; ifontNames.Num(); i++) 118 | { 119 | int id = (int)SendMessage(hwndFonts, CB_ADDSTRING, 0, (LPARAM)configInfo->fontNames[i].Array()); 120 | SendMessage(hwndFonts, CB_SETITEMDATA, id, (LPARAM)i); 121 | id = (int)SendMessage(hwndNicknameFonts, CB_ADDSTRING, 0, (LPARAM)configInfo->fontNames[i].Array()); 122 | SendMessage(hwndNicknameFonts, CB_SETITEMDATA, id, (LPARAM)i); 123 | } 124 | 125 | CTSTR lpFont = data->GetString(TEXT("font")); 126 | UINT id = FindFontFace(configInfo, hwndFonts, lpFont); 127 | if(id == INVALID) 128 | id = (UINT)SendMessage(hwndFonts, CB_FINDSTRINGEXACT, -1, (LPARAM)TEXT("Arial")); 129 | SendMessage(hwndFonts, CB_SETCURSEL, id, 0); 130 | 131 | CTSTR lpNicknameFont = data->GetString(TEXT("nicknameFont")); 132 | id = FindFontFace(configInfo, hwndNicknameFonts, lpNicknameFont); 133 | if(id == INVALID) 134 | id = (UINT)SendMessage(hwndNicknameFonts, CB_FINDSTRINGEXACT, -1, (LPARAM)TEXT("Arial")); 135 | SendMessage(hwndNicknameFonts, CB_SETCURSEL, id, 0); 136 | 137 | //Groupbox.Design----------------------------------------- 138 | UINT basewidth=0; 139 | UINT baseheight=0; 140 | OBSGetBaseSize(basewidth, baseheight); 141 | SendMessage(GetDlgItem(hwnd, IDC_EXTENTWIDTH), UDM_SETRANGE32, 32, 2048); 142 | SendMessage(GetDlgItem(hwnd, IDC_EXTENTWIDTH), UDM_SETPOS32, 0, data->GetInt(TEXT("extentWidth"), basewidth)); 143 | SendMessage(GetDlgItem(hwnd, IDC_EXTENTHEIGHT), UDM_SETRANGE32, 32, 2048); 144 | SendMessage(GetDlgItem(hwnd, IDC_EXTENTHEIGHT), UDM_SETPOS32, 0, data->GetInt(TEXT("extentHeight"), baseheight)); 145 | SendMessage(GetDlgItem(hwnd, IDC_NUMOFLINES), UDM_SETRANGE32, 3, 10); 146 | SendMessage(GetDlgItem(hwnd, IDC_NUMOFLINES), UDM_SETPOS32, 0, data->GetInt(TEXT("NumOfLines"), 5)); 147 | SendMessage(GetDlgItem(hwnd, IDC_SCROLLSPEED), UDM_SETRANGE32, 5, 100); //only positive 148 | SendMessage(GetDlgItem(hwnd, IDC_SCROLLSPEED), UDM_SETPOS32, 0, data->GetInt(TEXT("scrollSpeed"), 10)); 149 | CCSetColor(GetDlgItem(hwnd, IDC_BACKGROUNDCOLOR), data->GetInt(TEXT("backgroundColor"), 0xFF000000)); 150 | SendMessage(GetDlgItem(hwnd, IDC_BACKGROUNDOPACITY), UDM_SETRANGE32, 0, 100); 151 | SendMessage(GetDlgItem(hwnd, IDC_BACKGROUNDOPACITY), UDM_SETPOS32, 0, data->GetInt(TEXT("backgroundOpacity"), 0)); 152 | SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_SETCHECK, data->GetInt(TEXT("pointFiltering"), 0) != 0 ? BST_CHECKED : BST_UNCHECKED, 0); 153 | 154 | //Groupbox.Message----------------------------------------- 155 | SendMessage(GetDlgItem(hwnd, IDC_TEXTSIZE), UDM_SETRANGE32, 5, 2048); 156 | SendMessage(GetDlgItem(hwnd, IDC_TEXTSIZE), UDM_SETPOS32, 0, data->GetInt(TEXT("fontSize"), 48)); 157 | CCSetColor(GetDlgItem(hwnd, IDC_COLOR), data->GetInt(TEXT("color"), 0xFFFFFFFF)); 158 | SendMessage(GetDlgItem(hwnd, IDC_TEXTOPACITY), UDM_SETRANGE32, 0, 100); 159 | SendMessage(GetDlgItem(hwnd, IDC_TEXTOPACITY), UDM_SETPOS32, 0, data->GetInt(TEXT("textOpacity"), 100)); 160 | SendMessage(GetDlgItem(hwnd, IDC_BOLD), BM_SETCHECK, data->GetInt(TEXT("bold"), 1) ? BST_CHECKED : BST_UNCHECKED, 0); 161 | SendMessage(GetDlgItem(hwnd, IDC_ITALIC), BM_SETCHECK, data->GetInt(TEXT("italic"), 0) ? BST_CHECKED : BST_UNCHECKED, 0); 162 | SendMessage(GetDlgItem(hwnd, IDC_UNDERLINE), BM_SETCHECK, data->GetInt(TEXT("underline"), 0) ? BST_CHECKED : BST_UNCHECKED, 0); 163 | 164 | bool bCheckedOutline = data->GetInt(TEXT("useOutline"), 1) != 0; 165 | SendMessage(GetDlgItem(hwnd, IDC_USEOUTLINE), BM_SETCHECK, bCheckedOutline ? BST_CHECKED : BST_UNCHECKED, 0); 166 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS_EDIT), bCheckedOutline); 167 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS), bCheckedOutline); 168 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINECOLOR), bCheckedOutline); 169 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINEOPACITY_EDIT), bCheckedOutline); 170 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINEOPACITY), bCheckedOutline); 171 | 172 | SendMessage(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS), UDM_SETRANGE32, 1, 20); 173 | SendMessage(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS), UDM_SETPOS32, 0, data->GetInt(TEXT("outlineSize"), 5)); 174 | CCSetColor(GetDlgItem(hwnd, IDC_OUTLINECOLOR), data->GetInt(TEXT("outlineColor"), 0xFF000000)); 175 | SendMessage(GetDlgItem(hwnd, IDC_OUTLINEOPACITY), UDM_SETRANGE32, 0, 100); 176 | SendMessage(GetDlgItem(hwnd, IDC_OUTLINEOPACITY), UDM_SETPOS32, 0, data->GetInt(TEXT("outlineOpacity"), 100)); 177 | 178 | //Groupbox.Nickname---------------------------------------- 179 | bool bCheckedNickname = data->GetInt(TEXT("useNickname"), 1) != 0; 180 | SendMessage(GetDlgItem(hwnd, IDC_USENICKNAME), BM_SETCHECK, bCheckedNickname ? BST_CHECKED : BST_UNCHECKED, 0); 181 | EnableWindow(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), bCheckedNickname); 182 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEFALLBACKCOLOR), bCheckedNickname); 183 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEFONT), bCheckedNickname); 184 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMESIZE), bCheckedNickname); 185 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY), bCheckedNickname); 186 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEBOLD), bCheckedNickname); 187 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEITALIC), bCheckedNickname); 188 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEUNDERLINE), bCheckedNickname); 189 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), bCheckedNickname); 190 | 191 | CCSetColor(GetDlgItem(hwnd, IDC_NICKNAMEFALLBACKCOLOR), data->GetInt(TEXT("NicknameFallbackColor"), 0xFFFFFF00)); 192 | bool bCheckedUseNicknameColor = data->GetInt(TEXT("useNicknameColor"), 1) != 0; 193 | SendMessage(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), BM_SETCHECK, bCheckedUseNicknameColor ? BST_CHECKED : BST_UNCHECKED, 0); 194 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMESIZE), UDM_SETRANGE32, 5, 2048); 195 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMESIZE), UDM_SETPOS32, 0, data->GetInt(TEXT("nicknameSize"), 48)); 196 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY), UDM_SETRANGE32, 0, 100); 197 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY), UDM_SETPOS32, 0, data->GetInt(TEXT("nicknameOpacity"), 100)); 198 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEBOLD), BM_SETCHECK, data->GetInt(TEXT("nicknameBold"), 1) ? BST_CHECKED : BST_UNCHECKED, 0); 199 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEITALIC), BM_SETCHECK, data->GetInt(TEXT("nicknameItalic"), 0) ? BST_CHECKED : BST_UNCHECKED, 0); 200 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUNDERLINE), BM_SETCHECK, data->GetInt(TEXT("nicknameUnderline"), 0) ? BST_CHECKED : BST_UNCHECKED, 0); 201 | 202 | bool bCheckedNicknameOutline = data->GetInt(TEXT("nicknameUseOutline"), 1) != 0; 203 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), BM_SETCHECK, bCheckedNicknameOutline ? BST_CHECKED : BST_UNCHECKED, 0); 204 | 205 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), bCheckedNickname && bCheckedUseNicknameColor && bCheckedNicknameOutline); 206 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINECOLOR), bCheckedNickname && bCheckedNicknameOutline); 207 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS_EDIT), bCheckedNickname && bCheckedNicknameOutline); 208 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), bCheckedNickname && bCheckedNicknameOutline); 209 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY_EDIT), bCheckedNickname && bCheckedNicknameOutline); 210 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), bCheckedNickname && bCheckedNicknameOutline); 211 | 212 | CCSetColor(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINECOLOR), data->GetInt(TEXT("nicknameOutlineColor"), 0xFF000000)); 213 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), UDM_SETRANGE32, 1, 20); 214 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), UDM_SETPOS32, 0, data->GetInt(TEXT("nicknameOutlineSize"), 5)); 215 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), UDM_SETRANGE32, 0, 100); 216 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), UDM_SETPOS32, 0, data->GetInt(TEXT("nicknameOutlineOpacity"), 100)); 217 | SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), BM_SETCHECK, data->GetInt(TEXT("nicknameOutlineAutoColor"), 1) ? BST_CHECKED : BST_UNCHECKED, 0); 218 | //Groupbox.Connection---------------------------------------- 219 | HWND hwndIRCServer = GetDlgItem(hwnd, IDC_IRCSERVER); 220 | SendMessage(hwndIRCServer, CB_ADDSTRING, 0, (LPARAM)L"Twitch (irc.chat.twitch.tv)"); 221 | SendMessage(hwndIRCServer, CB_ADDSTRING, 0, (LPARAM)L"Twitch (irc.twitch.tv)"); 222 | int iServer = data->GetInt(TEXT("iServer"), 0); 223 | ClampVal(iServer, 0, 1); 224 | SendMessage(hwndIRCServer, CB_SETCURSEL, iServer, 0); 225 | 226 | HWND hwndIRCPort = GetDlgItem(hwnd, IDC_PORT); 227 | SendMessage(hwndIRCPort, CB_ADDSTRING, 0, (LPARAM)L"6667"); 228 | SendMessage(hwndIRCPort, CB_ADDSTRING, 0, (LPARAM)L"80"); 229 | int iPort = data->GetInt(TEXT("iPort"), 0); 230 | ClampVal(iServer, 0, 2); 231 | SendMessage(hwndIRCPort, CB_SETCURSEL, iPort, 0); 232 | 233 | SetWindowText(GetDlgItem(hwnd, IDC_CHANNEL), data->GetString(TEXT("Channel"))); 234 | 235 | bool bUseJustinfan = data->GetInt(TEXT("useJustinfan"), 1) != 0; 236 | SendMessage(GetDlgItem(hwnd, IDC_USEJUSTINFAN), BM_SETCHECK, bUseJustinfan ? BST_CHECKED : BST_UNCHECKED, 0); 237 | 238 | SetWindowText(GetDlgItem(hwnd, IDC_NICKNAME), data->GetString(TEXT("Nickname"))); 239 | SetWindowText(GetDlgItem(hwnd, IDC_PASSWORD), data->GetString(TEXT("Password"))); 240 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAME), !bUseJustinfan); 241 | EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD), !bUseJustinfan); 242 | 243 | //Finish------------------------------------------------------------------------- 244 | 245 | bInitializedDialog = true; 246 | 247 | return TRUE; 248 | } 249 | 250 | case WM_DESTROY: 251 | bInitializedDialog = false; 252 | break; 253 | 254 | case WM_COMMAND: 255 | switch(LOWORD(wParam)) 256 | { //FONT ONLY 257 | case IDC_FONT: 258 | case IDC_NICKNAMEFONT: 259 | if(bInitializedDialog) 260 | { 261 | if(HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE) 262 | { 263 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 264 | if(!configInfo) break; 265 | 266 | String strFont; 267 | if(HIWORD(wParam) == CBN_SELCHANGE) 268 | strFont = GetFontFace(configInfo, (HWND)lParam); 269 | else 270 | { 271 | UINT id = FindFontName(configInfo, (HWND)lParam, GetEditText((HWND)lParam)); 272 | if(id != INVALID) 273 | strFont = configInfo->fontFaces[id]; 274 | } 275 | 276 | ImageSource *source = API->GetSceneImageSource(configInfo->lpName); 277 | if(source && strFont.IsValid()) { 278 | switch(LOWORD(wParam)) { 279 | case IDC_FONT: 280 | source->SetString(TEXT("font"), strFont); 281 | break; 282 | case IDC_NICKNAMEFONT: 283 | source->SetString(TEXT("nicknameFont"), strFont); 284 | break; 285 | } 286 | } 287 | } 288 | } 289 | break; 290 | 291 | //COLOR SELECTION 292 | case IDC_BACKGROUNDCOLOR: 293 | case IDC_COLOR: 294 | case IDC_OUTLINECOLOR: 295 | case IDC_NICKNAMEFALLBACKCOLOR: 296 | case IDC_NICKNAMEOUTLINECOLOR: 297 | if(bInitializedDialog) 298 | { 299 | DWORD color = CCGetColor((HWND)lParam); 300 | 301 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 302 | if(!configInfo) break; 303 | ImageSource *source = API->GetSceneImageSource(configInfo->lpName); 304 | if(source) 305 | { 306 | switch(LOWORD(wParam)) 307 | { 308 | case IDC_BACKGROUNDCOLOR: source->SetInt(TEXT("backgroundColor"), color); break; 309 | case IDC_COLOR: source->SetInt(TEXT("color"), color); break; 310 | case IDC_OUTLINECOLOR: source->SetInt(TEXT("outlineColor"), color); break; 311 | case IDC_NICKNAMEFALLBACKCOLOR: source->SetInt(TEXT("NicknameFallbackColor"), color); break; 312 | case IDC_NICKNAMEOUTLINECOLOR: source->SetInt(TEXT("nicknameOutlineColor"), color); break; 313 | } 314 | } 315 | } 316 | break; 317 | 318 | //TEXT EDIT 319 | //ExtentSize: change only after press "OK" 320 | case IDC_NUMOFLINES_EDIT: 321 | case IDC_SCROLLSPEED_EDIT: 322 | case IDC_BACKGROUNDOPACITY_EDIT: 323 | case IDC_TEXTSIZE_EDIT: 324 | case IDC_TEXTOPACITY_EDIT: 325 | case IDC_OUTLINETHICKNESS_EDIT: 326 | case IDC_OUTLINEOPACITY_EDIT: 327 | case IDC_NICKNAMESIZE_EDIT: 328 | case IDC_NICKNAMEOPACITY_EDIT: 329 | case IDC_NICKNAMEOUTLINETHICKNESS_EDIT: 330 | case IDC_NICKNAMEOUTLINEOPACITY_EDIT: 331 | if(HIWORD(wParam) == EN_CHANGE && bInitializedDialog) 332 | { 333 | int val = (int)SendMessage(GetWindow((HWND)lParam, GW_HWNDNEXT), UDM_GETPOS32, 0, 0); 334 | 335 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 336 | if(!configInfo) break; 337 | 338 | ImageSource *source = API->GetSceneImageSource(configInfo->lpName); 339 | if(source) 340 | { 341 | switch(LOWORD(wParam)) 342 | { 343 | case IDC_NUMOFLINES_EDIT: source->SetInt(TEXT("NumOfLines"), val); break; 344 | case IDC_SCROLLSPEED_EDIT: source->SetInt(TEXT("scrollSpeed"), val); break; 345 | case IDC_BACKGROUNDOPACITY_EDIT: source->SetInt(TEXT("backgroundOpacity"), val); break; 346 | case IDC_TEXTSIZE_EDIT: source->SetInt(TEXT("fontSize"), val); break; 347 | case IDC_TEXTOPACITY_EDIT: source->SetInt(TEXT("textOpacity"), val); break; 348 | case IDC_OUTLINETHICKNESS_EDIT: source->SetFloat(TEXT("outlineSize"), (float)val); break; 349 | case IDC_OUTLINEOPACITY_EDIT: source->SetInt(TEXT("outlineOpacity"), val); break; 350 | case IDC_NICKNAMESIZE_EDIT: source->SetInt(TEXT("nicknameSize"), val); break; 351 | case IDC_NICKNAMEOPACITY_EDIT: source->SetInt(TEXT("nicknameOpacity"), val); break; 352 | case IDC_NICKNAMEOUTLINETHICKNESS_EDIT: source->SetFloat(TEXT("nicknameOutlineSize"), (float)val); break; 353 | case IDC_NICKNAMEOUTLINEOPACITY_EDIT: source->SetInt(TEXT("nicknameOutlineOpacity"), val); break; 354 | } 355 | } 356 | } 357 | break; 358 | 359 | //Checkbox 360 | //case IDC_POINTFILTERING: Effect After Press OK 361 | case IDC_BOLD: 362 | case IDC_ITALIC: 363 | case IDC_UNDERLINE: 364 | case IDC_USEOUTLINE: 365 | case IDC_USENICKNAME: 366 | case IDC_USENICKNAMECOLOR: 367 | case IDC_NICKNAMEBOLD: 368 | case IDC_NICKNAMEITALIC: 369 | case IDC_NICKNAMEUNDERLINE: 370 | case IDC_NICKNAMEUSEOUTLINE: 371 | case IDC_NICKNAMEOUTLINEAUTOCOLOR: 372 | case IDC_USEJUSTINFAN: 373 | if(HIWORD(wParam) == BN_CLICKED && bInitializedDialog) 374 | { 375 | BOOL bChecked = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; 376 | 377 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 378 | if(!configInfo) break; 379 | XElement *data = configInfo->data; 380 | 381 | ImageSource *source = API->GetSceneImageSource(configInfo->lpName); 382 | if(source) 383 | { 384 | switch(LOWORD(wParam)) 385 | { 386 | case IDC_BOLD: source->SetInt(TEXT("bold"), bChecked); break; 387 | case IDC_ITALIC: source->SetInt(TEXT("italic"), bChecked); break; 388 | case IDC_UNDERLINE: source->SetInt(TEXT("underline"), bChecked); break; 389 | case IDC_USEOUTLINE: source->SetInt(TEXT("useOutline"), bChecked); break; 390 | case IDC_USENICKNAME: source->SetInt(TEXT("useNickname"), bChecked); break; 391 | case IDC_USENICKNAMECOLOR: source->SetInt(TEXT("useNicknameColor"), bChecked); break; 392 | case IDC_NICKNAMEBOLD: source->SetInt(TEXT("nicknameBold"), bChecked); break; 393 | case IDC_NICKNAMEITALIC: source->SetInt(TEXT("nicknameItalic"), bChecked); break; 394 | case IDC_NICKNAMEUNDERLINE: source->SetInt(TEXT("nicknameUnderline"), bChecked); break; 395 | case IDC_NICKNAMEUSEOUTLINE: source->SetInt(TEXT("nicknameUseOutline"), bChecked); break; 396 | case IDC_NICKNAMEOUTLINEAUTOCOLOR: source->SetInt(TEXT("nicknameOutlineAutoColor"), bChecked); break; 397 | case IDC_USEJUSTINFAN: /*source->SetInt(TEXT("useJustinfan"), bChecked);*/ break; //Effect after OK 398 | } 399 | } 400 | //Enable or Disable object 401 | if(LOWORD(wParam) == IDC_USEOUTLINE) 402 | { 403 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS_EDIT), bChecked); 404 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS), bChecked); 405 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINECOLOR), bChecked); 406 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINEOPACITY_EDIT), bChecked); 407 | EnableWindow(GetDlgItem(hwnd, IDC_OUTLINEOPACITY), bChecked); 408 | } 409 | 410 | if(LOWORD(wParam) == IDC_USENICKNAME) 411 | { 412 | BOOL bCheckedNickname = bChecked; 413 | EnableWindow(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), bCheckedNickname); 414 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEFALLBACKCOLOR), bCheckedNickname); 415 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEFONT), bCheckedNickname); 416 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMESIZE), bCheckedNickname); 417 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMESIZE_EDIT), bCheckedNickname); 418 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY), bCheckedNickname); 419 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY_EDIT), bCheckedNickname); 420 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEBOLD), bCheckedNickname); 421 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEITALIC), bCheckedNickname); 422 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEUNDERLINE), bCheckedNickname); 423 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), bCheckedNickname); 424 | 425 | //BOOL bCheckedNickname = SendMessage(GetDlgItem(hwnd, IDC_USENICKNAME), BM_GETCHECK, 0, 0) == BST_CHECKED; 426 | BOOL bCheckedUseNicknameColor = SendMessage(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), BM_GETCHECK, 0, 0) == BST_CHECKED; 427 | BOOL bCheckedNicknameOutline = SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), BM_GETCHECK, 0, 0) == BST_CHECKED; 428 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), bCheckedNickname && bCheckedUseNicknameColor && bCheckedNicknameOutline); 429 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINECOLOR), bCheckedNickname && bCheckedNicknameOutline); 430 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS_EDIT), bCheckedNickname && bCheckedNicknameOutline); 431 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), bCheckedNickname && bCheckedNicknameOutline); 432 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY_EDIT), bCheckedNickname && bCheckedNicknameOutline); 433 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), bCheckedNickname && bCheckedNicknameOutline); 434 | } 435 | 436 | if(LOWORD(wParam) == IDC_NICKNAMEUSEOUTLINE) 437 | { 438 | BOOL bCheckedNickname = SendMessage(GetDlgItem(hwnd, IDC_USENICKNAME), BM_GETCHECK, 0, 0) == BST_CHECKED; 439 | BOOL bCheckedUseNicknameColor = SendMessage(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), BM_GETCHECK, 0, 0) == BST_CHECKED; 440 | BOOL bCheckedNicknameOutline = bChecked; 441 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), bCheckedNickname && bCheckedUseNicknameColor && bCheckedNicknameOutline); 442 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINECOLOR), bCheckedNickname && bCheckedNicknameOutline); 443 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS_EDIT), bCheckedNickname && bCheckedNicknameOutline); 444 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), bCheckedNickname && bCheckedNicknameOutline); 445 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY_EDIT), bCheckedNickname && bCheckedNicknameOutline); 446 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), bCheckedNickname && bCheckedNicknameOutline); 447 | } 448 | if(LOWORD(wParam) == IDC_USENICKNAMECOLOR) 449 | { 450 | BOOL bCheckedNickname = SendMessage(GetDlgItem(hwnd, IDC_USENICKNAME), BM_GETCHECK, 0, 0) == BST_CHECKED; 451 | BOOL bCheckedUseNicknameColor = bChecked; 452 | BOOL bCheckedNicknameOutline = SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), BM_GETCHECK, 0, 0) == BST_CHECKED; 453 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), bCheckedNickname && bCheckedUseNicknameColor && bCheckedNicknameOutline); 454 | } 455 | 456 | if(LOWORD(wParam) == IDC_USEJUSTINFAN) 457 | { 458 | EnableWindow(GetDlgItem(hwnd, IDC_NICKNAME), !bChecked); 459 | EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD), !bChecked); 460 | } 461 | } 462 | break; 463 | 464 | case IDC_IRCSERVER: 465 | case IDC_PORT: 466 | case IDC_NICKNAME: 467 | case IDC_PASSWORD: 468 | case IDC_CHANNEL: 469 | break; 470 | 471 | case IDOK: 472 | { 473 | ConfigTextSourceInfo *configInfo = (ConfigTextSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER); 474 | if(!configInfo) break; 475 | XElement *data = configInfo->data; 476 | 477 | String strFont = GetFontFace(configInfo, GetDlgItem(hwnd, IDC_FONT)); 478 | String strFontDisplayName = GetEditText(GetDlgItem(hwnd, IDC_FONT)); 479 | if( strFont.IsEmpty() ) { 480 | UINT id = FindFontName(configInfo, GetDlgItem(hwnd, IDC_FONT), strFontDisplayName); 481 | if(id != INVALID) 482 | strFont = configInfo->fontFaces[id]; 483 | } 484 | 485 | if( strFont.IsEmpty() ) { 486 | String strError = Str("Sources.TextSource.FontNotFound"); 487 | strError.FindReplace(TEXT("$1"), strFontDisplayName); 488 | MessageBox(hwnd, strError, NULL, 0); 489 | break; 490 | } 491 | 492 | String strNicknameFont = GetFontFace(configInfo, GetDlgItem(hwnd, IDC_NICKNAMEFONT)); 493 | String strNicknameFontDisplayName = GetEditText(GetDlgItem(hwnd, IDC_NICKNAMEFONT)); 494 | if( strNicknameFont.IsEmpty() ) { 495 | UINT id = FindFontName(configInfo, GetDlgItem(hwnd, IDC_NICKNAMEFONT), strNicknameFontDisplayName); 496 | if(id != INVALID) 497 | strNicknameFont = configInfo->fontFaces[id]; 498 | } 499 | 500 | if( strNicknameFont.IsEmpty() ) { 501 | String strError = Str("Sources.TextSource.FontNotFound"); 502 | strError.FindReplace(TEXT("$1"), strNicknameFontDisplayName); 503 | MessageBox(hwnd, strError, NULL, 0); 504 | break; 505 | } 506 | 507 | UINT extentWidth=(UINT)SendMessage(GetDlgItem(hwnd, IDC_EXTENTWIDTH), UDM_GETPOS32, 0, 0); 508 | UINT extentHeight=(UINT)SendMessage(GetDlgItem(hwnd, IDC_EXTENTHEIGHT), UDM_GETPOS32, 0, 0); 509 | configInfo->cx = float(extentWidth); 510 | configInfo->cy = float(extentHeight); 511 | data->SetFloat(TEXT("baseSizeCX"), configInfo->cx); 512 | data->SetFloat(TEXT("baseSizeCY"), configInfo->cy); 513 | data->SetInt(TEXT("extentWidth"), extentWidth); 514 | data->SetInt(TEXT("extentHeight"), extentHeight); 515 | data->SetInt(TEXT("NumOfLines"),(int)SendMessage(GetDlgItem(hwnd, IDC_NUMOFLINES), UDM_GETPOS32, 0, 0)); 516 | data->SetInt(TEXT("scrollSpeed"), (int)SendMessage(GetDlgItem(hwnd, IDC_SCROLLSPEED), UDM_GETPOS32, 0, 0)); 517 | data->SetInt(TEXT("backgroundColor"), CCGetColor(GetDlgItem(hwnd, IDC_BACKGROUNDCOLOR))); 518 | data->SetInt(TEXT("backgroundOpacity"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_BACKGROUNDOPACITY), UDM_GETPOS32, 0, 0)); 519 | data->SetInt(TEXT("pointFiltering"), SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_GETCHECK, 0, 0) == BST_CHECKED); 520 | 521 | data->SetString(TEXT("font"), strFont); 522 | data->SetInt(TEXT("fontSize"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_TEXTSIZE), UDM_GETPOS32, 0, 0)); 523 | data->SetInt(TEXT("color"), CCGetColor(GetDlgItem(hwnd, IDC_COLOR))); 524 | data->SetInt(TEXT("textOpacity"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_TEXTOPACITY), UDM_GETPOS32, 0, 0)); 525 | data->SetInt(TEXT("bold"), SendMessage(GetDlgItem(hwnd, IDC_BOLD), BM_GETCHECK, 0, 0) == BST_CHECKED); 526 | data->SetInt(TEXT("italic"), SendMessage(GetDlgItem(hwnd, IDC_ITALIC), BM_GETCHECK, 0, 0) == BST_CHECKED); 527 | data->SetInt(TEXT("underline"), SendMessage(GetDlgItem(hwnd, IDC_UNDERLINE), BM_GETCHECK, 0, 0) == BST_CHECKED); 528 | data->SetInt(TEXT("useOutline"), SendMessage(GetDlgItem(hwnd, IDC_USEOUTLINE), BM_GETCHECK, 0, 0) == BST_CHECKED); 529 | data->SetInt(TEXT("outlineColor"), CCGetColor(GetDlgItem(hwnd, IDC_OUTLINECOLOR))); 530 | data->SetFloat(TEXT("outlineSize"), (float)SendMessage(GetDlgItem(hwnd, IDC_OUTLINETHICKNESS), UDM_GETPOS32, 0, 0)); 531 | data->SetInt(TEXT("outlineOpacity"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_OUTLINEOPACITY), UDM_GETPOS32, 0, 0)); 532 | 533 | data->SetInt(TEXT("useNickname"), SendMessage(GetDlgItem(hwnd, IDC_USENICKNAME), BM_GETCHECK, 0, 0) == BST_CHECKED ); 534 | data->SetInt(TEXT("NicknameFallbackColor"), CCGetColor(GetDlgItem(hwnd, IDC_NICKNAMEFALLBACKCOLOR))); 535 | data->SetInt(TEXT("useNicknameColor"), SendMessage(GetDlgItem(hwnd, IDC_USENICKNAMECOLOR), BM_GETCHECK, 0, 0) == BST_CHECKED ); 536 | data->SetString(TEXT("nicknameFont"), strNicknameFont); 537 | data->SetInt(TEXT("nicknameSize"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_NICKNAMESIZE), UDM_GETPOS32, 0, 0)); 538 | data->SetInt(TEXT("nicknameBold"), SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEBOLD), BM_GETCHECK, 0, 0) == BST_CHECKED); 539 | data->SetInt(TEXT("nicknameItalic"), SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEITALIC), BM_GETCHECK, 0, 0) == BST_CHECKED); 540 | data->SetInt(TEXT("nicknameUnderline"), SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUNDERLINE), BM_GETCHECK, 0, 0) == BST_CHECKED); 541 | data->SetInt(TEXT("nicknameOpacity"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOPACITY), UDM_GETPOS32, 0, 0)); 542 | data->SetInt(TEXT("nicknameUseOutline"), SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEUSEOUTLINE), BM_GETCHECK, 0, 0) == BST_CHECKED); 543 | data->SetInt(TEXT("nicknameOutlineColor"), CCGetColor(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINECOLOR))); 544 | data->SetFloat(TEXT("nicknameOutlineSize"), (float)SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINETHICKNESS), UDM_GETPOS32, 0, 0)); 545 | data->SetInt(TEXT("nicknameOutlineOpacity"), (UINT)SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEOPACITY), UDM_GETPOS32, 0, 0)); 546 | data->SetInt(TEXT("nicknameOutlineAutoColor"), SendMessage(GetDlgItem(hwnd, IDC_NICKNAMEOUTLINEAUTOCOLOR), BM_GETCHECK, 0, 0) == BST_CHECKED); 547 | 548 | data->SetInt(TEXT("iServer"), (int)SendMessage(GetDlgItem(hwnd, IDC_IRCSERVER), CB_GETCURSEL, 0, 0)); 549 | data->SetInt(TEXT("iPort"), (int)SendMessage(GetDlgItem(hwnd, IDC_PORT), CB_GETCURSEL, 0, 0)); 550 | data->SetString(TEXT("Nickname"), GetEditText(GetDlgItem(hwnd, IDC_NICKNAME))); 551 | data->SetString(TEXT("Password"), GetEditText(GetDlgItem(hwnd, IDC_PASSWORD))); 552 | data->SetString(TEXT("Channel"), GetEditText(GetDlgItem(hwnd, IDC_CHANNEL))); 553 | 554 | data->SetInt(TEXT("useJustinfan"), SendMessage(GetDlgItem(hwnd, IDC_USEJUSTINFAN), BM_GETCHECK, 0, 0) == BST_CHECKED ); 555 | 556 | } 557 | 558 | case IDCANCEL: 559 | if(LOWORD(wParam) == IDCANCEL) 560 | DoCancelStuff(hwnd); 561 | 562 | EndDialog(hwnd, LOWORD(wParam)); 563 | } 564 | break; 565 | 566 | case WM_CLOSE: 567 | DoCancelStuff(hwnd); 568 | EndDialog(hwnd, IDCANCEL); 569 | } 570 | return 0; 571 | } 572 | 573 | bool STDCALL ConfigureTextSource(XElement *element, bool bCreating) 574 | { 575 | if(!element) 576 | { 577 | AppWarning(TEXT("ConfigureTextSource: NULL element")); 578 | return false; 579 | } 580 | 581 | XElement *data = element->GetElement(TEXT("data")); 582 | if(!data) 583 | data = element->CreateElement(TEXT("data")); 584 | 585 | ConfigTextSourceInfo configInfo; 586 | configInfo.lpName = element->GetName(); 587 | configInfo.data = data; 588 | 589 | if(DialogBoxParam(hinstMain, MAKEINTRESOURCE(IDD_CONFIG), API->GetMainWindow(), ConfigureTextProc, (LPARAM)&configInfo) == IDOK) 590 | { 591 | element->SetFloat(TEXT("cx"), configInfo.cx); 592 | element->SetFloat(TEXT("cy"), configInfo.cy); 593 | 594 | return true; 595 | } 596 | 597 | return false; 598 | } 599 | 600 | 601 | bool LoadPlugin() 602 | { 603 | pluginLocale = new LocaleStringLookup; 604 | 605 | if(!pluginLocale->LoadStringFile(TEXT("plugins/NicoCommentPlugin/locale/en.txt"))) 606 | AppWarning(TEXT("Could not open locale string file '%s'"), TEXT("plugins/NicoCommentPlugin/locale/en.txt")); 607 | 608 | if(scmpi(API->GetLanguage(), TEXT("en")) != 0) 609 | { 610 | String pluginStringFile; 611 | pluginStringFile << TEXT("plugins/NicoCommentPlugin/locale/") << API->GetLanguage() << TEXT(".txt"); 612 | if(!pluginLocale->LoadStringFile(pluginStringFile)) 613 | AppWarning(TEXT("Could not open locale string file '%s'"), pluginStringFile.Array()); 614 | } 615 | 616 | InitColorControl(hinstMain); 617 | API->RegisterImageSourceClass(TEXT("NicoCommentPlugin"), PluginStr("ClassName"), (OBSCREATEPROC)CreateTextSource, (OBSCONFIGPROC)ConfigureTextSource); 618 | return true; 619 | } 620 | 621 | void UnloadPlugin() 622 | { 623 | delete pluginLocale; 624 | } 625 | 626 | CTSTR GetPluginName() 627 | { 628 | return PluginStr("Plugin.Name"); 629 | } 630 | 631 | CTSTR GetPluginDescription() 632 | { 633 | return PluginStr("Plugin.Description"); 634 | } 635 | 636 | BOOL CALLBACK DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpBla) 637 | { 638 | if(dwReason == DLL_PROCESS_ATTACH) 639 | hinstMain = hInst; 640 | 641 | return TRUE; 642 | } 643 | 644 | -------------------------------------------------------------------------------- /NicoCommentPlugin.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #define _CRT_RAND_S 20 | #include 21 | #include "OBSApi.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "resource.h" 30 | #include "constants.h" 31 | #include "Log.h" 32 | #include "SimpleSocket.h" 33 | #include "SimpleThread.h" 34 | #include "IRCMsgThread.h" 35 | #include "IRCBot.h" 36 | 37 | // Message Queued for some time: 38 | // The Frequency of UpdateTexture 39 | #define fMsgBufferTime 0.5f 40 | #define fMsgSpeed (float)(scrollSpeed*extentWidth) / 50.0f 41 | #define fRatio 1.05f 42 | #define iNumChars 1 43 | 44 | #define ClampVal(val, minVal, maxVal) \ 45 | if(val < minVal) val = minVal; \ 46 | else if(val > maxVal) val = maxVal; 47 | 48 | extern LocaleStringLookup *pluginLocale; 49 | #define PluginStr(text) pluginLocale->LookupString(TEXT(text)) 50 | 51 | inline DWORD GetAlphaVal(UINT opacityLevel) 52 | { 53 | return ((opacityLevel*255/100)&0xFF) << 24; 54 | } 55 | enum MSGTYPE {MSG_USER, MSG_CHAT}; 56 | 57 | typedef struct comment { 58 | String comment_str; 59 | UINT comment_Row; 60 | Gdiplus::RectF boundingBox; 61 | UINT comment_color; 62 | MSGTYPE MsgType; 63 | UINT MsgID; 64 | } Comment; 65 | 66 | 67 | bool isFinished(const Comment &obj) { return (obj.boundingBox.GetRight()<0.0f); } 68 | 69 | class NicoCommentPlugin : public ImageSource 70 | { 71 | //Dialog Input Parameters 72 | //Design 73 | UINT extentWidth, extentHeight; 74 | UINT NumOfLines; 75 | int scrollSpeed; 76 | DWORD backgroundColor; 77 | UINT backgroundOpacity; 78 | bool bUsePointFiltering; 79 | //Message 80 | String strFont; 81 | int size; 82 | DWORD color; 83 | UINT opacity; 84 | bool bBold, bItalic, bUnderline; 85 | bool bUseOutline; 86 | float outlineSize; 87 | DWORD outlineColor; 88 | UINT outlineOpacity; 89 | //Nickname 90 | bool bUseNickname; 91 | DWORD NicknameFallbackColor; 92 | bool bUseNicknameColor; 93 | 94 | String strNicknameFont; 95 | int nicknameSize; 96 | DWORD nicknameColor; 97 | UINT nicknameOpacity; 98 | bool bNicknameBold, bNicknameItalic, bNicknameUnderline; 99 | bool bNicknameUseOutline; 100 | float nicknameOutlineSize; 101 | DWORD nicknameOutlineColor; 102 | UINT nicknameOutlineOpacity; 103 | bool bNicknameOutlineAutoColor; 104 | 105 | //Connect 106 | UINT iServer; 107 | UINT iPort; 108 | String Nickname; 109 | String Password; 110 | String Channel; 111 | bool bUseJustinfan; 112 | 113 | //Internal Structure 114 | 115 | 116 | std::wstring server; 117 | std::wstring port; 118 | std::wstring nickname; 119 | std::wstring login; 120 | std::wstring password; 121 | std::wstring channel; 122 | std::wstring last_server; 123 | std::wstring last_port; 124 | std::wstring last_nickname; 125 | std::wstring last_login; 126 | std::wstring last_password; 127 | std::wstring last_channel; 128 | std::wstring justinfan; 129 | 130 | std::list *Lcomment; 131 | UINT Row[10]; 132 | UINT last_RandID; 133 | IRCBot ircbot; 134 | 135 | bool bDoUpdate; 136 | bool bUpdateTexture; 137 | 138 | float scrollValue; 139 | float duration; 140 | float ircTimer; 141 | float showExtentTime; 142 | 143 | Vect2 baseSize; 144 | SIZE textureSize; 145 | Texture *texture; 146 | 147 | SamplerState *ss; 148 | std::unique_ptr sampler; 149 | UINT globalOpacity; 150 | 151 | XElement *data; 152 | 153 | UINT Rand_Height_Generator(){ 154 | //random number generator for Height 155 | UINT rand_num; 156 | rand_s(&rand_num); 157 | //detecting empty row; 158 | UINT iAnyZero=0; 159 | UINT ZeroRow[10]; 160 | for(UINT i=0;imaxRow) maxRow=Row[i]; 173 | for(UINT i=0;iSetLineJoin(Gdiplus::LineJoinRound); 212 | 213 | // Draw the outline 214 | graphics->DrawPath(pen, outlinePath); 215 | 216 | // Draw the text 217 | graphics->FillPath(brush, &path); 218 | 219 | delete pen; 220 | delete outlinePath; 221 | } 222 | 223 | HFONT GetFont(MSGTYPE MsgType) 224 | { 225 | HFONT hFont = NULL; 226 | 227 | LOGFONT lf; 228 | zero(&lf, sizeof(lf)); 229 | lf.lfQuality = ANTIALIASED_QUALITY; 230 | String strChooseFont; 231 | 232 | switch (MsgType) { 233 | case MSG_USER: 234 | lf.lfHeight = nicknameSize; 235 | lf.lfWeight = bNicknameBold ? FW_BOLD : FW_DONTCARE; 236 | lf.lfItalic = bNicknameItalic; 237 | lf.lfUnderline = bNicknameUnderline; 238 | strChooseFont=strNicknameFont; 239 | break; 240 | 241 | case MSG_CHAT: 242 | lf.lfHeight = size; 243 | lf.lfWeight = bBold ? FW_BOLD : FW_DONTCARE; 244 | lf.lfItalic = bItalic; 245 | lf.lfUnderline = bUnderline; 246 | strChooseFont=strFont; 247 | break; 248 | } 249 | 250 | if(strChooseFont.IsValid()) 251 | { 252 | scpy_n(lf.lfFaceName, strChooseFont, 31); 253 | hFont = CreateFontIndirect(&lf); 254 | } 255 | 256 | if(!hFont) 257 | { 258 | scpy_n(lf.lfFaceName, TEXT("Arial"), 31); 259 | hFont = CreateFontIndirect(&lf); 260 | } 261 | 262 | return hFont; 263 | } 264 | /* HFONT GetFallbackFont() //Unicode Fallback Font: Arial Unicode MS from MS OFFICE 265 | { 266 | HFONT hFont = NULL; 267 | LOGFONT lf; 268 | zero(&lf, sizeof(lf)); 269 | lf.lfHeight = size; 270 | lf.lfWeight = bBold ? FW_BOLD : FW_DONTCARE; 271 | lf.lfItalic = bItalic; 272 | lf.lfUnderline = bUnderline; 273 | lf.lfQuality = ANTIALIASED_QUALITY; 274 | scpy_n(lf.lfFaceName, TEXT("Arial Unicode MS"), 31); 275 | hFont = CreateFontIndirect(&lf); 276 | if(!hFont) { //if Arial Unicode MS not found 277 | hFont = GetFont(); //use USER-Selected Font 278 | } 279 | return hFont; 280 | }*/ 281 | 282 | void SetStringFormat(Gdiplus::StringFormat &format) 283 | { 284 | UINT formatFlags; 285 | 286 | formatFlags = Gdiplus::StringFormatFlagsNoFitBlackBox 287 | | Gdiplus::StringFormatFlagsMeasureTrailingSpaces; 288 | 289 | format.SetFormatFlags(formatFlags); 290 | format.SetTrimming(Gdiplus::StringTrimmingWord); 291 | } 292 | 293 | float Calculate_BoundaryBox(String msg, Gdiplus::PointF origin, Gdiplus::RectF &boundingBox, MSGTYPE MsgType ) 294 | { 295 | HDC hdc = CreateCompatibleDC(NULL); 296 | HFONT hFont=GetFont(MsgType); if(!hFont) return 0.0f; 297 | Gdiplus::Font font(hdc, hFont); 298 | //SelectObject(hdc,hFont); 299 | //WORD GlyInd='\0'; 300 | //int iError=GetGlyphIndicesW(hdc, msg.Array(), 1, &GlyInd, GGI_MARK_NONEXISTING_GLYPHS); 301 | //if(GlyInd==0xFFFF) { 302 | // onDebugMsg(L"GetGlyphIndicesW: msg=%ls, Glyind=0x%04X, Use Fallback Font instead",msg.Array(),GlyInd); 303 | // hFont=GetFallbackFont(); 304 | // SelectObject(hdc,hFont); 305 | // iError=GetGlyphIndicesW(hdc, msg.Array(), 1, &GlyInd, GGI_MARK_NONEXISTING_GLYPHS); 306 | // onDebugMsg(L"Using FallbackFont, : msg=%ls, Glyind=0x%04X", msg.Array(), GlyInd); 307 | //} 308 | //ABCFLOAT tmpABCFloat; 309 | //GetCharABCWidthsFloat(hdc, GlyInd, GlyInd, &tmpABCFloat); 310 | //onDebugMsg(L"Font ABC: msg=%ls, A: %f, B:%f, C:%f, A+B+C:%f", msg.Array(), 311 | // tmpABCFloat.abcfA, tmpABCFloat.abcfB, tmpABCFloat.abcfC, 312 | // tmpABCFloat.abcfA+tmpABCFloat.abcfB+tmpABCFloat.abcfC ); 313 | 314 | Gdiplus::Graphics *graphics = new Gdiplus::Graphics(hdc); 315 | Gdiplus::StringFormat format(Gdiplus::StringFormat::GenericTypographic()); 316 | SetStringFormat(format); 317 | graphics->SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit); 318 | 319 | Gdiplus::Status stat = graphics->MeasureString(msg, -1, &font, origin, &format, &boundingBox); 320 | //onDebugMsg(L"Font Width: msg=%ls, BoxWidth:%f, Diff:%f", msg.Array(), boundingBox.Width, 321 | // boundingBox.Width-(tmpABCFloat.abcfA+tmpABCFloat.abcfB+tmpABCFloat.abcfC) ); 322 | if(stat != Gdiplus::Ok) 323 | AppWarning(TEXT("NicoCommentPlugin::UpdateTexture: Gdiplus::Graphics::MeasureString failed: %u %ls"), (int)stat,msg.Array()); 324 | delete graphics; 325 | DeleteDC(hdc); 326 | hdc = NULL; 327 | DeleteObject(hFont); 328 | //TEST 329 | //boundingBox.Width=(tmpABCFloat.abcfB); 330 | //return (float)(boundingBox.X+tmpABCFloat.abcfA+tmpABCFloat.abcfB+tmpABCFloat.abcfC); 331 | return boundingBox.GetRight(); 332 | 333 | } 334 | 335 | float PushMsgIntoList(std::wstring msg, UINT rand_row, UINT iColor, float tmpX, MSGTYPE MsgType ) { 336 | //Generate Unique ID for each message 337 | UINT UniqueID; 338 | do { rand_s(&UniqueID); } while(UniqueID==last_RandID); 339 | last_RandID=UniqueID; 340 | 341 | UINT msglen=(UINT)msg.length(); 342 | if(msglen>0){ 343 | //Parse into iNumChars characters....need tuning, maybe 10 or higher 344 | std::queue Qtmp; 345 | for(UINT i=0;i<((msglen-1)/ iNumChars );i++) Qtmp.push(msg.substr(i*iNumChars,iNumChars)); 346 | Qtmp.push(msg.substr(((msglen-1)/iNumChars)*iNumChars)); 347 | //push into Lcomment 348 | while(!Qtmp.empty()) 349 | { 350 | Comment tmp_comment; 351 | tmp_comment.comment_str=String(Qtmp.front().c_str()); 352 | tmp_comment.comment_Row=rand_row; 353 | tmp_comment.comment_color=iColor; 354 | tmp_comment.MsgType=MsgType; 355 | tmp_comment.MsgID=UniqueID; //Unique ID 356 | Row[tmp_comment.comment_Row]++; 357 | tmp_comment.boundingBox=Gdiplus::RectF(0.0f,0.0f,32.0f,32.0f); 358 | Gdiplus::PointF origin(tmpX,float(rand_row)/float(NumOfLines)*(float)extentHeight); 359 | //MeasureString: calculate the corresponding bounding Box; 360 | tmpX=Calculate_BoundaryBox(tmp_comment.comment_str, origin, tmp_comment.boundingBox, MsgType); 361 | Lcomment->push_back(tmp_comment); 362 | Qtmp.pop(); 363 | } 364 | } 365 | return tmpX; 366 | } 367 | 368 | 369 | void UpdateTexture() 370 | { 371 | //Log(TEXT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d."),Row[0],Row[1],Row[2],Row[3],Row[4],Row[5],Row[6],Row[7],Row[8],Row[9]); 372 | HFONT hChatFont = GetFont(MSG_CHAT); if(!hChatFont) return; 373 | HFONT hUserFont = GetFont(MSG_USER); if(!hUserFont) return; 374 | 375 | // HFONT hFallbackFont = GetFallbackFont(); 376 | Gdiplus::Status stat; 377 | Gdiplus::RectF layoutBox; 378 | 379 | HDC hTempDC = CreateCompatibleDC(NULL); 380 | Gdiplus::Font chatFont(hTempDC, hChatFont); 381 | Gdiplus::Font userFont(hTempDC, hUserFont); 382 | /* Gdiplus::Font fallbackfont(hTempDC, hFallbackFont);*/ 383 | 384 | Gdiplus::StringFormat format(Gdiplus::StringFormat::GenericTypographic()); 385 | SetStringFormat(format); 386 | 387 | UINT TextureWidth = extentWidth+(UINT)(fMsgBufferTime*fMsgSpeed*fRatio); //extra 0.05 for uncertainty 388 | 389 | //---------------------------------------------------------------------- 390 | // write image 391 | 392 | BITMAPINFO bi; 393 | zero(&bi, sizeof(bi)); 394 | 395 | void* lpBits; 396 | 397 | BITMAPINFOHEADER &bih = bi.bmiHeader; 398 | bih.biSize = sizeof(bih); 399 | bih.biBitCount = 32; 400 | bih.biWidth = TextureWidth; 401 | bih.biHeight = extentHeight; 402 | bih.biPlanes = 1; 403 | 404 | HBITMAP hBitmap = CreateDIBSection(hTempDC, &bi, DIB_RGB_COLORS, &lpBits, NULL, 0); 405 | Gdiplus::Bitmap bmp(TextureWidth, extentHeight, 4*TextureWidth, PixelFormat32bppARGB, (BYTE*)lpBits); 406 | Gdiplus::Graphics *graphics = new Gdiplus::Graphics(&bmp); 407 | Gdiplus::SolidBrush *brush = new Gdiplus::SolidBrush(Gdiplus::Color(GetAlphaVal(opacity)|(color&0x00FFFFFF))); 408 | 409 | DWORD bkColor; 410 | 411 | if(backgroundOpacity == 0 && scrollSpeed !=0) 412 | bkColor = 1<<24 | (color&0x00FFFFFF); 413 | else 414 | bkColor = GetAlphaVal(backgroundOpacity) | (backgroundColor&0x00FFFFFF); //bUseExtents is always true //never vertical //never wrap 415 | 416 | stat = graphics->Clear(Gdiplus::Color( bkColor )); 417 | if(stat != Gdiplus::Ok) 418 | AppWarning(TEXT("TextSource::UpdateTexture: Graphics::Clear failed: %u"), (int)stat); 419 | 420 | graphics->SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias); 421 | graphics->SetCompositingMode(Gdiplus::CompositingModeSourceOver); 422 | graphics->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); 423 | 424 | 425 | Gdiplus::GraphicsPath path; 426 | Gdiplus::FontFamily fontFamily; 427 | Gdiplus::Font* font; 428 | 429 | 430 | if(!Lcomment->empty()) { //Do nothing if Lcomment is empty 431 | 432 | //Rebuild the messages to strings to speed up drawing 433 | std::list TmpLcomment; 434 | Comment tmp_comment; 435 | /*Initialization*/ 436 | UINT UniqueID; 437 | do { rand_s(&UniqueID); } while(UniqueID==Lcomment->begin()->MsgID); 438 | tmp_comment.MsgID=UniqueID; 439 | tmp_comment.comment_str.Clear(); 440 | Gdiplus::PointF origin(0.0f,0.0f); 441 | 442 | for (std::list::iterator it = Lcomment->begin(); it != Lcomment->end(); it++) { 443 | if(it->MsgID!=tmp_comment.MsgID) {// different object: User and Message use different PushMsgIntoList, so it will works normally, 444 | if(!tmp_comment.comment_str.IsEmpty()) { 445 | Calculate_BoundaryBox(tmp_comment.comment_str, origin, tmp_comment.boundingBox, tmp_comment.MsgType); //last value of a comment 446 | TmpLcomment.push_back(tmp_comment); 447 | } 448 | //typedef struct comment { String comment_str; UINT comment_Row; UINT comment_color; MSGTYPE MsgType; UINT MsgID; Gdiplus::RectF boundingBox; } Comment; 449 | //initialization of next object 450 | tmp_comment.comment_str = it->comment_str; 451 | tmp_comment.comment_Row = it->comment_Row; 452 | tmp_comment.comment_color = it->comment_color; 453 | tmp_comment.MsgType = it->MsgType; 454 | tmp_comment.MsgID = it->MsgID; 455 | tmp_comment.boundingBox=Gdiplus::RectF(0.0f,0.0f,32.0f,32.0f); 456 | it->boundingBox.GetLocation(&origin); //get the origin of the first character 457 | } 458 | else { //same object; 459 | tmp_comment.comment_str+=it->comment_str; 460 | } 461 | } 462 | /*Finishing the rebuild*/ 463 | if(!tmp_comment.comment_str.IsEmpty()) { 464 | Calculate_BoundaryBox(tmp_comment.comment_str, origin, tmp_comment.boundingBox, tmp_comment.MsgType); 465 | TmpLcomment.push_back(tmp_comment); 466 | } 467 | 468 | for (std::list::iterator it = TmpLcomment.begin(); it != TmpLcomment.end(); it++) { 469 | //Check For Fallback; 470 | /*SelectObject(hTempDC,hFont); 471 | WORD GlyInd='\0'; 472 | GetGlyphIndicesW(hTempDC,it->comment_str.Array(),1,&GlyInd,GGI_MARK_NONEXISTING_GLYPHS); 473 | if(GlyInd==0xFFFF) { 474 | font=&fallbackfont; 475 | font->GetFamily(&fontFamily); 476 | } 477 | else { 478 | font=&userFont; 479 | font->GetFamily(&fontFamily); 480 | }*/ 481 | bool bCommentUseOutline = false; 482 | DWORD tmp_OutlineColor=outlineColor; 483 | switch(it->MsgType){ 484 | case MSG_USER: 485 | font=&userFont; 486 | font->GetFamily(&fontFamily); 487 | bCommentUseOutline=bNicknameUseOutline; 488 | if(bNicknameOutlineAutoColor&&bUseNicknameColor){ 489 | UINT brightness=((it->comment_color&0x00FF0000)>>16) + ((it->comment_color&0x0000FF00)>>8) + (it->comment_color&0x000000FF); 490 | if( brightness > 3*128 ) tmp_OutlineColor=0xFF000000;//(black line) 491 | else tmp_OutlineColor=0xFFFFFFFF; 492 | } else tmp_OutlineColor=nicknameOutlineColor; 493 | 494 | break; 495 | case MSG_CHAT: 496 | font=&chatFont; 497 | font->GetFamily(&fontFamily); 498 | bCommentUseOutline=bUseOutline; 499 | tmp_OutlineColor=outlineColor; 500 | break; 501 | } 502 | //Draw The Character One By One 503 | if(it->boundingBox.GetLeft()SetColor(Gdiplus::Color(GetAlphaVal(opacity)|(it->comment_color&0x00FFFFFF))); 506 | path.Reset(); 507 | path.AddString(it->comment_str, -1, &fontFamily, font->GetStyle(), font->GetSize(), it->boundingBox, &format); 508 | DrawOutlineText(graphics, *font, path, format, brush, it->MsgType,tmp_OutlineColor); 509 | } else { 510 | brush->SetColor(Gdiplus::Color(GetAlphaVal(opacity)|(it->comment_color&0x00FFFFFF))); 511 | stat = graphics->DrawString(it->comment_str, -1, font, it->boundingBox, &format, brush); 512 | if(stat != Gdiplus::Ok) 513 | AppWarning(TEXT("TextSource::UpdateTexture: Graphics::DrawString failed: %u"), (int)stat); 514 | } 515 | } 516 | } 517 | } 518 | delete brush; 519 | delete graphics; 520 | 521 | //---------------------------------------------------------------------- 522 | // upload texture 523 | if(textureSize.cx != TextureWidth || textureSize.cy != extentHeight){ 524 | if(texture) 525 | { 526 | delete texture; 527 | texture = NULL; 528 | } 529 | texture = CreateTexture(TextureWidth, extentHeight, GS_BGRA, lpBits, FALSE, FALSE); 530 | textureSize.cx=TextureWidth; 531 | textureSize.cy=extentHeight; 532 | } 533 | 534 | else if(texture) 535 | texture->SetImage(lpBits, GS_IMAGEFORMAT_BGRA, 4*TextureWidth); 536 | 537 | if(!texture) 538 | { 539 | AppWarning(TEXT("NicoCommentPlugin::UpdateTexture: could not create texture")); 540 | DeleteObject(hChatFont); 541 | DeleteObject(hUserFont); 542 | } 543 | 544 | DeleteDC(hTempDC); 545 | DeleteObject(hBitmap); 546 | scrollValue=0.0f; 547 | } 548 | 549 | public: 550 | inline NicoCommentPlugin(XElement *data) 551 | { 552 | ircTimer = 4.0f; 553 | last_server=std::wstring(L""); 554 | last_port=std::wstring(L""); 555 | last_nickname=std::wstring(L""); 556 | last_login=std::wstring(L""); 557 | last_password=std::wstring(L""); 558 | last_channel=std::wstring(L""); 559 | justinfan=std::wstring(L""); 560 | this->data = data; 561 | UpdateSettings(); 562 | 563 | SamplerInfo si; 564 | zero(&si, sizeof(si)); 565 | si.addressU = GS_ADDRESS_REPEAT; 566 | si.addressV = GS_ADDRESS_REPEAT; 567 | si.borderColor = 0; 568 | si.filter = GS_FILTER_LINEAR; 569 | ss = CreateSamplerState(si); 570 | globalOpacity = 100; 571 | //last_row1 = 0; 572 | //last_row2 = 20; 573 | Lcomment = new std::list; 574 | duration = 0.0f; 575 | 576 | onSysMsg(L"Using Nico Comment Plugin Version %d.%d.%d.%d BETA",PLUGIN_VERSION); 577 | zero(&Row,sizeof(UINT)*10); 578 | login=L"NICO_COMMENT_PLUGIN_BETA"; //not used 579 | 580 | } 581 | 582 | ~NicoCommentPlugin() 583 | { 584 | onSysMsg(L"Closing NicoComment Plugin-Begin Process"); 585 | ircbot.close(); 586 | onSysMsg(L"Closing NicoComment Plugin-IRCBot Closed"); 587 | if(texture) 588 | { 589 | delete texture; 590 | texture = NULL; 591 | } 592 | delete ss; 593 | delete Lcomment; 594 | } 595 | 596 | void Preprocess() 597 | { 598 | if(bUpdateTexture) 599 | { 600 | bUpdateTexture = false; 601 | UpdateTexture(); 602 | } 603 | } 604 | 605 | void Tick(float fSeconds) 606 | { 607 | ircTimer+=fSeconds; 608 | if(ircTimer > 5.0f) { 609 | ircTimer=0.0f; 610 | IRCBotStatus iStatus=ircbot.CheckIRCBotStatus(); 611 | if(iStatus==BOT_CONNECTED) iStatus=ircbot.AliveCheckTask(); 612 | if(iStatus==BOT_NEEDRECONNECT) ircbot.reconnect(); 613 | } 614 | 615 | if(scrollSpeed != 0 && texture) 616 | scrollValue += fSeconds*fMsgSpeed/((float)(extentWidth)+(fMsgBufferTime*fMsgSpeed*fRatio)); 617 | 618 | if(!Lcomment->empty()) { 619 | for (std::list::iterator it = Lcomment->begin(); it != Lcomment->end(); it++) { 620 | it-> boundingBox.X-=fSeconds*fMsgSpeed; 621 | if(it->comment_Row!=99)if(it->boundingBox.GetRight() < extentWidth){ //99 for removed 622 | Row[it->comment_Row]--; 623 | it->comment_Row=99; 624 | } 625 | } 626 | Lcomment->remove_if(isFinished);//REMOVE object at the left of the screen 627 | if( Lcomment->empty() ) bDoUpdate = true; //Empty out everything: need to clear texture. 628 | } 629 | 630 | //if(showExtentTime > 0.0f) showExtentTime -= fSeconds; 631 | //Not used; maybe add back. 632 | float fMsgBegin=(float)extentWidth+fMsgBufferTime*fMsgSpeed; 633 | float tmpX=fMsgBegin; 634 | TircMsg tircmsg; 635 | std::wstring msg; 636 | while(!ircbot.QueueEmpty()){ 637 | //receive Msg 638 | ircbot.receiveMsg(tircmsg); 639 | UINT rand_row=Rand_Height_Generator(); 640 | if(bUseNickname) { 641 | UINT iColor=NicknameFallbackColor; 642 | if(bUseNicknameColor) if(tircmsg.usercolor!=0) iColor=tircmsg.usercolor; 643 | tmpX=PushMsgIntoList(tircmsg.user+L": ", rand_row, iColor, tmpX, MSG_USER); 644 | } 645 | tmpX=PushMsgIntoList(tircmsg.msg, rand_row, color, tmpX, MSG_CHAT); 646 | } 647 | 648 | //Update Texture after 1 seconds. 649 | if(Lcomment->empty()){ 650 | duration=0.0f; 651 | if(!bDoUpdate) { 652 | scrollValue=0.0f; //Empty Queue in, Empty Queue out. 653 | return; 654 | } 655 | } 656 | 657 | duration+=fSeconds; 658 | if(duration > (fMsgBufferTime - TINY_EPSILON)) { 659 | bDoUpdate = true; 660 | duration=0.0f; 661 | } 662 | 663 | if(bDoUpdate) 664 | { 665 | bDoUpdate = false; 666 | bUpdateTexture = true; 667 | } 668 | 669 | } 670 | 671 | void Render(const Vect2 &pos, const Vect2 &size) 672 | { 673 | if(texture) 674 | { 675 | Vect2 sizeMultiplier = size/baseSize; 676 | Vect2 newSize = Vect2(float(textureSize.cx), float(textureSize.cy))*sizeMultiplier; 677 | 678 | Vect2 extentVal = Vect2(float(extentWidth), float(extentHeight))*sizeMultiplier; 679 | if(showExtentTime > 0.0f) 680 | { 681 | Shader *pShader = GS->GetCurrentPixelShader(); 682 | Shader *vShader = GS->GetCurrentVertexShader(); 683 | 684 | Color4 rectangleColor = Color4(0.0f, 1.0f, 0.0f, 1.0f); 685 | if(showExtentTime < 1.0f) 686 | rectangleColor.w = showExtentTime; 687 | 688 | //App->solidPixelShader->SetColor(App->solidPixelShader->GetParameter(0), rectangleColor); 689 | //LoadVertexShader(App->solidVertexShader); 690 | //LoadPixelShader(App->solidPixelShader); 691 | DrawBox(pos, extentVal); 692 | 693 | LoadVertexShader(vShader); 694 | LoadPixelShader(pShader); 695 | } 696 | 697 | XRect rect = {int(pos.x), int(pos.y), int(extentVal.x), int(extentVal.y)}; 698 | SetScissorRect(&rect); 699 | 700 | if(bUsePointFiltering) { 701 | if (!sampler) { 702 | SamplerInfo samplerinfo; 703 | samplerinfo.filter = GS_FILTER_POINT; 704 | std::unique_ptr new_sampler(CreateSamplerState(samplerinfo)); 705 | sampler = std::move(new_sampler); 706 | } 707 | 708 | LoadSamplerState(sampler.get(), 0); 709 | } 710 | 711 | DWORD alpha = DWORD(double(globalOpacity)*2.55); 712 | DWORD outputColor = (alpha << 24) | 0xFFFFFF; 713 | 714 | UVCoord ul(0.0f, 0.0f); 715 | UVCoord lr(1.0f, 1.0f); 716 | ul.x += scrollValue; 717 | lr.x += scrollValue; 718 | 719 | LoadSamplerState(ss); 720 | DrawSpriteEx(texture, outputColor, pos.x, pos.y, pos.x+newSize.x, pos.y+newSize.y, ul.x, ul.y, lr.x, lr.y); 721 | 722 | if (bUsePointFiltering) 723 | LoadSamplerState(NULL, 0); 724 | 725 | SetScissorRect(NULL); 726 | } 727 | } 728 | 729 | Vect2 GetSize() const 730 | { 731 | return baseSize; 732 | } 733 | 734 | void UpdateSettings() 735 | { 736 | baseSize.x = data->GetFloat(TEXT("baseSizeCX"), 100); 737 | baseSize.y = data->GetFloat(TEXT("baseSizeCY"), 100); 738 | //Design 739 | extentWidth = data->GetInt(TEXT("extentWidth"), 0); 740 | extentHeight= data->GetInt(TEXT("extentHeight"), 0); 741 | NumOfLines = data->GetInt(TEXT("NumOfLines"), 0); 742 | scrollSpeed = data->GetInt(TEXT("scrollSpeed"), 0); 743 | backgroundColor = data->GetInt(TEXT("backgroundColor"), 0xFF000000); 744 | backgroundOpacity = data->GetInt(TEXT("backgroundOpacity"), 0); 745 | bUsePointFiltering = data->GetInt(TEXT("pointFiltering"), 0) != 0; 746 | 747 | //Message 748 | strFont = data->GetString(TEXT("font"), TEXT("Arial")); 749 | size = data->GetInt(TEXT("fontSize"), 48); 750 | color = data->GetInt(TEXT("color"), 0xFFFFFFFF); 751 | opacity = data->GetInt(TEXT("textOpacity"), 100); 752 | bBold = data->GetInt(TEXT("bold"), 0) != 0; 753 | bItalic = data->GetInt(TEXT("italic"), 0) != 0; 754 | bUnderline = data->GetInt(TEXT("underline"), 0) != 0; 755 | bUseOutline = data->GetInt(TEXT("useOutline"),1) != 0; 756 | outlineColor = data->GetInt(TEXT("outlineColor"), 0xFF000000); 757 | outlineSize = data->GetFloat(TEXT("outlineSize"), 5); 758 | outlineOpacity = data->GetInt(TEXT("outlineOpacity"), 100); 759 | 760 | //Nickname 761 | bUseNickname = data->GetInt(TEXT("useNickname"), 0) != 0; 762 | NicknameFallbackColor = data->GetInt(TEXT("NicknameFallbackColor"), 0xFFFFFF00) ; 763 | bUseNicknameColor = data->GetInt(TEXT("useNicknameColor"), 0) != 0; 764 | strNicknameFont = data->GetString(TEXT("nicknameFont"), TEXT("Arial")); 765 | nicknameSize = data->GetInt(TEXT("nicknameSize"), 48); 766 | nicknameColor = data->GetInt(TEXT("nicknameColor"), 0xFF000000); 767 | nicknameOpacity = data->GetInt(TEXT("nicknameOpacity"), 100); 768 | bNicknameBold = data->GetInt(TEXT("nicknameBold"), 0) != 0; 769 | bNicknameItalic = data->GetInt(TEXT("nicknameItalic"), 0) != 0; 770 | bNicknameUnderline = data->GetInt(TEXT("nicknameUnderline"), 0) != 0; 771 | bNicknameUseOutline = data->GetInt(TEXT("nicknameUseOutline"),1) != 0; 772 | nicknameOutlineColor = data->GetInt(TEXT("nicknameOutlineColor"), 0xFF000000); 773 | nicknameOutlineSize = data->GetFloat(TEXT("nicknameOutlineSize"), 5); 774 | nicknameOutlineOpacity = data->GetInt(TEXT("nicknameOutlineOpacity"), 100); 775 | bNicknameOutlineAutoColor = data->GetInt(TEXT("nicknameOutlineAutoColor"),1) != 0; 776 | 777 | //Connection 778 | iServer = data->GetInt(TEXT("iServer"), 0); 779 | iPort = data->GetInt(TEXT("iPort"), 0); 780 | Nickname = data->GetString(TEXT("Nickname")); 781 | Password = data->GetString(TEXT("Password")); 782 | Channel = data->GetString(TEXT("Channel")); 783 | bUseJustinfan = data->GetInt(TEXT("useJustinfan"),1) != 0; 784 | 785 | bUpdateTexture = true; 786 | TryConnect(); 787 | } 788 | 789 | void SetString(CTSTR lpName, CTSTR lpVal) 790 | { 791 | if(scmpi(lpName, TEXT("Nickname")) == 0) 792 | {Nickname = lpVal; TryConnect();} 793 | else if(scmpi(lpName, TEXT("Password")) == 0) 794 | {Password = lpVal; TryConnect();} 795 | else if(scmpi(lpName, TEXT("Channel")) == 0) 796 | {Channel = lpVal; TryConnect();} 797 | else if(scmpi(lpName, TEXT("font")) == 0) 798 | strFont = lpVal; 799 | else if(scmpi(lpName, TEXT("nicknameFont")) == 0) 800 | strNicknameFont = lpVal; 801 | 802 | bUpdateTexture = true; 803 | } 804 | 805 | void SetInt(CTSTR lpName, int iValue) 806 | { 807 | if(scmpi(lpName, TEXT("extentWidth")) == 0) { 808 | extentWidth = iValue; //showExtentTime = 2.0f; 809 | return; 810 | } 811 | else if(scmpi(lpName, TEXT("extentHeight")) == 0) { 812 | extentHeight = iValue; //showExtentTime = 2.0f; 813 | return; 814 | } 815 | 816 | if(scmpi(lpName, TEXT("NumOfLines")) == 0) 817 | NumOfLines = iValue; 818 | else if(scmpi(lpName, TEXT("scrollSpeed")) == 0) 819 | scrollSpeed = iValue; 820 | else if(scmpi(lpName, TEXT("backgroundColor")) == 0) 821 | backgroundColor = iValue; 822 | else if(scmpi(lpName, TEXT("backgroundOpacity")) == 0) 823 | backgroundOpacity = iValue; 824 | else if(scmpi(lpName, TEXT("pointFiltering")) == 0) 825 | bUsePointFiltering = iValue != 0; 826 | //---------------------------------------------------------------- 827 | else if(scmpi(lpName, TEXT("fontSize")) == 0) 828 | size = iValue; 829 | else if(scmpi(lpName, TEXT("color")) == 0) 830 | color = iValue; 831 | else if(scmpi(lpName, TEXT("textOpacity")) == 0) 832 | opacity = iValue; 833 | else if(scmpi(lpName, TEXT("bold")) == 0) 834 | bBold = iValue != 0; 835 | else if(scmpi(lpName, TEXT("italic")) == 0) 836 | bItalic = iValue != 0; 837 | else if(scmpi(lpName, TEXT("underline")) == 0) 838 | bUnderline = iValue != 0; 839 | else if(scmpi(lpName, TEXT("useOutline")) == 0) 840 | bUseOutline = iValue != 0; 841 | else if(scmpi(lpName, TEXT("outlineColor")) == 0) 842 | outlineColor = iValue; 843 | else if(scmpi(lpName, TEXT("outlineOpacity")) == 0) 844 | outlineOpacity = iValue; 845 | //---------------------------------------------------------------- 846 | else if(scmpi(lpName, TEXT("useNickname")) == 0) 847 | bUseNickname = iValue != 0; 848 | else if(scmpi(lpName, TEXT("NicknameFallbackColor")) == 0) 849 | NicknameFallbackColor = iValue; 850 | else if(scmpi(lpName, TEXT("useNicknameColor")) == 0) 851 | bUseNicknameColor = iValue != 0; 852 | else if(scmpi(lpName, TEXT("nicknameSize")) == 0) 853 | nicknameSize = iValue; 854 | else if(scmpi(lpName, TEXT("nicknameColor")) == 0) 855 | nicknameColor = iValue; 856 | else if(scmpi(lpName, TEXT("nicknameOpacity")) == 0) 857 | nicknameOpacity = iValue; 858 | else if(scmpi(lpName, TEXT("nicknameBold")) == 0) 859 | bNicknameBold = iValue != 0; 860 | else if(scmpi(lpName, TEXT("nicknameItalic")) == 0) 861 | bNicknameItalic = iValue != 0; 862 | else if(scmpi(lpName, TEXT("nicknameUnderline")) == 0) 863 | bNicknameUnderline = iValue != 0; 864 | else if(scmpi(lpName, TEXT("nicknameUseOutline")) == 0) 865 | bNicknameUseOutline = iValue != 0; 866 | else if(scmpi(lpName, TEXT("nicknameOutlineColor")) == 0) 867 | nicknameOutlineColor = iValue; 868 | else if(scmpi(lpName, TEXT("nicknameOutlineOpacity")) == 0) 869 | nicknameOutlineOpacity = iValue; 870 | else if(scmpi(lpName, TEXT("nicknameOutlineAutoColor")) == 0) 871 | bNicknameOutlineAutoColor = iValue != 0; 872 | //---------------------------------------------------------------- 873 | else if(scmpi(lpName, TEXT("iServer")) == 0) 874 | {iServer = iValue; TryConnect();} 875 | else if(scmpi(lpName, TEXT("iPort")) == 0) 876 | {iPort = iValue; TryConnect();} 877 | else if(scmpi(lpName, TEXT("useJustinfan")) == 0) 878 | {bUseJustinfan = iValue != 0; TryConnect();} 879 | 880 | 881 | bUpdateTexture = true; 882 | } 883 | 884 | void SetFloat(CTSTR lpName, float fValue) 885 | { 886 | if(scmpi(lpName, TEXT("outlineSize")) == 0) 887 | outlineSize = fValue; 888 | else if(scmpi(lpName, TEXT("nicknameOutlineSize")) == 0) 889 | nicknameOutlineSize = fValue; 890 | else if(scmpi(lpName, TEXT("baseSizeCX")) == 0) 891 | baseSize.x = fValue; 892 | else if(scmpi(lpName, TEXT("baseSizeCY")) == 0) 893 | baseSize.y = fValue; 894 | 895 | bUpdateTexture = true; 896 | } 897 | 898 | void TryConnect() 899 | { 900 | login=std::wstring(L"NICO_COMMENT_PLUGIN_BETA"); //not used 901 | switch(iServer){ 902 | case 0: {server=std::wstring(L"irc.chat.twitch.tv");break;} 903 | case 1: {server=std::wstring(L"irc.twitch.tv");break;} 904 | default: {server=std::wstring(L"");break;} 905 | } 906 | 907 | switch(iPort){ 908 | case 0: {port=std::wstring(L"6667");break;} 909 | case 1: {port=std::wstring(L"80");break;} 910 | default: {port=std::wstring(L"");break;} 911 | } 912 | 913 | if(bUseJustinfan){ 914 | if(justinfan.empty()) { //generate a new justinfan name 915 | wchar_t buffer[20]; 916 | UINT rand_num; 917 | rand_s(&rand_num); 918 | swprintf_s(buffer,20*sizeof(wchar_t),L"justinfan%06d",rand_num%1000000); 919 | justinfan=std::wstring(buffer); 920 | } 921 | //Apply the nick/pass 922 | nickname=justinfan; 923 | password=std::wstring(L"blah"); 924 | } 925 | else { 926 | if(!Nickname.IsEmpty()) nickname=std::wstring(Nickname.Array()); 927 | else nickname=std::wstring(L""); 928 | if(!Password.IsEmpty()) password=std::wstring(Password.Array()); 929 | else password=std::wstring(L""); 930 | } 931 | 932 | if(!Channel.IsEmpty()) { 933 | Channel=Channel.MakeLower(); 934 | if(Channel.Array()[0]!=L'#') channel=std::wstring(L"#")+std::wstring(Channel.Array()); 935 | else channel=std::wstring(Channel.Array()); 936 | } 937 | else channel=L""; 938 | 939 | if(ircbot.isConnected()) { //Check if disconnection needed 940 | if( last_server.compare(server)!=0 || last_port.compare(port)!=0 || 941 | last_nickname.compare(nickname)!=0 || last_password.compare(password)!=0 || 942 | last_channel.compare(channel)!=0 ) { //any one of them differs: Change of detail, need reconnect 943 | ircbot.close(); 944 | //Sleep(500); 945 | } 946 | } 947 | 948 | if(!ircbot.isConnected()) { //if not connected, connect at first - If enough information 949 | if(server.empty()||port.empty()||nickname.empty()||password.empty()||channel.empty()) 950 | onSysMsg(L"Not Enough Login Information. "); 951 | else{ 952 | last_server=server; last_port=port; last_nickname=nickname; 953 | last_password=password; last_channel=channel; 954 | ircbot.connect(last_server,last_port,last_nickname,login,last_password,last_channel); 955 | } 956 | } 957 | 958 | } 959 | 960 | inline void ResetExtentRect() {showExtentTime = 0.0f;} 961 | }; 962 | 963 | struct ConfigTextSourceInfo 964 | { 965 | CTSTR lpName; 966 | XElement *data; 967 | float cx, cy; 968 | 969 | StringList fontNames; 970 | StringList fontFaces; 971 | }; 972 | 973 | -------------------------------------------------------------------------------- /NicoCommentPlugin.rc: -------------------------------------------------------------------------------- 1 | // Generated by ResEdit 1.6.2 2 | // Copyright (C) 2006-2014 3 | // http://www.resedit.net 4 | 5 | #include 6 | #include 7 | #include 8 | #include "resource.h" 9 | 10 | 11 | 12 | 13 | // 14 | // Dialog resources 15 | // 16 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 17 | IDD_CONFIG DIALOGEX 0, 0, 372, 320 18 | STYLE DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU 19 | CAPTION "Title.NicoCommentPlugin" 20 | FONT 8, "MS Shell Dlg", 400, 0, 1 21 | { 22 | GROUPBOX "Groupbox.Design", IDC_STATIC, 8, 5, 356, 48, 0, WS_EX_LEFT 23 | RTEXT "Design.ExtentSize", IDC_STATIC, 12, 17, 55, 8, SS_RIGHT, WS_EX_LEFT 24 | EDITTEXT IDC_EXTENTWIDTH_EDIT, 70, 15, 35, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 25 | CONTROL "", IDC_EXTENTWIDTH, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 94, 15, 10, 14, WS_EX_LEFT 26 | EDITTEXT IDC_EXTENTHEIGHT_EDIT, 116, 15, 35, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 27 | CONTROL "", IDC_EXTENTHEIGHT, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 140, 15, 10, 14, WS_EX_LEFT 28 | RTEXT "Design.Rows", IDC_STATIC, 156, 17, 31, 8, SS_RIGHT, WS_EX_LEFT 29 | EDITTEXT IDC_NUMOFLINES_EDIT, 189, 15, 30, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 30 | CONTROL "", IDC_NUMOFLINES, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 208, 15, 10, 14, WS_EX_LEFT 31 | RTEXT "Design.ScrollSpeed", IDC_STATIC, 226, 17, 82, 8, SS_RIGHT, WS_EX_LEFT 32 | EDITTEXT IDC_SCROLLSPEED_EDIT, 316, 15, 40, 14, ES_AUTOHSCROLL, WS_EX_LEFT 33 | CONTROL "", IDC_SCROLLSPEED, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 345, 15, 10, 14, WS_EX_LEFT 34 | RTEXT "Design.BackgroundColor", IDC_STATIC, 14, 35, 70, 8, SS_RIGHT, WS_EX_LEFT 35 | CONTROL "", IDC_BACKGROUNDCOLOR, "OBSColorControl", 0x50010000, 86, 33, 14, 14, 0x00000000 36 | RTEXT "Design.BackgroundOpacity", IDC_STATIC, 120, 35, 77, 8, SS_RIGHT, WS_EX_LEFT 37 | EDITTEXT IDC_BACKGROUNDOPACITY_EDIT, 198, 33, 40, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 38 | CONTROL "", IDC_BACKGROUNDOPACITY, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 227, 33, 11, 14, WS_EX_LEFT 39 | AUTOCHECKBOX "Design.PointFiltering", IDC_POINTFILTERING, 245, 34, 110, 8, 0, WS_EX_RIGHT 40 | GROUPBOX "Groupbox.Message", IDC_STATIC, 8, 57, 356, 70, 0, WS_EX_LEFT 41 | RTEXT "Message.Font", IDC_STATIC, 12, 72, 60, 8, SS_RIGHT, WS_EX_LEFT 42 | COMBOBOX IDC_FONT, 75, 70, 110, 30, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_SORT, WS_EX_LEFT 43 | RTEXT "Message.FontSize", IDC_STATIC, 187, 72, 57, 8, SS_RIGHT, WS_EX_LEFT 44 | EDITTEXT IDC_TEXTSIZE_EDIT, 249, 70, 40, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 45 | CONTROL "", IDC_TEXTSIZE, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 278, 70, 10, 14, WS_EX_LEFT 46 | RTEXT "Message.Color", IDC_STATIC, 12, 90, 63, 8, SS_RIGHT, WS_EX_LEFT 47 | CONTROL "", IDC_COLOR, "OBSColorControl", 0x50010000, 75, 88, 14, 14, 0x00000000 48 | RTEXT "Message.Opacity", IDC_STATIC, 131, 90, 70, 8, SS_RIGHT, WS_EX_LEFT 49 | EDITTEXT IDC_TEXTOPACITY_EDIT, 202, 88, 40, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 50 | CONTROL "", IDC_TEXTOPACITY, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 231, 88, 10, 14, WS_EX_LEFT 51 | AUTOCHECKBOX "Message.Bold", IDC_BOLD, 295, 64, 60, 10, 0, WS_EX_RIGHT 52 | AUTOCHECKBOX "Message.Italic", IDC_ITALIC, 295, 77, 60, 10, 0, WS_EX_RIGHT 53 | AUTOCHECKBOX "Message.Underline", IDC_UNDERLINE, 295, 90, 60, 10, 0, WS_EX_RIGHT 54 | AUTOCHECKBOX "Message.UseOutline", IDC_USEOUTLINE, 20, 109, 55, 10, 0, WS_EX_LEFT 55 | RTEXT "Message.Outline.Color", IDC_STATIC, 78, 110, 62, 8, SS_RIGHT, WS_EX_LEFT 56 | CONTROL "", IDC_OUTLINECOLOR, "OBSColorControl", 0x50010000, 141, 108, 14, 14, 0x00000000 57 | RTEXT "Message.Outline.Thickness", IDC_STATIC, 158, 110, 74, 8, SS_RIGHT, WS_EX_LEFT 58 | EDITTEXT IDC_OUTLINETHICKNESS_EDIT, 235, 108, 30, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 59 | CONTROL "", IDC_OUTLINETHICKNESS, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 254, 108, 11, 14, WS_EX_LEFT 60 | RTEXT "Message.Outline.Opacity", IDC_STATIC, 266, 110, 57, 8, SS_RIGHT, WS_EX_LEFT 61 | EDITTEXT IDC_OUTLINEOPACITY_EDIT, 325, 108, 30, 14, ES_AUTOHSCROLL, WS_EX_LEFT 62 | CONTROL "", IDC_OUTLINEOPACITY, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 344, 108, 10, 14, WS_EX_LEFT 63 | GROUPBOX "Groupbox.Nickname", IDC_STATIC, 8, 132, 356, 87, 0, WS_EX_LEFT 64 | AUTOCHECKBOX "Nickname.UseNickname", IDC_USENICKNAME, 20, 143, 79, 8, 0, WS_EX_LEFT 65 | RTEXT "Nickname.DefaultNicknameColor", IDC_STATIC, 103, 144, 95, 8, SS_RIGHT, WS_EX_LEFT 66 | CONTROL "", IDC_NICKNAMEFALLBACKCOLOR, "OBSColorControl", 0x50010000, 200, 141, 14, 14, 0x00000000 67 | AUTOCHECKBOX "Nickname.UseNicknameColor", IDC_USENICKNAMECOLOR, 227, 143, 130, 10, 0, WS_EX_LEFT 68 | RTEXT "Nickname.Font", IDC_STATIC, 15, 163, 60, 8, SS_RIGHT, WS_EX_LEFT 69 | COMBOBOX IDC_NICKNAMEFONT, 78, 161, 110, 30, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_SORT, WS_EX_LEFT 70 | RTEXT "Nickname.FontSize", IDC_STATIC, 190, 163, 57, 8, SS_RIGHT, WS_EX_LEFT 71 | EDITTEXT IDC_NICKNAMESIZE_EDIT, 252, 161, 40, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 72 | CONTROL "", IDC_NICKNAMESIZE, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 281, 161, 11, 14, WS_EX_LEFT 73 | AUTOCHECKBOX "Nickname.Bold", IDC_NICKNAMEBOLD, 298, 158, 60, 10, 0, WS_EX_RIGHT 74 | AUTOCHECKBOX "Nickname.Italic", IDC_NICKNAMEITALIC, 296, 172, 62, 8, 0, WS_EX_RIGHT 75 | AUTOCHECKBOX "Nickname.Underline", IDC_NICKNAMEUNDERLINE, 298, 184, 60, 10, 0, WS_EX_RIGHT 76 | AUTOCHECKBOX "Nickname.UseOutline", IDC_NICKNAMEUSEOUTLINE, 19, 183, 75, 10, 0, WS_EX_LEFT 77 | RTEXT "Nickname.Opacity", IDC_STATIC, 154, 183, 70, 8, SS_RIGHT, WS_EX_LEFT 78 | EDITTEXT IDC_NICKNAMEOPACITY_EDIT, 225, 181, 40, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 79 | CONTROL "", IDC_NICKNAMEOPACITY, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 254, 181, 10, 14, WS_EX_LEFT 80 | AUTOCHECKBOX "Nickname.Outline.AutoColor", IDC_NICKNAMEOUTLINEAUTOCOLOR, 19, 202, 85, 10, 0, WS_EX_LEFT 81 | RTEXT "Nickname.Outline.Color", IDC_STATIC, 107, 203, 50, 8, SS_RIGHT, WS_EX_LEFT 82 | CONTROL "", IDC_NICKNAMEOUTLINECOLOR, "OBSColorControl", 0x50010000, 159, 200, 14, 14, 0x00000000 83 | RTEXT "Nickname.Outline.Thickness", IDC_STATIC, 174, 202, 58, 8, SS_RIGHT, WS_EX_LEFT 84 | EDITTEXT IDC_NICKNAMEOUTLINETHICKNESS_EDIT, 235, 200, 30, 14, ES_AUTOHSCROLL | ES_NUMBER, WS_EX_LEFT 85 | CONTROL "", IDC_NICKNAMEOUTLINETHICKNESS, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 254, 200, 11, 14, WS_EX_LEFT 86 | RTEXT "Nickname.Outline.Opacity", IDC_STATIC, 273, 201, 50, 8, SS_RIGHT, WS_EX_LEFT 87 | EDITTEXT IDC_NICKNAMEOUTLINEOPACITY_EDIT, 325, 199, 30, 14, ES_AUTOHSCROLL, WS_EX_LEFT 88 | CONTROL "", IDC_NICKNAMEOUTLINEOPACITY, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY | UDS_SETBUDDYINT, 344, 199, 10, 14, WS_EX_LEFT 89 | GROUPBOX "Groupbox.Connection", IDC_STATIC, 8, 223, 356, 60, 0, WS_EX_LEFT 90 | RTEXT "Connection.Server", IDC_STATIC, 15, 240, 40, 8, SS_RIGHT, WS_EX_LEFT 91 | COMBOBOX IDC_IRCSERVER, 58, 238, 90, 20, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT 92 | RTEXT "Connection.Port", IDC_STATIC, 150, 240, 30, 8, SS_RIGHT, WS_EX_LEFT 93 | COMBOBOX IDC_PORT, 181, 238, 35, 20, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT 94 | RTEXT "Connection.Channel", IDC_STATIC, 217, 240, 50, 8, SS_RIGHT, WS_EX_LEFT 95 | LTEXT "#", IDC_STATIC, 271, 240, 8, 8, SS_LEFT, WS_EX_LEFT 96 | EDITTEXT IDC_CHANNEL, 276, 238, 80, 14, ES_AUTOHSCROLL, WS_EX_LEFT 97 | AUTOCHECKBOX "Connection.UseAnonymousLogin", IDC_USEJUSTINFAN, 20, 259, 70, 10, 0, WS_EX_LEFT 98 | RTEXT "Connection.Username", IDC_STATIC, 88, 260, 50, 8, SS_RIGHT, WS_EX_LEFT 99 | EDITTEXT IDC_NICKNAME, 146, 258, 70, 14, ES_AUTOHSCROLL, WS_EX_LEFT 100 | RTEXT "Connection.Password", IDC_STATIC, 217, 260, 50, 8, SS_RIGHT, WS_EX_LEFT 101 | EDITTEXT IDC_PASSWORD, 276, 258, 80, 14, ES_AUTOHSCROLL | ES_PASSWORD, WS_EX_LEFT 102 | LTEXT "Nico Comment Plugin, Beta Test Version", IDC_STATIC, 10, 290, 125, 8, SS_LEFT, WS_EX_LEFT 103 | LTEXT "Created by Append Huang", IDC_STATIC, 10, 302, 81, 8, SS_LEFT, WS_EX_LEFT 104 | PUSHBUTTON "OK", IDOK, 261, 297, 50, 14, 0, WS_EX_LEFT 105 | PUSHBUTTON "Cancel", IDCANCEL, 315, 297, 50, 14, 0, WS_EX_LEFT 106 | } 107 | 108 | 109 | 110 | // 111 | // Version Information resources 112 | // 113 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 114 | 1 VERSIONINFO 115 | FILEVERSION PLUGIN_VERSION 116 | PRODUCTVERSION PLUGIN_VERSION 117 | FILEOS VOS_UNKNOWN 118 | FILETYPE VFT_DLL 119 | FILESUBTYPE VFT2_UNKNOWN 120 | FILEFLAGSMASK 0x00000000 121 | FILEFLAGS 0x00000000 122 | { 123 | 124 | } 125 | -------------------------------------------------------------------------------- /NicoCommentPlugin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C++ Express 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NicoCommentPlugin", "NicoCommentPlugin.vcxproj", "{54567BF8-F240-44D8-95FC-6CE4715BA743}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Debug|Win32.Build.0 = Debug|Win32 16 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Debug|x64.ActiveCfg = Debug|x64 17 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Debug|x64.Build.0 = Debug|x64 18 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Release|Win32.ActiveCfg = Release|Win32 19 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Release|Win32.Build.0 = Release|Win32 20 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Release|x64.ActiveCfg = Release|x64 21 | {54567BF8-F240-44D8-95FC-6CE4715BA743}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /NicoCommentPlugin.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Appendko/NicoCommentPlugin/9d6fa86a2945176e06595dee1013ce6cfce33a72/NicoCommentPlugin.suo -------------------------------------------------------------------------------- /NicoCommentPlugin.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {54567BF8-F240-44D8-95FC-6CE4715BA743} 23 | Win32Proj 24 | TextOutputSourcePlugin 25 | 26 | 27 | 28 | DynamicLibrary 29 | true 30 | Unicode 31 | v100 32 | 33 | 34 | DynamicLibrary 35 | true 36 | Unicode 37 | Windows7.1SDK 38 | 39 | 40 | DynamicLibrary 41 | false 42 | true 43 | Unicode 44 | Windows7.1SDK 45 | 46 | 47 | DynamicLibrary 48 | false 49 | true 50 | Unicode 51 | Windows7.1SDK 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | 72 | 73 | true 74 | 75 | 76 | false 77 | 78 | 79 | false 80 | true 81 | 82 | 83 | 84 | 85 | 86 | Level3 87 | Disabled 88 | WIN32;_DEBUG;_WINDOWS;_USRDLL;TEXTOUTPUTSOURCEPLUGIN_EXPORTS;%(PreprocessorDefinitions) 89 | D:\OBS-stable\OBSApi;%(AdditionalIncludeDirectories) 90 | 91 | 92 | Windows 93 | true 94 | D:\OBS-stable\OBSApi\Debug;%(AdditionalLibraryDirectories) 95 | OBSApi.lib;gdiplus.lib;%(AdditionalDependencies) 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_WINDOWS;_USRDLL;TEXTOUTPUTSOURCEPLUGIN_EXPORTS;%(PreprocessorDefinitions) 105 | D:\OBS-stable\OBSApi;%(AdditionalIncludeDirectories) 106 | 107 | 108 | Windows 109 | true 110 | C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64;D:\OBS-stable\OBSApi\x64\Debug;%(AdditionalLibraryDirectories) 111 | OBSApi.lib;gdiplus.lib;%(AdditionalDependencies) 112 | 113 | 114 | 115 | 116 | Level3 117 | 118 | 119 | MaxSpeed 120 | true 121 | true 122 | WIN32;NDEBUG;_WINDOWS;_USRDLL;TEXTOUTPUTSOURCEPLUGIN_EXPORTS;%(PreprocessorDefinitions) 123 | D:\OBS-stable\OBSApi;%(AdditionalIncludeDirectories) 124 | 125 | 126 | Windows 127 | true 128 | true 129 | true 130 | D:\OBS-stable\OBSApi\Release;%(AdditionalLibraryDirectories) 131 | OBSApi.lib;gdiplus.lib;%(AdditionalDependencies) 132 | UseLinkTimeCodeGeneration 133 | 134 | 135 | 136 | copy $(OutDir)$(ProjectName).dll D:\Streaming\OBS\Current\32bit\plugins 137 | 138 | 139 | 140 | 141 | Level3 142 | 143 | 144 | MaxSpeed 145 | true 146 | true 147 | WIN32;NDEBUG;_WINDOWS;_USRDLL;TEXTOUTPUTSOURCEPLUGIN_EXPORTS;%(PreprocessorDefinitions) 148 | D:\OBS-stable\OBSApi;%(AdditionalIncludeDirectories) 149 | 150 | 151 | Windows 152 | true 153 | true 154 | true 155 | D:\OBS-stable\OBSApi\x64\Release;C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64;%(AdditionalLibraryDirectories) 156 | OBSApi.lib;gdiplus.lib;%(AdditionalDependencies) 157 | 158 | 159 | copy $(OutDir)$(ProjectName).dll D:\Streaming\OBS\Current\64bit\plugins 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /NicoCommentPlugin.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 標頭檔 20 | 21 | 22 | 標頭檔 23 | 24 | 25 | 標頭檔 26 | 27 | 28 | 標頭檔 29 | 30 | 31 | 標頭檔 32 | 33 | 34 | 標頭檔 35 | 36 | 37 | 標頭檔 38 | 39 | 40 | 標頭檔 41 | 42 | 43 | 標頭檔 44 | 45 | 46 | 47 | 48 | 原始程式檔 49 | 50 | 51 | 原始程式檔 52 | 53 | 54 | 原始程式檔 55 | 56 | 57 | 原始程式檔 58 | 59 | 60 | 原始程式檔 61 | 62 | 63 | 原始程式檔 64 | 65 | 66 | 67 | 68 | 資源檔 69 | 70 | 71 | -------------------------------------------------------------------------------- /NicoCommentPlugin.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | D:\Streaming\OBS\Current\64bit\OBS.exe 5 | 6 | 7 | D:\Streaming\OBS\Current\64bit 8 | WindowsLocalDebugger 9 | 10 | 11 | D:\Streaming\OBS\Current\32bit 12 | WindowsLocalDebugger 13 | D:\Streaming\OBS\Current\32bit\OBS.exe 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nico Comment Plugin 2 | =========== 3 | A plugin of Open Broadcaster Software(OBS) to display the chat as Niconico-style comments. 4 | 5 | System requirement 6 | =========== 7 | Open Broadcaster Software. (I compiled with OBSApi in OBS Ver 0.5.5.4.) 8 | Visual C++ 2010 Runtime Redistributable package 9 | 10 | ChangeLog 11 | =========== 12 | - 2016.04.30 Version 0.1.3.7 beta 13 | - Solve the problem that the username becomes something like #FF7F50 14 | - 2016.03.30 Version 0.1.3.6 beta 15 | - Remove Justin.tv support and Port 443 16 | - Rewrite the UserColor parser to get information from TAGS 17 | - Use DisplayName instead of ID 18 | - Rebuild messages from characters (Lcomment) and draw each messages at once. 19 | 20 | - 2014.06.22 Version 0.1.3.0 beta 21 | - Fix the bug of user color map - now it updates data when a user changes his/her color. 22 | - Force LoginCheckTask() run in AliveCheckTasks() after disconnection check. 23 | 24 | - 2014.06.21 Version 0.1.2.3 alpha 25 | - Completely Removed the lag due to connecting IRCbot. Connect in a Tick(). 26 | - Nickname now have its own Font formats and colors. 27 | - Auto-determine the suitable outline color for Nicknames with Justin/Twitch Color. 28 | - Japanese translation. 29 | 30 | - 2014.06.18 Version 0.1.2.2 alpha 31 | - Improved stability 32 | - Removed all Timer functions. use Tick() in OBS to trigger the events instead. 33 | - Rewrite the Thread part to handle the status of the thread correctly. 34 | - Add the timeout time when receiving data from socket. 35 | 36 | - 2014.06.13 Version 0.1.2 alpha 37 | - Improved stability - although still buggy on accident disconnection 38 | - Add a 20sec timeout to force terminate unclosed thread while closing IRCBot. 39 | - Added an option of anonymous login. 40 | - Added the feature to display message with nickname, using the color from Twitch/Justin.tv. 41 | - Use a new randomize function to choose the row to draw the message 42 | - Redesigned a new configurations dialog, with EN and TW localizations. 43 | - Both Twitch and Justin.tv can use port 6667/443/80 now. 44 | - Fixed the bug of unable to choose the color while opening the dialog the first time . 45 | - Replaced the Vector container to Linked List to enhance the performance 46 | - Removed the SendThread and ReceivedThread in IRCBot; rewrited the IRCMsgThread to handle the sending and receiving functions. 47 | - Rewrite the StopThread function: use WaitForSingleObject function now. 48 | 49 | - 2014.06.13 Version 0.1.1 alpha 50 | - Add the function of buffering Texture - significantly improves the performance. 51 | - Now UpdateTexture() run once a second, not once a frame. 52 | - Draw the text by character, not by sentence, capable for very long sentences. 53 | - The characters which won't appear in 1 sec will not be drawn in UpdateTexture(). 54 | - Use OBS default Log() instead of any file loggings. 55 | - Lowercase the #channel string. 56 | - Ignore the message from Nightbot and Moobot. 57 | - Recalculate the scroll speed; 10 stands for 5 seconds. 58 | - Fixed the port used by JTV to be 6667 (but not shown on text) 59 | 60 | - 2014.06.11 Version 0.1 alpha 61 | - The very first version. Still lots of bugs to fix. 62 | 63 | Installation 64 | ======== 65 | Put NicoCommentPlugin.dll and NicoCommentPlugin folder into OBS's plugins folder. 66 | 67 | Licence 68 | ======== 69 | GNU GPL v2 70 | 71 | Download 72 | https://mega.co.nz/#F!MBESTYoJ!vyWh9fJ45Rbdkwf-S_fIIw 73 | 74 | Source Code 75 | ========== 76 | Nico Comment Plugin(GitHub): 77 | https://github.com/Appendko/NicoCommentPlugin 78 | 79 | Author 80 | ========== 81 | Append Huang 82 | Append@gmail.com 83 | 84 | Reference 85 | ========== 86 | The text rendering functions are based on TextSource in OBS. 87 | https://github.com/jp9000/OBS/ 88 | 89 | The basic structure of the IRCbot part is based on Fuunkao Sekai's JTChat 90 | https://github.com/fuunkaosekai/JTChat 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /README_zh-tw.md: -------------------------------------------------------------------------------- 1 | Nico Comment Plugin 2 | =========== 3 | Open Broadcaster Software(OBS)插件。把聊天室的訊息以Niconico的彈幕型式顯示在OBS的畫面中。 4 | 5 | 系統需求 6 | =========== 7 | Open Broadcaster Software. (編譯中用到OBS Ver 0.5.5.4.裡面附的OBSApi) 8 | Visual C++ 2010 可轉散發套件 9 | 10 | 更新紀錄 11 | =========== 12 | - 2016.04.30 Version 0.1.3.7 beta 13 | - 配合Twitch的標籤更新,解決ID變成色碼的問題 14 | - 2016.03.30 Version 0.1.3.6 beta 15 | - 移除對Justin.tv的支援,Port移除443 16 | - 從tags取得使用者ID顏色 17 | - 用DisplayName取代原本的ID顯示 18 | - 從字元的List重新建構出每個訊息,然後每次直接渲染整條訊息 19 | 20 | - 2014.06.22 Version 0.1.3.0 beta 21 | - 修正user color map的錯誤 - 現在他會在使用者改變顏色的時候更新map內的資料。 22 | - 強迫LoginCheckTask()在AliveCheckTasks()中執行,而且必須在異常斷線檢查之後。 23 | 24 | - 2014.06.21 Version 0.1.2.3 alpha 25 | - 完全消除啟動的延遲時間。在Tick()中才觸發連線的判定。 26 | - 暱稱現在有自己的字型設定和顏色。 27 | - 暱稱使用Justin/Twitch色彩時,可以自動選擇適合的外框線顏色。 28 | - 加上日文版介面翻譯。 29 | 30 | - 2014.06.18 Version 0.1.2.2 alpha 31 | - 穩定性大幅提升。 32 | - 完全移除掉計時器的部分。用OBS內建的Tick()函式來觸發需要計時器的事件。 33 | - 重寫執行緒的部分,確保執行緒的執行狀態。. 34 | - 從Socket接收資料的時候加上20秒的上限時間。 35 | 36 | - 2014.06.13 Version 0.1.2 alpha 37 | - 穩定性提升 - 不過在不正常斷線的時候還是有些問題。 38 | - 在關閉IRCBot的時候,加上20秒強迫關閉執行緒的判斷。 39 | - 加入匿名登入選項。 40 | - 加入顯示暱稱的選項,並且可以套用Justin/Twitch的暱稱顏色。 41 | - 用一個新的亂數產生方法去選擇合適的行來放置新訊息。 42 | - 重新製作了設定的對話框,加上英文和中文的訊息翻譯。 43 | - Twitch和Justin.tv現在都可以用 port 6667/443/80。 44 | - 修正第一次打開設定對話框時沒辦法設定顏色的問題。 45 | - 用Linked List取代原本的Vector,大幅增進效能。 46 | - 移除掉IRCBot中的SendThread和ReceivedThread,把所需要的功能直接寫進IRCMsgThread。 47 | - 重寫StopThread 的函式。用WaitForSingleObject來確保Thread結束。 48 | 49 | - 2014.06.13 Version 0.1.1 alpha 50 | - 加上緩衝材質的時間,非常大幅的提升效能。 51 | - 材質更新的頻率從每個畫格更新一次變成每秒更新一次。 52 | - 從逐句渲染改成逐字渲染,現在可以處理很長的句子。 53 | - 一秒內不會出現在畫面上的字就不會被渲染出來。 54 | - 用OBS內建的Log()來取代其他的所有檔案紀錄。 55 | - 把頻道的字串自動轉換成小寫。 56 | - 忽略 Nightbot and Moobot 的訊息 57 | - 重新計算了訊息捲動的速度;10表示字會出現在畫面上五秒 58 | - 把JTV的連線用Port鎖在6667 59 | 60 | - 2014.06.11 Version 0.1 alpha 61 | - 第一個釋出版本。有很多需要修正的地方 62 | 63 | 安裝 64 | ======== 65 | 把NicoCommentPlugin.dll和NicoCommentPlugin資料夾一起放進去plugins裡面。然後打開OBS,在來源的地方按右鍵新增就會看到"Nico彈幕插件" 66 | 67 | 授權 68 | ======== 69 | GNU GPL v2 70 | 71 | 下載 72 | https://mega.co.nz/#F!MBESTYoJ!vyWh9fJ45Rbdkwf-S_fIIw 73 | 74 | 原始碼 75 | ========== 76 | Nico Comment Plugin(GitHub): 77 | https://github.com/Appendko/NicoCommentPlugin 78 | 79 | 作者 80 | ========== 81 | 鴉片(Append Huang) 82 | Append@gmail.com 83 | 84 | 參考資料 85 | ========== 86 | 文字渲染的函示是參考OBS內建的文字來源。 87 | https://github.com/jp9000/OBS/ 88 | 89 | IRCBot的架構主要參考雖小臉世界(Fuunkao Sekai)撰寫的JTChat。 90 | https://github.com/fuunkaosekai/JTChat 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /SimpleSocket.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include "SimpleSocket.h" 21 | #include "constants.h" 22 | #pragma comment(lib, "Ws2_32.lib") 23 | #pragma once 24 | 25 | SimpleSocket::SimpleSocket() { 26 | TheSocket = INVALID_SOCKET; 27 | ptr = NULL; 28 | result = NULL; 29 | ipv4=L"0.0.0.0"; 30 | iStatus=SOCKET_CLOSED; 31 | } 32 | 33 | SimpleSocket::~SimpleSocket(){ 34 | if (TheSocket != INVALID_SOCKET) CloseSocket(); 35 | } 36 | 37 | bool SimpleSocket::InitializeSocket(std::wstring server, std::wstring port) { //0 for success, 1 for error 38 | //Initialize the variables...Maybe not neccessary 39 | TheSocket = INVALID_SOCKET; 40 | ptr = NULL; 41 | result = NULL; 42 | 43 | int iResult; 44 | iResult = WSAStartup(MAKEWORD(2,2), &wsaData); 45 | if (iResult != 0) { 46 | onSysMsg(L"WSAStartup failed: %d", iResult); 47 | return 1; 48 | iStatus=SOCKET_CLOSED; 49 | } 50 | 51 | struct addrinfoW hints; 52 | 53 | ZeroMemory( &hints, sizeof(hints) ); 54 | hints.ai_family = AF_INET; 55 | hints.ai_socktype = SOCK_STREAM; 56 | hints.ai_protocol = IPPROTO_TCP; 57 | iResult = GetAddrInfoW(server.c_str(), port.c_str(), &hints, &result); 58 | 59 | if (iResult != 0) { 60 | onSysMsg(L"GetAddrInfoW failed: %d", iResult); 61 | WSACleanup(); 62 | iStatus=SOCKET_CLOSED; 63 | return 1; 64 | } 65 | 66 | // Attempt to connect to the first address returned by 67 | // the call to GetAddrInfoW 68 | ptr=result; 69 | 70 | // Create a SOCKET for connecting to server 71 | TheSocket = socket(ptr->ai_family, ptr->ai_socktype, 72 | ptr->ai_protocol); 73 | 74 | if (TheSocket == INVALID_SOCKET) { 75 | onSysMsg(L"Error at socket(): %d", WSAGetLastError()); 76 | FreeAddrInfoW(result); 77 | WSACleanup(); 78 | iStatus=SOCKET_CLOSED; 79 | return 1; 80 | } 81 | DWORD RCVTIMEO=15000; 82 | iResult = setsockopt(TheSocket, SOL_SOCKET, SO_RCVTIMEO, (char *) &RCVTIMEO, sizeof(DWORD)); 83 | iStatus=SOCKET_INITIALIZED; 84 | return 0; 85 | } 86 | 87 | bool SimpleSocket::ConnectSocket() { //0 for success, 1 for error 88 | int iResult=SOCKET_ERROR; 89 | // try the next address returned by GetAddrInfoW if the connect call failed 90 | for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { 91 | iResult = connect( TheSocket, ptr->ai_addr, (int)ptr->ai_addrlen); 92 | /*ipv4*/ 93 | sockaddr_in* tmp_addr=(sockaddr_in*)(ptr->ai_addr); 94 | unsigned char i1=(tmp_addr->sin_addr.S_un.S_un_b.s_b1); 95 | unsigned char i2=(tmp_addr->sin_addr.S_un.S_un_b.s_b2); 96 | unsigned char i3=(tmp_addr->sin_addr.S_un.S_un_b.s_b3); 97 | unsigned char i4=(tmp_addr->sin_addr.S_un.S_un_b.s_b4); 98 | wchar_t buffer[32]=L""; 99 | swprintf_s(buffer,32,L"%d.%d.%d.%d",i1,i2,i3,i4); 100 | ipv4=std::wstring(buffer); 101 | 102 | if (iResult == SOCKET_ERROR) { //ERROR 103 | closesocket(TheSocket); 104 | TheSocket = INVALID_SOCKET; 105 | iStatus=SOCKET_CLOSED; 106 | } else if (iResult == 0) { //OK 107 | break; 108 | } 109 | } 110 | 111 | FreeAddrInfoW(result); // after success connection or tried all addrs in result, free "result" 112 | 113 | if (TheSocket == INVALID_SOCKET) { 114 | onSysMsg(L"Unable to connect to server!"); 115 | WSACleanup(); 116 | iStatus=SOCKET_CLOSED; 117 | return 1; 118 | } 119 | else { 120 | iStatus=SOCKET_CONNECTED; 121 | return 0; 122 | } 123 | 124 | } 125 | 126 | bool SimpleSocket::CloseSocket() { 127 | // shutdown the send half of the connection since no more data will be sent 128 | if(TheSocket!=INVALID_SOCKET){ 129 | int iResult = shutdown(TheSocket, SD_BOTH); 130 | if (iResult == SOCKET_ERROR) { 131 | onSysMsg(L"Closing: shutdown failed: %d", WSAGetLastError()); 132 | closesocket(TheSocket); 133 | WSACleanup(); 134 | iStatus=SOCKET_CLOSED; 135 | return 1; 136 | } 137 | } 138 | /*recv to no data ? No need since there's always new data on IRC 139 | char buffer[DEFAULT_BUFLEN]; 140 | unsigned int bufferlen=0; 141 | memset(buffer, 0, DEFAULT_BUFLEN); 142 | if(TheSocketPtr->recv_data((char*)buffer, bufferlen)*/ 143 | // cleanup 144 | closesocket(TheSocket); 145 | WSACleanup(); 146 | iStatus=SOCKET_CLOSED; 147 | return 0; 148 | } 149 | 150 | 151 | bool SimpleSocket::send_data(char* buffer, unsigned int bufferlen) {//Send some data 152 | int iResult; 153 | iResult = send(TheSocket,buffer,bufferlen,0); 154 | if (iResult == SOCKET_ERROR) { 155 | onSysMsg(L"send failed: %d", WSAGetLastError()); /*NEED CHANGE*/ 156 | closesocket(TheSocket); 157 | WSACleanup(); 158 | return 1; 159 | } 160 | // shutdown the connection for sending since no more data will be sent 161 | // the client can still use TheSocket for receiving data 162 | /* iResult = shutdown(TheSocket, SD_SEND); 163 | if (iResult == SOCKET_ERROR) { 164 | onDebugMsg(L"shutdown failed: %d\n", WSAGetLastError()); 165 | closesocket(TheSocket); 166 | WSACleanup(); 167 | } */ //do not shutdown if more data will be sent. 168 | return (iResult == SOCKET_ERROR); //true for Error, false for Normal termination. 169 | } 170 | 171 | bool SimpleSocket::recv_data(char* buffer, unsigned int &bufferlen) { 172 | int iResult; 173 | // TCP has an 64kb limit; recv for this size. buffer should be recvbuffer. 174 | memset(buffer, 0, DEFAULT_BUFLEN); 175 | iResult = recv(TheSocket, buffer, DEFAULT_BUFLEN, 0); 176 | if (iResult > 0) bufferlen=iResult; 177 | else if (iResult == SOCKET_ERROR) onSysMsg(L"recv failed: %d", WSAGetLastError()); 178 | return (iResult == SOCKET_ERROR); //true for Error, false for Normal termination. 179 | } 180 | 181 | std::wstring SimpleSocket::SocketIP() {return ipv4;} -------------------------------------------------------------------------------- /SimpleSocket.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "Log.h" 24 | #pragma comment(lib, "Ws2_32.lib") 25 | #pragma once 26 | 27 | enum Socket_Status {SOCKET_CLOSED,SOCKET_INITIALIZED,SOCKET_CONNECTED}; 28 | 29 | class SimpleSocket 30 | { 31 | SOCKET TheSocket; 32 | WSADATA wsaData; 33 | struct addrinfoW *ptr, *result; 34 | std::wstring ipv4; 35 | 36 | public: 37 | Socket_Status iStatus; 38 | 39 | SimpleSocket(); 40 | ~SimpleSocket(); 41 | bool InitializeSocket(std::wstring server, std::wstring port); 42 | bool CloseSocket(); 43 | bool ConnectSocket(); 44 | bool send_data(char* buffer, unsigned int bufferlen); 45 | bool recv_data(char* buffer, unsigned int &bufferlen); 46 | inline bool isSocketInvalid() {return (TheSocket == INVALID_SOCKET) ;} 47 | inline bool isConnected() { return !(send_data((char*)" ",sizeof(char)));}; 48 | std::wstring SocketIP(); 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /SimpleThread.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #pragma once 21 | 22 | class SimpleThread{ 23 | protected: 24 | HANDLE thread; 25 | HANDLE timer; 26 | HANDLE stopReq; 27 | bool bKillThread; 28 | 29 | virtual DWORD Run() {return 0;} 30 | static DWORD WINAPI ThreadProc(LPVOID pParam){ return static_cast(pParam)->Run(); } 31 | 32 | public: 33 | inline bool isAlive() {return (thread!=0);} 34 | SimpleThread() { 35 | thread = 0; 36 | bKillThread = true; 37 | stopReq = CreateEvent(NULL, FALSE, FALSE, NULL); 38 | } 39 | 40 | ~SimpleThread() { 41 | StopThread(); 42 | CloseHandle(stopReq); 43 | } 44 | 45 | void StartThread() { 46 | onSysMsg(L"StartThread(): Thread %d",(int)thread); 47 | if(thread==0) { 48 | //onDebugMsg(L"Begin StartThread(): Thread %d",(int)thread); 49 | bKillThread = false; 50 | thread = CreateThread(NULL, 0, ThreadProc, (LPVOID)this, 0, NULL); 51 | } 52 | } 53 | 54 | void StopThread () { 55 | bKillThread = true; 56 | onSysMsg(L"StopThread(): Thread %d",(int)thread); 57 | if(thread!=0) { 58 | //onDebugMsg(L"Begin StopThread(): Thread %d",(int)thread); 59 | DWORD retv; 60 | 61 | retv=WaitForSingleObject(thread,30000); 62 | switch(retv){ 63 | case WAIT_TIMEOUT://Failed waiting//Extremely Case 64 | case WAIT_ABANDONED: 65 | if(retv==WAIT_TIMEOUT) onDebugMsg(L"Thread Closing: TIMEOUT, FORCE TERMINATING THE THREAD"); 66 | if(retv==WAIT_ABANDONED) onDebugMsg(L"Thread Closing: ABANDONED, FORCE TERMINATING THE THREAD"); 67 | onDebugMsg(L"Thread Closing: Might Lead to Some UNSTABLITY"); 68 | DWORD exitcode; 69 | GetExitCodeThread(thread,&exitcode); 70 | TerminateThread(thread,exitcode); 71 | break; 72 | case WAIT_FAILED: 73 | if(thread==0) onSysMsg(L"Thread Closing: Already Closed, thread=%d",thread); 74 | else onDebugMsg(L"Thread Closing: FAILED, thread=%d",thread); 75 | break; 76 | case WAIT_OBJECT_0: 77 | onSysMsg(L"Thread Closing: Finished"); 78 | } 79 | onSysMsg(L"Thread Closed: RETV = 0x%08X",retv); 80 | if(thread!=0) { 81 | CloseHandle(thread); 82 | thread = 0; 83 | } 84 | } 85 | } 86 | }; 87 | 88 | 89 | -------------------------------------------------------------------------------- /constants.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #ifndef DEFAULT_BUFLEN 20 | #define DEFAULT_BUFLEN 65536 21 | #endif 22 | 23 | #ifndef UNICODE 24 | #define UNICODE 25 | #endif 26 | 27 | #ifndef DEF_IRCMSG 28 | typedef struct _ircmsg{ 29 | std::wstring user; 30 | std::wstring msg; 31 | unsigned int usercolor; 32 | } TircMsg; 33 | #define DEF_IRCMSG 34 | #endif -------------------------------------------------------------------------------- /locale/en.txt: -------------------------------------------------------------------------------- 1 | Cancel="Cancel" 2 | ClassName="Nico Comment Plugin" 3 | OK="OK" 4 | 5 | Title.NicoCommentPlugin="Nico Comment Plugin" 6 | 7 | Groupbox.Design="Layout" 8 | Design.ExtentSize="Extent Size" 9 | Design.BackgroundColor="Background Color" 10 | Design.Rows="#Rows" 11 | Design.ScrollSpeed="Scroll Speed" 12 | Design.BackgroundOpacity="Background Opacity" 13 | Design.PointFiltering="Use Point Filtering" 14 | 15 | Groupbox.Message="Chat Message" 16 | Message.Font="Font" 17 | Message.FontSize="Size" 18 | Message.Bold="Bold" 19 | Message.Italic="Italic" 20 | Message.Underline="Underline" 21 | Message.Color="Color" 22 | Message.Opacity="Opacity" 23 | Message.UseOutline="Use Outline" 24 | Message.Outline.Color="Outline Color" 25 | Message.Outline.Thickness="Outline Thickness" 26 | Message.Outline.Opacity="Outline Opacity" 27 | 28 | Groupbox.Nickname="Chat Nickname" 29 | Nickname.UseNickname="Display Nickname" 30 | Nickname.DefaultNicknameColor="Default Nickname Color" 31 | Nickname.UseNicknameColor="Use Justin/Twitch Nickname Color" 32 | Nickname.Font="Font" 33 | Nickname.FontSize="Size" 34 | Nickname.Bold="Bold" 35 | Nickname.Italic="Italic" 36 | Nickname.Underline="Underline" 37 | Nickname.UseOutline="Use Outline" 38 | Nickname.Opacity="Opacity" 39 | Nickname.Outline.AutoColor="Auto Outline Color" 40 | Nickname.Outline.Color="Outline Color" 41 | Nickname.Outline.Thickness="Outline Thickness" 42 | Nickname.Outline.Opacity="Outline Opacity" 43 | 44 | Groupbox.Connection="Connection" 45 | Connection.Server="Server" 46 | Connection.Port="Port" 47 | Connection.Channel="Channel" 48 | Connection.UseAnonymousLogin="Anonymous Login" 49 | Connection.Username="Account" 50 | Connection.Password="Password" 51 | 52 | Plugin.Description="Nico Comment Plugin for OBS. Display the chat messages in Niconico style." 53 | Plugin.Name="Nico Comment Plugin" 54 | 55 | -------------------------------------------------------------------------------- /locale/ja.txt: -------------------------------------------------------------------------------- 1 | Cancel="キャンセル" 2 | ClassName="ニコスタイル弾幕プラグイン" 3 | OK="OK" 4 | 5 | Title.NicoCommentPlugin="ニコ弾幕プラグイン" 6 | 7 | Groupbox.Design="レイアウト" 8 | Design.ExtentSize="サイズ" 9 | Design.BackgroundColor="背景色" 10 | Design.Rows="行" 11 | Design.ScrollSpeed="スクロールの速さ" 12 | Design.BackgroundOpacity="背景の不透明度" 13 | Design.PointFiltering="ポイントフィルタリング" 14 | 15 | Groupbox.Message="メッセージ" 16 | Message.Font="フォント" 17 | Message.FontSize="サイズ" 18 | Message.Bold="太字" 19 | Message.Italic="斜体" 20 | Message.Underline="下線" 21 | Message.Color="色" 22 | Message.Opacity="不透明度" 23 | Message.UseOutline="アウトライン" 24 | Message.Outline.Color="色" 25 | Message.Outline.Thickness="厚さ" 26 | Message.Outline.Opacity="不透明度" 27 | 28 | Groupbox.Nickname="ニックネーム" 29 | Nickname.UseNickname="ニックネーム" 30 | Nickname.DefaultNicknameColor="デフォルトの色" 31 | Nickname.UseNicknameColor="Justin/Twitchアカウントの色" 32 | Nickname.Font="フォント" 33 | Nickname.FontSize="サイズ" 34 | Nickname.Bold="太字" 35 | Nickname.Italic="斜体" 36 | Nickname.Underline="下線" 37 | Nickname.Opacity="不透明度" 38 | Nickname.UseOutline="アウトライン" 39 | Nickname.Outline.AutoColor="自動的に色を判断" 40 | Nickname.Outline.Color="色" 41 | Nickname.Outline.Thickness="厚さ" 42 | Nickname.Outline.Opacity="不透明度" 43 | 44 | Groupbox.Connection="接続" 45 | Connection.Server="サーバー" 46 | Connection.Port="ポート" 47 | Connection.Channel="チャンネル" 48 | Connection.UseAnonymousLogin="匿名ログイン" 49 | Connection.Username="アカウント" 50 | Connection.Password="パスワード" 51 | 52 | Plugin.Description="Twitch/Justinのチャットルームでのメッセージはニコニコのコメント弾幕のように画面に写します。" 53 | Plugin.Name="ニコスタイル弾幕プラグイン" 54 | 55 | -------------------------------------------------------------------------------- /locale/tw.txt: -------------------------------------------------------------------------------- 1 | Cancel="取消" 2 | ClassName="Nico彈幕插件" 3 | OK="確定" 4 | 5 | Title.NicoCommentPlugin="Nico彈幕插件" 6 | 7 | Groupbox.Design="版面設計" 8 | Design.ExtentSize="版面尺寸" 9 | Design.BackgroundColor="背景顏色" 10 | Design.Rows="行數" 11 | Design.ScrollSpeed="捲動速度" 12 | Design.BackgroundOpacity="背景不透明度" 13 | Design.PointFiltering="使用點過濾" 14 | 15 | Groupbox.Message="訊息" 16 | Message.Font="字形" 17 | Message.FontSize="大小" 18 | Message.Bold="粗體" 19 | Message.Italic="斜體" 20 | Message.Underline="底線" 21 | Message.Color="顏色" 22 | Message.Opacity="不透明度" 23 | Message.UseOutline="使用外框線" 24 | Message.Outline.Color="外框線顏色" 25 | Message.Outline.Thickness="外框線粗細" 26 | Message.Outline.Opacity="外框不透明度" 27 | 28 | Groupbox.Nickname="聊天帳號" 29 | Nickname.UseNickname="顯示聊天帳號" 30 | Nickname.DefaultNicknameColor="預設帳號顏色" 31 | Nickname.UseNicknameColor="使用Justin/Twitch帳號顏色" 32 | Nickname.Font="字形" 33 | Nickname.FontSize="大小" 34 | Nickname.Bold="粗體" 35 | Nickname.Italic="斜體" 36 | Nickname.Underline="底線" 37 | Nickname.Opacity="不透明度" 38 | Nickname.UseOutline="使用外框線" 39 | Nickname.Outline.AutoColor="自動外框顏色" 40 | Nickname.Outline.Color="外框線顏色" 41 | Nickname.Outline.Thickness="外框線粗細" 42 | Nickname.Outline.Opacity="外框不透明度" 43 | 44 | Groupbox.Connection="連線" 45 | Connection.Server="伺服器" 46 | Connection.Port="埠" 47 | Connection.Channel="頻道" 48 | Connection.UseAnonymousLogin="匿名登入" 49 | Connection.Username="帳號" 50 | Connection.Password="密碼" 51 | 52 | Plugin.Description="OBS用Nico彈幕插件。將Twitch/Justin上的聊天室訊息以Niconico的彈幕風格呈現在畫面上。" 53 | Plugin.Name="Nico彈幕插件" 54 | 55 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | #ifndef IDC_STATIC 2 | #define IDC_STATIC (-1) 3 | #endif 4 | #define PLUGIN_VERSION 0,1,3,7 5 | #define IDD_CONFIG 101 6 | #define IDC_EXTENTWIDTH_EDIT 1001 7 | #define IDC_EXTENTWIDTH 1002 8 | #define IDC_EXTENTHEIGHT_EDIT 1003 9 | #define IDC_EXTENTHEIGHT 1004 10 | #define IDC_NUMOFLINES_EDIT 1005 11 | #define IDC_NUMOFLINES 1006 12 | #define IDC_SCROLLSPEED_EDIT 1007 13 | #define IDC_SCROLLSPEED 1008 14 | #define IDC_BACKGROUNDCOLOR 1009 15 | #define IDC_BACKGROUNDOPACITY_EDIT 1010 16 | #define IDC_BACKGROUNDOPACITY 1011 17 | #define IDC_POINTFILTERING 1012 18 | #define IDC_FONT 1013 19 | #define IDC_TEXTSIZE_EDIT 1014 20 | #define IDC_TEXTSIZE 1015 21 | #define IDC_COLOR 1016 22 | #define IDC_TEXTOPACITY_EDIT 1017 23 | #define IDC_TEXTOPACITY 1018 24 | #define IDC_BOLD 1019 25 | #define IDC_ITALIC 1020 26 | #define IDC_UNDERLINE 1021 27 | #define IDC_USEOUTLINE 1022 28 | #define IDC_OUTLINECOLOR 1023 29 | #define IDC_OUTLINETHICKNESS_EDIT 1024 30 | #define IDC_OUTLINETHICKNESS 1025 31 | #define IDC_OUTLINEOPACITY_EDIT 1026 32 | #define IDC_OUTLINEOPACITY 1027 33 | #define IDC_USENICKNAME 1028 34 | #define IDC_NICKNAMEFALLBACKCOLOR 1029 35 | #define IDC_USENICKNAMECOLOR 1030 36 | #define IDC_NICKNAMEFONT 1031 37 | #define IDC_NICKNAMESIZE_EDIT 1032 38 | #define IDC_NICKNAMESIZE 1033 39 | #define IDC_NICKNAMEBOLD 1034 40 | #define IDC_NICKNAMEITALIC 1035 41 | #define IDC_NICKNAMEUNDERLINE 1036 42 | #define IDC_NICKNAMEUSEOUTLINE 1037 43 | #define IDC_NICKNAMEOPACITY_EDIT 1038 44 | #define IDC_NICKNAMEOPACITY 1039 45 | #define IDC_NICKNAMEOUTLINEAUTOCOLOR 1040 46 | #define IDC_NICKNAMEOUTLINECOLOR 1041 47 | #define IDC_NICKNAMEOUTLINETHICKNESS_EDIT 1042 48 | #define IDC_NICKNAMEOUTLINETHICKNESS 1043 49 | #define IDC_NICKNAMEOUTLINEOPACITY_EDIT 1044 50 | #define IDC_NICKNAMEOUTLINEOPACITY 1045 51 | #define IDC_IRCSERVER 1046 52 | #define IDC_PORT 1047 53 | #define IDC_CHANNEL 1048 54 | #define IDC_USEJUSTINFAN 1049 55 | #define IDC_NICKNAME 1050 56 | #define IDC_PASSWORD 1051 57 | -------------------------------------------------------------------------------- /wstringfunc.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "wstringfunc.h" 26 | 27 | //split from http://stackoverflow.com/questions/236129/how-to-split-a-wstring-in-c ; 28 | std::vector &split(const std::wstring &s, wchar_t delim, unsigned int maximum, std::vector &elems) { 29 | std::wstringstream ss(s); 30 | std::wstring item; 31 | 32 | while (elems.size() < maximum-1) { 33 | if(std::getline(ss, item, delim)) { 34 | if( (item.length()==0)) continue; 35 | elems.push_back(item); 36 | } 37 | else break; 38 | } 39 | if ( (unsigned int)ss.tellg() < s.length()) { 40 | unsigned int i = (unsigned int)ss.tellg(); 41 | while(i split(const std::wstring &s, wchar_t delim, unsigned int maximum) { 50 | std::vector elems; 51 | split(s, delim, maximum, elems); 52 | return elems; 53 | } 54 | 55 | std::wstring replaceFirst(std::wstring &s, 56 | std::wstring toReplace, 57 | std::wstring replaceWith) { 58 | return(s.replace(s.find(toReplace), toReplace.length(), replaceWith)); 59 | } 60 | 61 | std::wstring ToUpperString (std::wstring &s) 62 | { std::transform (s.begin(), s.end(), s.begin(), towupper); return s;} 63 | std::string ToUpperString (std::string &s) 64 | { std::transform (s.begin(), s.end(), s.begin(), toupper); return s;} 65 | std::wstring ToLowerString (std::wstring &s) 66 | { std::transform (s.begin(), s.end(), s.begin(), towlower); return s;} 67 | std::string ToLowerString (std::string &s) 68 | { std::transform (s.begin(), s.end(), s.begin(), tolower); return s;} 69 | 70 | 71 | std::string to_utf8(const wchar_t* buffer, int len) { 72 | int nChars = WideCharToMultiByte(CP_UTF8, 0, buffer, len, NULL, 0, NULL, NULL); 73 | if (nChars == 0) return ""; 74 | std::string newbuffer; 75 | newbuffer.resize(nChars); 76 | WideCharToMultiByte(CP_UTF8, 0, buffer, len, const_cast(newbuffer.c_str()), nChars, NULL, NULL); 77 | return newbuffer; 78 | } 79 | 80 | std::string to_utf8(const std::wstring& str){ 81 | return to_utf8(str.c_str(), (int)str.size()); 82 | } 83 | 84 | 85 | std::wstring from_utf8(const char* buffer, int len){ 86 | int nChars = MultiByteToWideChar(CP_UTF8, 0, buffer, len, NULL, 0); 87 | if (nChars == 0) return L""; 88 | std::wstring newbuffer; 89 | newbuffer.resize(nChars); 90 | MultiByteToWideChar(CP_UTF8, 0, buffer, len, const_cast(newbuffer.c_str()), nChars); 91 | return newbuffer; 92 | } 93 | std::wstring from_utf8(const std::string& str){ 94 | return from_utf8(str.c_str(), (int)str.size()); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /wstringfunc.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Append Huang 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include 21 | #pragma once 22 | 23 | std::vector &split(const std::wstring &s, wchar_t delim, unsigned int maximum, std::vector &elems); 24 | std::vector split(const std::wstring &s, wchar_t delim, unsigned int maximum); 25 | std::wstring replaceFirst(std::wstring &s, std::wstring toReplace, std::wstring replaceWith); 26 | std::wstring ToUpperString (std::wstring &s); 27 | std::string ToUpperString (std::string &s); 28 | std::wstring ToLowerString (std::wstring &s); 29 | std::string ToLowerString (std::string &s); 30 | 31 | 32 | std::string to_utf8(const wchar_t* buffer, int len); 33 | std::string to_utf8(const std::wstring& str); 34 | std::wstring from_utf8(const char* buffer, int len); 35 | std::wstring from_utf8(const std::string& str); --------------------------------------------------------------------------------