/indra/llcharacter/llgesture.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 374 lines · 259 code · 56 blank · 59 comment · 22 complexity · 0029f7a320b59ddc02ddb9b40bb71206 MD5 · raw file

  1. /**
  2. * @file llgesture.cpp
  3. *
  4. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  5. * Second Life Viewer Source Code
  6. * Copyright (C) 2010, Linden Research, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation;
  11. * version 2.1 of the License only.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  23. * $/LicenseInfo$
  24. */
  25. #include "linden_common.h"
  26. #include "indra_constants.h"
  27. #include "llgesture.h"
  28. #include "llendianswizzle.h"
  29. #include "message.h"
  30. #include <boost/tokenizer.hpp>
  31. // for allocating serialization buffers - these need to be updated when members change
  32. const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32);
  33. const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41;
  34. LLGesture::LLGesture()
  35. : mKey(KEY_NONE),
  36. mMask(MASK_NONE),
  37. mTrigger(),
  38. mTriggerLower(),
  39. mSoundItemID(),
  40. mAnimation(),
  41. mOutputString()
  42. { }
  43. LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger,
  44. const LLUUID &sound_item_id,
  45. const std::string &animation,
  46. const std::string &output_string)
  47. :
  48. mKey(key),
  49. mMask(mask),
  50. mTrigger(trigger),
  51. mTriggerLower(trigger),
  52. mSoundItemID(sound_item_id),
  53. mAnimation(animation),
  54. mOutputString(output_string)
  55. {
  56. mTriggerLower = utf8str_tolower(mTriggerLower);
  57. }
  58. LLGesture::LLGesture(U8 **buffer, S32 max_size)
  59. {
  60. *buffer = deserialize(*buffer, max_size);
  61. }
  62. LLGesture::LLGesture(const LLGesture &rhs)
  63. {
  64. mKey = rhs.mKey;
  65. mMask = rhs.mMask;
  66. mTrigger = rhs.mTrigger;
  67. mTriggerLower = rhs.mTriggerLower;
  68. mSoundItemID = rhs.mSoundItemID;
  69. mAnimation = rhs.mAnimation;
  70. mOutputString = rhs.mOutputString;
  71. }
  72. const LLGesture &LLGesture::operator =(const LLGesture &rhs)
  73. {
  74. mKey = rhs.mKey;
  75. mMask = rhs.mMask;
  76. mTrigger = rhs.mTrigger;
  77. mTriggerLower = rhs.mTriggerLower;
  78. mSoundItemID = rhs.mSoundItemID;
  79. mAnimation = rhs.mAnimation;
  80. mOutputString = rhs.mOutputString;
  81. return (*this);
  82. }
  83. BOOL LLGesture::trigger(KEY key, MASK mask)
  84. {
  85. llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
  86. return FALSE;
  87. }
  88. BOOL LLGesture::trigger(const std::string& trigger_string)
  89. {
  90. llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
  91. return FALSE;
  92. }
  93. // NOT endian-neutral
  94. U8 *LLGesture::serialize(U8 *buffer) const
  95. {
  96. htonmemcpy(buffer, &mKey, MVT_S8, 1);
  97. buffer += sizeof(mKey);
  98. htonmemcpy(buffer, &mMask, MVT_U32, 4);
  99. buffer += sizeof(mMask);
  100. htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16);
  101. buffer += 16;
  102. memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1); /* Flawfinder: ignore */
  103. buffer += mTrigger.length() + 1;
  104. memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1); /* Flawfinder: ignore */
  105. buffer += mAnimation.length() + 1;
  106. memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1); /* Flawfinder: ignore */
  107. buffer += mOutputString.length() + 1;
  108. return buffer;
  109. }
  110. U8 *LLGesture::deserialize(U8 *buffer, S32 max_size)
  111. {
  112. U8 *tmp = buffer;
  113. if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size)
  114. {
  115. llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl;
  116. return buffer;
  117. }
  118. htonmemcpy(&mKey, tmp, MVT_S8, 1);
  119. tmp += sizeof(mKey);
  120. htonmemcpy(&mMask, tmp, MVT_U32, 4);
  121. tmp += sizeof(mMask);
  122. htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16);
  123. tmp += 16;
  124. mTrigger.assign((char *)tmp);
  125. mTriggerLower = mTrigger;
  126. mTriggerLower = utf8str_tolower(mTriggerLower);
  127. tmp += mTrigger.length() + 1;
  128. mAnimation.assign((char *)tmp);
  129. //RN: force animation names to lower case
  130. // must do this for backwards compatibility
  131. mAnimation = utf8str_tolower(mAnimation);
  132. tmp += mAnimation.length() + 1;
  133. mOutputString.assign((char *)tmp);
  134. tmp += mOutputString.length() + 1;
  135. if (tmp > buffer + max_size)
  136. {
  137. llwarns << "Read past end of buffer, bad data!!!!" << llendl;
  138. return tmp;
  139. }
  140. return tmp;
  141. }
  142. S32 LLGesture::getMaxSerialSize()
  143. {
  144. return MAX_SERIAL_SIZE;
  145. }
  146. //---------------------------------------------------------------------
  147. // LLGestureList
  148. //---------------------------------------------------------------------
  149. LLGestureList::LLGestureList()
  150. : mList(0)
  151. {
  152. // add some gestures for debugging
  153. // LLGesture *gesture = NULL;
  154. /*
  155. gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)",
  156. SND_CHIRP, "dance2", ":-)" );
  157. mList.put(gesture);
  158. gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance",
  159. SND_OBJECT_CREATE, "dance3", "(dances)" );
  160. mList.put(gesture);
  161. gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie",
  162. LLUUID::null, "dance4", LLStringUtil::null );
  163. mList.put(gesture);
  164. gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue",
  165. LLUUID::null, "Express_Tongue_Out", LLStringUtil::null );
  166. mList.put(gesture);
  167. */
  168. }
  169. LLGestureList::~LLGestureList()
  170. {
  171. deleteAll();
  172. }
  173. void LLGestureList::deleteAll()
  174. {
  175. S32 count = mList.count();
  176. for (S32 i = 0; i < count; i++)
  177. {
  178. delete mList.get(i);
  179. }
  180. mList.reset();
  181. }
  182. // Iterates through space delimited tokens in string, triggering any gestures found.
  183. // Generates a revised string that has the found tokens replaced by their replacement strings
  184. // and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
  185. BOOL LLGestureList::triggerAndReviseString(const std::string &string, std::string* revised_string)
  186. {
  187. std::string tokenized = string;
  188. BOOL found_gestures = FALSE;
  189. BOOL first_token = TRUE;
  190. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  191. boost::char_separator<char> sep(" ");
  192. tokenizer tokens(string, sep);
  193. tokenizer::iterator token_iter;
  194. for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  195. {
  196. LLGesture* gesture = NULL;
  197. if( !found_gestures ) // Only pay attention to the first gesture in the string.
  198. {
  199. std::string cur_token_lower = *token_iter;
  200. LLStringUtil::toLower(cur_token_lower);
  201. for (S32 i = 0; i < mList.count(); i++)
  202. {
  203. gesture = mList.get(i);
  204. if (gesture->trigger(cur_token_lower))
  205. {
  206. if( !gesture->getOutputString().empty() )
  207. {
  208. if( !first_token )
  209. {
  210. revised_string->append( " " );
  211. }
  212. // Don't muck with the user's capitalization if we don't have to.
  213. const std::string& output = gesture->getOutputString();
  214. std::string output_lower = std::string(output.c_str());
  215. LLStringUtil::toLower(output_lower);
  216. if( cur_token_lower == output_lower )
  217. {
  218. revised_string->append(*token_iter);
  219. }
  220. else
  221. {
  222. revised_string->append(output);
  223. }
  224. }
  225. found_gestures = TRUE;
  226. break;
  227. }
  228. gesture = NULL;
  229. }
  230. }
  231. if( !gesture )
  232. {
  233. if( !first_token )
  234. {
  235. revised_string->append( " " );
  236. }
  237. revised_string->append( *token_iter );
  238. }
  239. first_token = FALSE;
  240. }
  241. return found_gestures;
  242. }
  243. BOOL LLGestureList::trigger(KEY key, MASK mask)
  244. {
  245. for (S32 i = 0; i < mList.count(); i++)
  246. {
  247. LLGesture* gesture = mList.get(i);
  248. if( gesture )
  249. {
  250. if (gesture->trigger(key, mask))
  251. {
  252. return TRUE;
  253. }
  254. }
  255. else
  256. {
  257. llwarns << "NULL gesture in gesture list (" << i << ")" << llendl;
  258. }
  259. }
  260. return FALSE;
  261. }
  262. // NOT endian-neutral
  263. U8 *LLGestureList::serialize(U8 *buffer) const
  264. {
  265. // a single S32 serves as the header that tells us how many to read
  266. S32 count = mList.count();
  267. htonmemcpy(buffer, &count, MVT_S32, 4);
  268. buffer += sizeof(count);
  269. for (S32 i = 0; i < count; i++)
  270. {
  271. buffer = mList[i]->serialize(buffer);
  272. }
  273. return buffer;
  274. }
  275. const S32 MAX_GESTURES = 4096;
  276. U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size)
  277. {
  278. deleteAll();
  279. S32 count;
  280. U8 *tmp = buffer;
  281. if (tmp + sizeof(count) > buffer + max_size)
  282. {
  283. llwarns << "Invalid max_size" << llendl;
  284. return buffer;
  285. }
  286. htonmemcpy(&count, tmp, MVT_S32, 4);
  287. if (count > MAX_GESTURES)
  288. {
  289. llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl;
  290. return tmp;
  291. }
  292. tmp += sizeof(count);
  293. mList.reserve_block(count);
  294. for (S32 i = 0; i < count; i++)
  295. {
  296. mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer));
  297. if (tmp - buffer > max_size)
  298. {
  299. llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl;
  300. return tmp;
  301. }
  302. }
  303. return tmp;
  304. }
  305. // this is a helper for deserialize
  306. // it gets overridden by LLViewerGestureList to create LLViewerGestures
  307. // overridden by child class to use local LLGesture implementation
  308. LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size)
  309. {
  310. return new LLGesture(buffer, max_size);
  311. }
  312. S32 LLGestureList::getMaxSerialSize()
  313. {
  314. return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize());
  315. }