diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcharacter/llgesture.cpp |
Print done when done.
Diffstat (limited to 'indra/llcharacter/llgesture.cpp')
-rw-r--r-- | indra/llcharacter/llgesture.cpp | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/indra/llcharacter/llgesture.cpp b/indra/llcharacter/llgesture.cpp new file mode 100644 index 0000000000..028c2f7fdf --- /dev/null +++ b/indra/llcharacter/llgesture.cpp @@ -0,0 +1,356 @@ +/** + * @file llgesture.cpp + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "indra_constants.h" + +#include "llgesture.h" +#include "llendianswizzle.h" +#include "message.h" +#include <boost/tokenizer.hpp> + +// for allocating serialization buffers - these need to be updated when members change +const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32); +const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41; + +LLGesture::LLGesture() +: mKey(KEY_NONE), + mMask(MASK_NONE), + mTrigger(), + mTriggerLower(), + mSoundItemID(), + mAnimation(), + mOutputString() +{ } + +LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger, + const LLUUID &sound_item_id, + const std::string &animation, + const std::string &output_string) +: + mKey(key), + mMask(mask), + mTrigger(trigger), + mTriggerLower(trigger), + mSoundItemID(sound_item_id), + mAnimation(animation), + mOutputString(output_string) +{ + mTriggerLower = utf8str_tolower(mTriggerLower); +} + +LLGesture::LLGesture(U8 **buffer, S32 max_size) +{ + *buffer = deserialize(*buffer, max_size); +} + +LLGesture::LLGesture(const LLGesture &rhs) +{ + mKey = rhs.mKey; + mMask = rhs.mMask; + mTrigger = rhs.mTrigger; + mTriggerLower = rhs.mTriggerLower; + mSoundItemID = rhs.mSoundItemID; + mAnimation = rhs.mAnimation; + mOutputString = rhs.mOutputString; +} + +const LLGesture &LLGesture::operator =(const LLGesture &rhs) +{ + mKey = rhs.mKey; + mMask = rhs.mMask; + mTrigger = rhs.mTrigger; + mTriggerLower = rhs.mTriggerLower; + mSoundItemID = rhs.mSoundItemID; + mAnimation = rhs.mAnimation; + mOutputString = rhs.mOutputString; + return (*this); +} + + +BOOL LLGesture::trigger(KEY key, MASK mask) +{ + llwarns << "Parent class trigger called: you probably didn't mean this." << llendl; + return FALSE; +} + + +BOOL LLGesture::trigger(const LLString &trigger_string) +{ + llwarns << "Parent class trigger called: you probably didn't mean this." << llendl; + return FALSE; +} + +// NOT endian-neutral +U8 *LLGesture::serialize(U8 *buffer) const +{ + htonmemcpy(buffer, &mKey, MVT_S8, 1); + buffer += sizeof(mKey); + htonmemcpy(buffer, &mMask, MVT_U32, 4); + buffer += sizeof(mMask); + htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16); + buffer += 16; + + memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1); /* Flawfinder: ignore */ + buffer += mTrigger.length() + 1; + memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1); /* Flawfinder: ignore */ + buffer += mAnimation.length() + 1; + memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1); /* Flawfinder: ignore */ + buffer += mOutputString.length() + 1; + + return buffer; +} + +U8 *LLGesture::deserialize(U8 *buffer, S32 max_size) +{ + U8 *tmp = buffer; + + if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size) + { + llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl; + return buffer; + } + + htonmemcpy(&mKey, tmp, MVT_S8, 1); + tmp += sizeof(mKey); + htonmemcpy(&mMask, tmp, MVT_U32, 4); + tmp += sizeof(mMask); + htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16); + tmp += 16; + + mTrigger.assign((char *)tmp); + mTriggerLower = mTrigger; + mTriggerLower = utf8str_tolower(mTriggerLower); + tmp += mTrigger.length() + 1; + mAnimation.assign((char *)tmp); + //RN: force animation names to lower case + // must do this for backwards compatibility + mAnimation = utf8str_tolower(mAnimation); + tmp += mAnimation.length() + 1; + mOutputString.assign((char *)tmp); + tmp += mOutputString.length() + 1; + + if (tmp > buffer + max_size) + { + llwarns << "Read past end of buffer, bad data!!!!" << llendl; + return tmp; + } + + return tmp; +} + +S32 LLGesture::getMaxSerialSize() +{ + return MAX_SERIAL_SIZE; +} + +//--------------------------------------------------------------------- +// LLGestureList +//--------------------------------------------------------------------- + +LLGestureList::LLGestureList() +: mList(0) +{ + // add some gestures for debugging +// LLGesture *gesture = NULL; +/* + gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)", + SND_CHIRP, "dance2", ":-)" ); + mList.put(gesture); + + gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance", + SND_OBJECT_CREATE, "dance3", "(dances)" ); + mList.put(gesture); + + gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie", + LLUUID::null, "dance4", LLString::null ); + mList.put(gesture); + + gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue", + LLUUID::null, "Express_Tongue_Out", LLString::null ); + mList.put(gesture); + */ +} + +LLGestureList::~LLGestureList() +{ + deleteAll(); +} + + +void LLGestureList::deleteAll() +{ + S32 count = mList.count(); + for (S32 i = 0; i < count; i++) + { + delete mList.get(i); + } + mList.reset(); +} + +// Iterates through space delimited tokens in string, triggering any gestures found. +// Generates a revised string that has the found tokens replaced by their replacement strings +// and (as a minor side effect) has multiple spaces in a row replaced by single spaces. +BOOL LLGestureList::triggerAndReviseString(const LLString &string, LLString* revised_string) +{ + LLString tokenized = string; + + BOOL found_gestures = FALSE; + BOOL first_token = TRUE; + + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep(" "); + tokenizer tokens(string, sep); + tokenizer::iterator token_iter; + + for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + LLGesture* gesture = NULL; + + if( !found_gestures ) // Only pay attention to the first gesture in the string. + { + LLString cur_token_lower = *token_iter; + LLString::toLower(cur_token_lower); + + for (S32 i = 0; i < mList.count(); i++) + { + gesture = mList.get(i); + if (gesture->trigger(cur_token_lower)) + { + if( !gesture->getOutputString().empty() ) + { + if( !first_token ) + { + revised_string->append( " " ); + } + + // Don't muck with the user's capitalization if we don't have to. + const std::string& output = gesture->getOutputString(); + LLString output_lower = LLString(output.c_str()); + LLString::toLower(output_lower); + if( cur_token_lower == output_lower ) + { + revised_string->append(*token_iter); + } + else + { + revised_string->append(output.c_str()); + } + + } + found_gestures = TRUE; + break; + } + gesture = NULL; + } + } + + if( !gesture ) + { + if( !first_token ) + { + revised_string->append( " " ); + } + revised_string->append( *token_iter ); + } + + first_token = FALSE; + } + return found_gestures; +} + + + +BOOL LLGestureList::trigger(KEY key, MASK mask) +{ + for (S32 i = 0; i < mList.count(); i++) + { + LLGesture* gesture = mList.get(i); + if( gesture ) + { + if (gesture->trigger(key, mask)) + { + return TRUE; + } + } + else + { + llwarns << "NULL gesture in gesture list (" << i << ")" << llendl + } + } + return FALSE; +} + +// NOT endian-neutral +U8 *LLGestureList::serialize(U8 *buffer) const +{ + // a single S32 serves as the header that tells us how many to read + S32 count = mList.count(); + htonmemcpy(buffer, &count, MVT_S32, 4); + buffer += sizeof(count); + + for (S32 i = 0; i < count; i++) + { + buffer = mList[i]->serialize(buffer); + } + + return buffer; +} + +const S32 MAX_GESTURES = 4096; + +U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size) +{ + deleteAll(); + + S32 count; + U8 *tmp = buffer; + + if (tmp + sizeof(count) > buffer + max_size) + { + llwarns << "Invalid max_size" << llendl; + return buffer; + } + + htonmemcpy(&count, tmp, MVT_S32, 4); + + if (count > MAX_GESTURES) + { + llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl; + return tmp; + } + + tmp += sizeof(count); + + mList.reserve_block(count); + + for (S32 i = 0; i < count; i++) + { + mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer)); + if (tmp - buffer > max_size) + { + llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl; + return tmp; + } + } + + return tmp; +} + +// this is a helper for deserialize +// it gets overridden by LLViewerGestureList to create LLViewerGestures +// overridden by child class to use local LLGesture implementation +LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size) +{ + return new LLGesture(buffer, max_size); +} + +S32 LLGestureList::getMaxSerialSize() +{ + return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize()); +} |