PageRenderTime 74ms CodeModel.GetById 20ms app.highlight 33ms RepoModel.GetById 18ms app.codeStats 0ms

/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
 26#include "linden_common.h"
 27
 28#include "indra_constants.h"
 29
 30#include "llgesture.h"
 31#include "llendianswizzle.h"
 32#include "message.h"
 33#include <boost/tokenizer.hpp>
 34
 35// for allocating serialization buffers - these need to be updated when members change
 36const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32);
 37const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41;
 38
 39LLGesture::LLGesture()
 40:	mKey(KEY_NONE),
 41	mMask(MASK_NONE),
 42	mTrigger(),
 43	mTriggerLower(),
 44	mSoundItemID(),
 45	mAnimation(),
 46	mOutputString()
 47{ }
 48
 49LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger,
 50					 const LLUUID &sound_item_id, 
 51					 const std::string &animation,
 52					 const std::string &output_string)
 53:
 54	mKey(key),
 55	mMask(mask),
 56	mTrigger(trigger),
 57	mTriggerLower(trigger),
 58	mSoundItemID(sound_item_id),
 59	mAnimation(animation),
 60	mOutputString(output_string)
 61{
 62	mTriggerLower = utf8str_tolower(mTriggerLower);
 63}
 64
 65LLGesture::LLGesture(U8 **buffer, S32 max_size)
 66{
 67	*buffer = deserialize(*buffer, max_size);
 68}
 69
 70LLGesture::LLGesture(const LLGesture &rhs)
 71{
 72	mKey			= rhs.mKey;
 73	mMask			= rhs.mMask;
 74	mTrigger		= rhs.mTrigger;
 75	mTriggerLower	= rhs.mTriggerLower;
 76	mSoundItemID	= rhs.mSoundItemID;
 77	mAnimation		= rhs.mAnimation;
 78	mOutputString	= rhs.mOutputString;
 79}
 80
 81const LLGesture &LLGesture::operator =(const LLGesture &rhs)
 82{
 83	mKey			= rhs.mKey;
 84	mMask			= rhs.mMask;
 85	mTrigger		= rhs.mTrigger;
 86	mTriggerLower	= rhs.mTriggerLower;
 87	mSoundItemID	= rhs.mSoundItemID;
 88	mAnimation		= rhs.mAnimation;
 89	mOutputString	= rhs.mOutputString;
 90	return (*this);
 91}
 92
 93
 94BOOL LLGesture::trigger(KEY key, MASK mask)
 95{
 96	llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
 97	return FALSE;
 98}
 99
100
101BOOL LLGesture::trigger(const std::string& trigger_string)
102{
103	llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
104	return FALSE;
105}
106
107// NOT endian-neutral
108U8 *LLGesture::serialize(U8 *buffer) const
109{
110	htonmemcpy(buffer, &mKey, MVT_S8, 1);
111	buffer += sizeof(mKey);
112	htonmemcpy(buffer, &mMask, MVT_U32, 4);
113	buffer += sizeof(mMask);
114	htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16);
115	buffer += 16;
116	
117	memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1);		/* Flawfinder: ignore */
118	buffer += mTrigger.length() + 1;
119	memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1);		/* Flawfinder: ignore */
120	buffer += mAnimation.length() + 1;
121	memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1);		/* Flawfinder: ignore */
122	buffer += mOutputString.length() + 1;
123
124	return buffer;
125}
126
127U8 *LLGesture::deserialize(U8 *buffer, S32 max_size)
128{
129	U8 *tmp = buffer;
130
131	if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size)
132	{
133		llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl;
134		return buffer;
135	}
136
137	htonmemcpy(&mKey, tmp, MVT_S8, 1);
138	tmp += sizeof(mKey);
139	htonmemcpy(&mMask, tmp, MVT_U32, 4);
140	tmp += sizeof(mMask);
141	htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16);
142	tmp += 16;
143	
144	mTrigger.assign((char *)tmp);
145	mTriggerLower = mTrigger;
146	mTriggerLower = utf8str_tolower(mTriggerLower);
147	tmp += mTrigger.length() + 1;
148	mAnimation.assign((char *)tmp);
149	//RN: force animation names to lower case
150	// must do this for backwards compatibility
151	mAnimation = utf8str_tolower(mAnimation);
152	tmp += mAnimation.length() + 1;
153	mOutputString.assign((char *)tmp);
154	tmp += mOutputString.length() + 1;
155
156	if (tmp > buffer + max_size)
157	{
158		llwarns << "Read past end of buffer, bad data!!!!" << llendl;
159		return tmp;
160	}
161
162	return tmp;
163}
164
165S32 LLGesture::getMaxSerialSize()
166{
167	return MAX_SERIAL_SIZE;
168}
169
170//---------------------------------------------------------------------
171// LLGestureList
172//---------------------------------------------------------------------
173
174LLGestureList::LLGestureList()
175:	mList(0)
176{
177	// add some gestures for debugging
178//	LLGesture *gesture = NULL;
179/*
180	gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)", 
181		SND_CHIRP, "dance2", ":-)" );
182	mList.put(gesture);
183
184	gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance", 
185		SND_OBJECT_CREATE, "dance3", "(dances)" );
186	mList.put(gesture);
187
188	gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie", 
189		LLUUID::null, "dance4", LLStringUtil::null );
190	mList.put(gesture);
191
192	gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue", 
193		LLUUID::null, "Express_Tongue_Out", LLStringUtil::null );
194	mList.put(gesture);
195	*/
196}
197
198LLGestureList::~LLGestureList()
199{
200	deleteAll();
201}
202
203
204void LLGestureList::deleteAll()
205{
206	S32 count = mList.count();
207	for (S32 i = 0; i < count; i++)
208	{
209		delete mList.get(i);
210	}
211	mList.reset();
212}
213
214// Iterates through space delimited tokens in string, triggering any gestures found.
215// Generates a revised string that has the found tokens replaced by their replacement strings
216// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
217BOOL LLGestureList::triggerAndReviseString(const std::string &string, std::string* revised_string)
218{
219	std::string tokenized = string;
220
221	BOOL found_gestures = FALSE;
222	BOOL first_token = TRUE;
223
224	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
225	boost::char_separator<char> sep(" ");
226	tokenizer tokens(string, sep);
227	tokenizer::iterator token_iter;
228
229	for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
230	{
231		LLGesture* gesture = NULL;
232
233		if( !found_gestures ) // Only pay attention to the first gesture in the string.
234		{
235			std::string cur_token_lower = *token_iter;
236			LLStringUtil::toLower(cur_token_lower);
237
238			for (S32 i = 0; i < mList.count(); i++)
239			{
240				gesture = mList.get(i);
241				if (gesture->trigger(cur_token_lower))
242				{
243					if( !gesture->getOutputString().empty() )
244					{
245						if( !first_token )
246						{
247							revised_string->append( " " );
248						}
249
250						// Don't muck with the user's capitalization if we don't have to.
251						const std::string& output = gesture->getOutputString();
252						std::string output_lower = std::string(output.c_str());
253						LLStringUtil::toLower(output_lower);
254						if( cur_token_lower == output_lower )
255						{
256							revised_string->append(*token_iter);
257						}
258						else
259						{
260							revised_string->append(output);
261						}
262
263					}
264					found_gestures = TRUE;
265					break;
266				}
267				gesture = NULL;
268			}
269		}
270
271		if( !gesture )
272		{
273			if( !first_token )
274			{
275				revised_string->append( " " );
276			}
277			revised_string->append( *token_iter );
278		}
279
280		first_token = FALSE;
281	}
282	return found_gestures;
283}
284
285
286
287BOOL LLGestureList::trigger(KEY key, MASK mask)
288{
289	for (S32 i = 0; i < mList.count(); i++)
290	{
291		LLGesture* gesture = mList.get(i);
292		if( gesture )
293		{
294			if (gesture->trigger(key, mask))
295			{
296				return TRUE;
297			}
298		}
299		else
300		{
301			llwarns << "NULL gesture in gesture list (" << i << ")" << llendl;
302		}
303	}
304	return FALSE;
305}
306
307// NOT endian-neutral
308U8 *LLGestureList::serialize(U8 *buffer) const
309{
310	// a single S32 serves as the header that tells us how many to read
311	S32 count = mList.count();
312	htonmemcpy(buffer, &count, MVT_S32, 4);
313	buffer += sizeof(count);
314
315	for (S32 i = 0; i < count; i++)
316	{
317		buffer = mList[i]->serialize(buffer);
318	}
319
320	return buffer;
321}
322
323const S32 MAX_GESTURES = 4096;
324
325U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size)
326{
327	deleteAll();
328
329	S32 count;
330	U8 *tmp = buffer;
331
332	if (tmp + sizeof(count) > buffer + max_size)
333	{
334		llwarns << "Invalid max_size" << llendl;
335		return buffer;
336	}
337
338	htonmemcpy(&count, tmp, MVT_S32, 4);
339
340	if (count > MAX_GESTURES)
341	{
342		llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl;
343		return tmp;
344	}
345
346	tmp += sizeof(count);
347
348	mList.reserve_block(count);
349
350	for (S32 i = 0; i < count; i++)
351	{
352		mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer));
353		if (tmp - buffer > max_size)
354		{
355			llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl;
356			return tmp;
357		}
358	}
359
360	return tmp;
361}
362
363// this is a helper for deserialize
364// it gets overridden by LLViewerGestureList to create LLViewerGestures
365// overridden by child class to use local LLGesture implementation
366LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size)
367{
368	return new LLGesture(buffer, max_size);
369}
370
371S32 LLGestureList::getMaxSerialSize()
372{
373	return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize());
374}