diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llcharacter/llgesture.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llcharacter/llgesture.cpp')
-rw-r--r-- | linden/indra/llcharacter/llgesture.cpp | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/linden/indra/llcharacter/llgesture.cpp b/linden/indra/llcharacter/llgesture.cpp new file mode 100644 index 0000000..944df78 --- /dev/null +++ b/linden/indra/llcharacter/llgesture.cpp | |||
@@ -0,0 +1,375 @@ | |||
1 | /** | ||
2 | * @file llgesture.cpp | ||
3 | * | ||
4 | * Copyright (c) 2002-2007, Linden Research, Inc. | ||
5 | * | ||
6 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
7 | * to you under the terms of the GNU General Public License, version 2.0 | ||
8 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
9 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
10 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
11 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
12 | * | ||
13 | * There are special exceptions to the terms and conditions of the GPL as | ||
14 | * it is applied to this Source Code. View the full text of the exception | ||
15 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
16 | * online at http://secondlife.com/developers/opensource/flossexception | ||
17 | * | ||
18 | * By copying, modifying or distributing this software, you acknowledge | ||
19 | * that you have read and understood your obligations described above, | ||
20 | * and agree to abide by those obligations. | ||
21 | * | ||
22 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
23 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
24 | * COMPLETENESS OR PERFORMANCE. | ||
25 | */ | ||
26 | |||
27 | #include "linden_common.h" | ||
28 | |||
29 | #include "indra_constants.h" | ||
30 | |||
31 | #include "llgesture.h" | ||
32 | #include "llendianswizzle.h" | ||
33 | #include "message.h" | ||
34 | #include <boost/tokenizer.hpp> | ||
35 | |||
36 | // for allocating serialization buffers - these need to be updated when members change | ||
37 | const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32); | ||
38 | const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41; | ||
39 | |||
40 | LLGesture::LLGesture() | ||
41 | : mKey(KEY_NONE), | ||
42 | mMask(MASK_NONE), | ||
43 | mTrigger(), | ||
44 | mTriggerLower(), | ||
45 | mSoundItemID(), | ||
46 | mAnimation(), | ||
47 | mOutputString() | ||
48 | { } | ||
49 | |||
50 | LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger, | ||
51 | const LLUUID &sound_item_id, | ||
52 | const std::string &animation, | ||
53 | const std::string &output_string) | ||
54 | : | ||
55 | mKey(key), | ||
56 | mMask(mask), | ||
57 | mTrigger(trigger), | ||
58 | mTriggerLower(trigger), | ||
59 | mSoundItemID(sound_item_id), | ||
60 | mAnimation(animation), | ||
61 | mOutputString(output_string) | ||
62 | { | ||
63 | mTriggerLower = utf8str_tolower(mTriggerLower); | ||
64 | } | ||
65 | |||
66 | LLGesture::LLGesture(U8 **buffer, S32 max_size) | ||
67 | { | ||
68 | *buffer = deserialize(*buffer, max_size); | ||
69 | } | ||
70 | |||
71 | LLGesture::LLGesture(const LLGesture &rhs) | ||
72 | { | ||
73 | mKey = rhs.mKey; | ||
74 | mMask = rhs.mMask; | ||
75 | mTrigger = rhs.mTrigger; | ||
76 | mTriggerLower = rhs.mTriggerLower; | ||
77 | mSoundItemID = rhs.mSoundItemID; | ||
78 | mAnimation = rhs.mAnimation; | ||
79 | mOutputString = rhs.mOutputString; | ||
80 | } | ||
81 | |||
82 | const LLGesture &LLGesture::operator =(const LLGesture &rhs) | ||
83 | { | ||
84 | mKey = rhs.mKey; | ||
85 | mMask = rhs.mMask; | ||
86 | mTrigger = rhs.mTrigger; | ||
87 | mTriggerLower = rhs.mTriggerLower; | ||
88 | mSoundItemID = rhs.mSoundItemID; | ||
89 | mAnimation = rhs.mAnimation; | ||
90 | mOutputString = rhs.mOutputString; | ||
91 | return (*this); | ||
92 | } | ||
93 | |||
94 | |||
95 | BOOL LLGesture::trigger(KEY key, MASK mask) | ||
96 | { | ||
97 | llwarns << "Parent class trigger called: you probably didn't mean this." << llendl; | ||
98 | return FALSE; | ||
99 | } | ||
100 | |||
101 | |||
102 | BOOL LLGesture::trigger(const LLString &trigger_string) | ||
103 | { | ||
104 | llwarns << "Parent class trigger called: you probably didn't mean this." << llendl; | ||
105 | return FALSE; | ||
106 | } | ||
107 | |||
108 | // NOT endian-neutral | ||
109 | U8 *LLGesture::serialize(U8 *buffer) const | ||
110 | { | ||
111 | htonmemcpy(buffer, &mKey, MVT_S8, 1); | ||
112 | buffer += sizeof(mKey); | ||
113 | htonmemcpy(buffer, &mMask, MVT_U32, 4); | ||
114 | buffer += sizeof(mMask); | ||
115 | htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16); | ||
116 | buffer += 16; | ||
117 | |||
118 | memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1); /* Flawfinder: ignore */ | ||
119 | buffer += mTrigger.length() + 1; | ||
120 | memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1); /* Flawfinder: ignore */ | ||
121 | buffer += mAnimation.length() + 1; | ||
122 | memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1); /* Flawfinder: ignore */ | ||
123 | buffer += mOutputString.length() + 1; | ||
124 | |||
125 | return buffer; | ||
126 | } | ||
127 | |||
128 | U8 *LLGesture::deserialize(U8 *buffer, S32 max_size) | ||
129 | { | ||
130 | U8 *tmp = buffer; | ||
131 | |||
132 | if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size) | ||
133 | { | ||
134 | llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl; | ||
135 | return buffer; | ||
136 | } | ||
137 | |||
138 | htonmemcpy(&mKey, tmp, MVT_S8, 1); | ||
139 | tmp += sizeof(mKey); | ||
140 | htonmemcpy(&mMask, tmp, MVT_U32, 4); | ||
141 | tmp += sizeof(mMask); | ||
142 | htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16); | ||
143 | tmp += 16; | ||
144 | |||
145 | mTrigger.assign((char *)tmp); | ||
146 | mTriggerLower = mTrigger; | ||
147 | mTriggerLower = utf8str_tolower(mTriggerLower); | ||
148 | tmp += mTrigger.length() + 1; | ||
149 | mAnimation.assign((char *)tmp); | ||
150 | //RN: force animation names to lower case | ||
151 | // must do this for backwards compatibility | ||
152 | mAnimation = utf8str_tolower(mAnimation); | ||
153 | tmp += mAnimation.length() + 1; | ||
154 | mOutputString.assign((char *)tmp); | ||
155 | tmp += mOutputString.length() + 1; | ||
156 | |||
157 | if (tmp > buffer + max_size) | ||
158 | { | ||
159 | llwarns << "Read past end of buffer, bad data!!!!" << llendl; | ||
160 | return tmp; | ||
161 | } | ||
162 | |||
163 | return tmp; | ||
164 | } | ||
165 | |||
166 | S32 LLGesture::getMaxSerialSize() | ||
167 | { | ||
168 | return MAX_SERIAL_SIZE; | ||
169 | } | ||
170 | |||
171 | //--------------------------------------------------------------------- | ||
172 | // LLGestureList | ||
173 | //--------------------------------------------------------------------- | ||
174 | |||
175 | LLGestureList::LLGestureList() | ||
176 | : mList(0) | ||
177 | { | ||
178 | // add some gestures for debugging | ||
179 | // LLGesture *gesture = NULL; | ||
180 | /* | ||
181 | gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)", | ||
182 | SND_CHIRP, "dance2", ":-)" ); | ||
183 | mList.put(gesture); | ||
184 | |||
185 | gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance", | ||
186 | SND_OBJECT_CREATE, "dance3", "(dances)" ); | ||
187 | mList.put(gesture); | ||
188 | |||
189 | gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie", | ||
190 | LLUUID::null, "dance4", LLString::null ); | ||
191 | mList.put(gesture); | ||
192 | |||
193 | gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue", | ||
194 | LLUUID::null, "Express_Tongue_Out", LLString::null ); | ||
195 | mList.put(gesture); | ||
196 | */ | ||
197 | } | ||
198 | |||
199 | LLGestureList::~LLGestureList() | ||
200 | { | ||
201 | deleteAll(); | ||
202 | } | ||
203 | |||
204 | |||
205 | void LLGestureList::deleteAll() | ||
206 | { | ||
207 | S32 count = mList.count(); | ||
208 | for (S32 i = 0; i < count; i++) | ||
209 | { | ||
210 | delete mList.get(i); | ||
211 | } | ||
212 | mList.reset(); | ||
213 | } | ||
214 | |||
215 | // Iterates through space delimited tokens in string, triggering any gestures found. | ||
216 | // Generates a revised string that has the found tokens replaced by their replacement strings | ||
217 | // and (as a minor side effect) has multiple spaces in a row replaced by single spaces. | ||
218 | BOOL LLGestureList::triggerAndReviseString(const LLString &string, LLString* revised_string) | ||
219 | { | ||
220 | LLString tokenized = string; | ||
221 | |||
222 | BOOL found_gestures = FALSE; | ||
223 | BOOL first_token = TRUE; | ||
224 | |||
225 | typedef boost::tokenizer<boost::char_separator<char> > tokenizer; | ||
226 | boost::char_separator<char> sep(" "); | ||
227 | tokenizer tokens(string, sep); | ||
228 | tokenizer::iterator token_iter; | ||
229 | |||
230 | for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
231 | { | ||
232 | LLGesture* gesture = NULL; | ||
233 | |||
234 | if( !found_gestures ) // Only pay attention to the first gesture in the string. | ||
235 | { | ||
236 | LLString cur_token_lower = *token_iter; | ||
237 | LLString::toLower(cur_token_lower); | ||
238 | |||
239 | for (S32 i = 0; i < mList.count(); i++) | ||
240 | { | ||
241 | gesture = mList.get(i); | ||
242 | if (gesture->trigger(cur_token_lower)) | ||
243 | { | ||
244 | if( !gesture->getOutputString().empty() ) | ||
245 | { | ||
246 | if( !first_token ) | ||
247 | { | ||
248 | revised_string->append( " " ); | ||
249 | } | ||
250 | |||
251 | // Don't muck with the user's capitalization if we don't have to. | ||
252 | const std::string& output = gesture->getOutputString(); | ||
253 | LLString output_lower = LLString(output.c_str()); | ||
254 | LLString::toLower(output_lower); | ||
255 | if( cur_token_lower == output_lower ) | ||
256 | { | ||
257 | revised_string->append(*token_iter); | ||
258 | } | ||
259 | else | ||
260 | { | ||
261 | revised_string->append(output.c_str()); | ||
262 | } | ||
263 | |||
264 | } | ||
265 | found_gestures = TRUE; | ||
266 | break; | ||
267 | } | ||
268 | gesture = NULL; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | if( !gesture ) | ||
273 | { | ||
274 | if( !first_token ) | ||
275 | { | ||
276 | revised_string->append( " " ); | ||
277 | } | ||
278 | revised_string->append( *token_iter ); | ||
279 | } | ||
280 | |||
281 | first_token = FALSE; | ||
282 | } | ||
283 | return found_gestures; | ||
284 | } | ||
285 | |||
286 | |||
287 | |||
288 | BOOL LLGestureList::trigger(KEY key, MASK mask) | ||
289 | { | ||
290 | for (S32 i = 0; i < mList.count(); i++) | ||
291 | { | ||
292 | LLGesture* gesture = mList.get(i); | ||
293 | if( gesture ) | ||
294 | { | ||
295 | if (gesture->trigger(key, mask)) | ||
296 | { | ||
297 | return TRUE; | ||
298 | } | ||
299 | } | ||
300 | else | ||
301 | { | ||
302 | llwarns << "NULL gesture in gesture list (" << i << ")" << llendl | ||
303 | } | ||
304 | } | ||
305 | return FALSE; | ||
306 | } | ||
307 | |||
308 | // NOT endian-neutral | ||
309 | U8 *LLGestureList::serialize(U8 *buffer) const | ||
310 | { | ||
311 | // a single S32 serves as the header that tells us how many to read | ||
312 | S32 count = mList.count(); | ||
313 | htonmemcpy(buffer, &count, MVT_S32, 4); | ||
314 | buffer += sizeof(count); | ||
315 | |||
316 | for (S32 i = 0; i < count; i++) | ||
317 | { | ||
318 | buffer = mList[i]->serialize(buffer); | ||
319 | } | ||
320 | |||
321 | return buffer; | ||
322 | } | ||
323 | |||
324 | const S32 MAX_GESTURES = 4096; | ||
325 | |||
326 | U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size) | ||
327 | { | ||
328 | deleteAll(); | ||
329 | |||
330 | S32 count; | ||
331 | U8 *tmp = buffer; | ||
332 | |||
333 | if (tmp + sizeof(count) > buffer + max_size) | ||
334 | { | ||
335 | llwarns << "Invalid max_size" << llendl; | ||
336 | return buffer; | ||
337 | } | ||
338 | |||
339 | htonmemcpy(&count, tmp, MVT_S32, 4); | ||
340 | |||
341 | if (count > MAX_GESTURES) | ||
342 | { | ||
343 | llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl; | ||
344 | return tmp; | ||
345 | } | ||
346 | |||
347 | tmp += sizeof(count); | ||
348 | |||
349 | mList.reserve_block(count); | ||
350 | |||
351 | for (S32 i = 0; i < count; i++) | ||
352 | { | ||
353 | mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer)); | ||
354 | if (tmp - buffer > max_size) | ||
355 | { | ||
356 | llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl; | ||
357 | return tmp; | ||
358 | } | ||
359 | } | ||
360 | |||
361 | return tmp; | ||
362 | } | ||
363 | |||
364 | // this is a helper for deserialize | ||
365 | // it gets overridden by LLViewerGestureList to create LLViewerGestures | ||
366 | // overridden by child class to use local LLGesture implementation | ||
367 | LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size) | ||
368 | { | ||
369 | return new LLGesture(buffer, max_size); | ||
370 | } | ||
371 | |||
372 | S32 LLGestureList::getMaxSerialSize() | ||
373 | { | ||
374 | return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize()); | ||
375 | } | ||