diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2018-08-11 09:02:16 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2018-08-11 09:02:16 -0400 |
commit | f6735af9315ed91a0d28804252c1351c9d4b379f (patch) | |
tree | 1b1fc36446fd12164f28a0d9b62bd2e89fd1601c | |
parent | 00839eb6350627c6272dea242b85ea24544cea33 (diff) | |
parent | 470e4b5afc7f0fd516eca9d61b95ff770adf3978 (diff) |
Automated merge with ssh://bitbucket.org/nat_linden/viewer-no-popup
67 files changed, 980 insertions, 512 deletions
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index e5089f028f..60359ca304 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -1566,9 +1566,10 @@ BOOL LLAvatarAppearance::allocateCollisionVolumes( U32 num ) delete_and_clear_array(mCollisionVolumes); mNumCollisionVolumes = 0; - mCollisionVolumes = new LLAvatarJointCollisionVolume[num]; + mCollisionVolumes = new(std::nothrow) LLAvatarJointCollisionVolume[num]; if (!mCollisionVolumes) { + LL_WARNS() << "Failed to allocate collision volumes" << LL_ENDL; return FALSE; } diff --git a/indra/llappearance/llwearabletype.cpp b/indra/llappearance/llwearabletype.cpp index 207e0c4011..cd602b43b4 100644 --- a/indra/llappearance/llwearabletype.cpp +++ b/indra/llappearance/llwearabletype.cpp @@ -94,7 +94,7 @@ LLWearableDictionary::LLWearableDictionary() addEntry(LLWearableType::WT_PHYSICS, new WearableEntry("physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE)); - addEntry(LLWearableType::WT_INVALID, new WearableEntry("invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE)); + addEntry(LLWearableType::WT_INVALID, new WearableEntry("invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_UNKNOWN, FALSE, FALSE)); addEntry(LLWearableType::WT_NONE, new WearableEntry("none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE)); } diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 77e57b14f5..6ab61689fd 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -265,9 +265,19 @@ BOOL LLVorbisDecodeState::initDecode() mInFilep = NULL; return FALSE; } - - mWAVBuffer.reserve(size_guess); - mWAVBuffer.resize(WAV_HEADER_SIZE); + + try + { + mWAVBuffer.reserve(size_guess); + mWAVBuffer.resize(WAV_HEADER_SIZE); + } + catch (std::bad_alloc) + { + LL_WARNS("AudioEngine") << "Out of memory when trying to alloc buffer: " << size_guess << LL_ENDL; + delete mInFilep; + mInFilep = NULL; + return FALSE; + } { // write the .wav format header diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index f12c64023a..330d812985 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -579,8 +579,15 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact else { anim_file_size = anim_file->getSize(); - anim_data = new U8[anim_file_size]; - success = anim_file->read(anim_data, anim_file_size); /*Flawfinder: ignore*/ + anim_data = new(std::nothrow) U8[anim_file_size]; + if (anim_data) + { + success = anim_file->read(anim_data, anim_file_size); /*Flawfinder: ignore*/ + } + else + { + LL_WARNS() << "Failed to allocate buffer: " << anim_file_size << mID << LL_ENDL; + } delete anim_file; anim_file = NULL; } diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 4304db36be..7e5a157cdf 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -95,6 +95,7 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false)); addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false)); addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false)); + addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE)); }; @@ -156,7 +157,7 @@ LLAssetType::EType LLAssetType::lookup(const std::string& type_name) return iter->first; } } - return AT_NONE; + return AT_UNKNOWN; } // static diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index b849be9f16..79ab3d7efe 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -130,7 +130,7 @@ public: // | 4. ADD TO LLViewerAssetType.cpp | // | 5. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | // +*********************************************************+ - + AT_UNKNOWN = 255, AT_NONE = -1 }; diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index dca03cfe04..1a4dd2ca99 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -889,7 +889,7 @@ void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components) bool LLImageRaw::resize(U16 width, U16 height, S8 components) { - if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components)) + if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components) && !isBufferInvalid()) { return true; } @@ -898,7 +898,7 @@ bool LLImageRaw::resize(U16 width, U16 height, S8 components) allocateDataSize(width,height,components); - return true; + return !isBufferInvalid(); } bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp index 2cdd26c22b..867b2bb47b 100644 --- a/indra/llimage/llimagebmp.cpp +++ b/indra/llimage/llimagebmp.cpp @@ -318,7 +318,7 @@ bool LLImageBMP::updateData() if( 0 != mColorPaletteColors ) { - mColorPalette = new U8[color_palette_size]; + mColorPalette = new(std::nothrow) U8[color_palette_size]; if (!mColorPalette) { LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL; @@ -344,7 +344,11 @@ bool LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time) return false; } - raw_image->resize(getWidth(), getHeight(), 3); + if (!raw_image->resize(getWidth(), getHeight(), 3)) + { + setLastError("llimagebmp failed to resize image!"); + return false; + } U8* src = mdata + mBitmapOffset; U8* dst = raw_image->getData(); diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp index 3a7319d765..36317a5ba8 100644 --- a/indra/llimage/llimagedxt.cpp +++ b/indra/llimage/llimagedxt.cpp @@ -289,7 +289,11 @@ bool LLImageDXT::decode(LLImageRaw* raw_image, F32 time) return false; } - raw_image->resize(width, height, ncomponents); + if (!raw_image->resize(width, height, ncomponents)) + { + setLastError("llImageDXT failed to resize image!"); + return false; + } memcpy(raw_image->getData(), data, image_size); /* Flawfinder: ignore */ return true; diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index 60b2d0faa5..3b1b060c02 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -29,6 +29,7 @@ #include "llimagejpeg.h" #include "llerror.h" +#include "llexception.h" jmp_buf LLImageJPEG::sSetjmpBuffer ; LLImageJPEG::LLImageJPEG(S32 quality) @@ -256,7 +257,10 @@ bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB) - raw_image->resize(getWidth(), getHeight(), getComponents()); + if (!raw_image->resize(getWidth(), getHeight(), getComponents())) + { + throw std::bad_alloc(); + } raw_image_data = raw_image->getData(); @@ -311,6 +315,13 @@ bool LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) jpeg_destroy_decompress(&cinfo); } + catch (std::bad_alloc) + { + setLastError( "Out of memory"); + jpeg_destroy_decompress(&cinfo); + return true; // done + } + catch (int) { jpeg_destroy_decompress(&cinfo); @@ -370,10 +381,11 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) // Double the buffer size; S32 new_buffer_size = self->mOutputBufferSize * 2; - U8* new_buffer = new U8[ new_buffer_size ]; + U8* new_buffer = new(std::nothrow) U8[ new_buffer_size ]; if (!new_buffer) { - LL_ERRS() << "Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )" << LL_ENDL; + self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); + LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); return false; } memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ @@ -493,7 +505,14 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) disclaimMem(mOutputBufferSize); mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024; claimMem(mOutputBufferSize); - mOutputBuffer = new U8[ mOutputBufferSize ]; + mOutputBuffer = new(std::nothrow) U8[ mOutputBufferSize ]; + if (mOutputBuffer == NULL) + { + disclaimMem(mOutputBufferSize); + mOutputBufferSize = 0; + setLastError("Failed to allocate output buffer"); + return false; + } const U8* raw_image_data = NULL; S32 row_stride = 0; diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp index a4823ed859..c4b98d8260 100644 --- a/indra/llimage/llimagepng.cpp +++ b/indra/llimage/llimagepng.cpp @@ -125,7 +125,12 @@ bool LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time) // Temporary buffer to hold the encoded image. Note: the final image // size should be much smaller due to compression. U32 bufferSize = getWidth() * getHeight() * getComponents() + 8192; - U8* tmpWriteBuffer = new U8[ bufferSize ]; + U8* tmpWriteBuffer = new(std::nothrow) U8[ bufferSize ]; + if (!tmpWriteBuffer) + { + setLastError("LLImagePNG::out of memory"); + return false; + } // Delegate actual encoding work to wrapper LLPngWrapper pngWrapper; diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp index 7c75aa1e2a..88bdae9b80 100644 --- a/indra/llimage/llimagetga.cpp +++ b/indra/llimage/llimagetga.cpp @@ -263,7 +263,7 @@ bool LLImageTGA::updateData() // only allocate memory for one if _we_ intend to use it. if ( (1 == mImageType) || (9 == mImageType) ) { - mColorMap = new U8[ color_map_bytes ]; + mColorMap = new(std::nothrow) U8[ color_map_bytes ]; if (!mColorMap) { LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL; @@ -336,7 +336,11 @@ bool LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time) // Copy everything after the header. - raw_image->resize(getWidth(), getHeight(), getComponents()); + if( !raw_image->resize(getWidth(), getHeight(), getComponents())) + { + setLastError("LLImageTGA::out of memory"); + return false; + } if( (getComponents() != 1) && (getComponents() != 3) && @@ -346,6 +350,11 @@ bool LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time) return false; } + if( raw_image->isBufferInvalid()) + { + setLastError("LLImageTGA::out of memory"); + return false; + } if( mOriginRightBit ) { @@ -395,6 +404,11 @@ bool LLImageTGA::decodeTruecolor( LLImageRaw* raw_image, bool rle, bool flipped // alpha was entirely opaque // convert to 24 bit image LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); + if (compacted_image->isBufferInvalid()) + { + success = false; + break; + } compacted_image->copy(raw_image); raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); raw_image->copy(compacted_image); @@ -411,9 +425,16 @@ bool LLImageTGA::decodeTruecolor( LLImageRaw* raw_image, bool rle, bool flipped // alpha was entirely opaque // convert to 24 bit image LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3); - compacted_image->copy(raw_image); - raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); - raw_image->copy(compacted_image); + if (compacted_image->isBufferInvalid()) + { + success = false; + } + else + { + compacted_image->copy(raw_image); + raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3); + raw_image->copy(compacted_image); + } } } @@ -1053,7 +1074,11 @@ bool LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight return false; } - raw_image->resize(getWidth(), getHeight(), getComponents()); + if( !raw_image->resize(getWidth(), getHeight(), getComponents()) ) + { + LL_ERRS() << "LLImageTGA: Failed to resize image" << LL_ENDL; + return false; + } U8* dst = raw_image->getData(); U8* src = getData() + mDataOffset; diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index eb70b78a36..f298764cc0 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -173,8 +173,11 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf // data space if (rawImage != NULL) { - rawImage->resize(static_cast<U16>(mWidth), - static_cast<U16>(mHeight), mChannels); + if (!rawImage->resize(static_cast<U16>(mWidth), + static_cast<U16>(mHeight), mChannels)) + { + LLTHROW(PngError("Failed to resize image")); + } U8 *dest = rawImage->getData(); int offset = mWidth * mChannels; @@ -207,6 +210,12 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf releaseResources(); return (FALSE); } + catch (std::bad_alloc) + { + mErrorMessage = "LLPngWrapper"; + releaseResources(); + return (FALSE); + } // Clean up and return releaseResources(); diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index d1e6807f52..7399e1bca4 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -181,7 +181,7 @@ LLInventoryType::EType LLInventoryType::defaultForAssetType(LLAssetType::EType a } else { - return IT_NONE; + return IT_UNKNOWN; } } diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h index fc3c78cf50..891ab217fd 100644 --- a/indra/llinventory/llinventorytype.h +++ b/indra/llinventory/llinventorytype.h @@ -66,6 +66,7 @@ public: IT_PERSON = 24, IT_COUNT = 25, + IT_UNKNOWN = 255, IT_NONE = -1 }; @@ -111,6 +112,7 @@ public: ICONNAME_MESH, ICONNAME_INVALID, + ICONNAME_UNKNOWN, ICONNAME_COUNT, ICONNAME_NONE = -1 }; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 89500dcc04..40217b2e80 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1262,7 +1262,8 @@ BOOL LLImageGL::createGLTexture() stop_glerror(); if (!mTexName) { - LL_ERRS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL; + LL_WARNS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL; + return FALSE; } return TRUE ; @@ -1395,7 +1396,16 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } if (!mTexName) { - LL_ERRS() << "LLImageGL::createGLTexture failed to make texture" << LL_ENDL; + if (old_name) + { + sGlobalTextureMemory -= mTextureMemory; + LLImageGL::deleteTextures(1, &old_name); + disclaimMem(mTextureMemory); + stop_glerror(); + } + + LL_WARNS() << "LLImageGL::createGLTexture failed to make texture" << LL_ENDL; + return FALSE; } if (mUseMipMaps) diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index ab20388261..f6c4dfb59d 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -85,7 +85,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} ARGS -E - copy_directory + copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/CrashReporter.nib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mac-crash-logger.app/Contents/Resources/CrashReporter.nib ) diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 487496df9a..6cf93b547e 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -377,6 +377,43 @@ bool LLFeatureManager::parseFeatureTable(std::string filename) F32 gpu_benchmark(); +#if LL_WINDOWS + +static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific + +U32 exception_benchmark_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +{ + if (code == STATUS_MSC_EXCEPTION) + { + // C++ exception, go on + return EXCEPTION_CONTINUE_SEARCH; + } + else + { + // handle it + return EXCEPTION_EXECUTE_HANDLER; + } +} + +F32 logExceptionBenchmark() +{ + // Todo: make a wrapper/class for SEH exceptions + F32 gbps = -1; + __try + { + gbps = gpu_benchmark(); + } + __except (exception_benchmark_filter(GetExceptionCode(), GetExceptionInformation())) + { + // convert to C++ styled exception + char integer_string[32]; + sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); + throw std::exception(integer_string); + } + return gbps; +} +#endif + bool LLFeatureManager::loadGPUClass() { if (!gSavedSettings.getBOOL("SkipBenchmark")) @@ -385,7 +422,11 @@ bool LLFeatureManager::loadGPUClass() F32 gbps; try { +#if LL_WINDOWS + gbps = logExceptionBenchmark(); +#else gbps = gpu_benchmark(); +#endif } catch (const std::exception& e) { @@ -400,11 +441,11 @@ bool LLFeatureManager::loadGPUClass() LL_WARNS("RenderInit") << "Unable to get an accurate benchmark; defaulting to class 3" << LL_ENDL; mGPUClass = GPU_CLASS_3; #else - if (gGLManager.mGLVersion < 2.f) + if (gGLManager.mGLVersion <= 2.f) { mGPUClass = GPU_CLASS_0; } - else if (gGLManager.mGLVersion < 3.f) + else if (gGLManager.mGLVersion <= 3.f) { mGPUClass = GPU_CLASS_1; } diff --git a/indra/newview/llfloaternotificationstabbed.cpp b/indra/newview/llfloaternotificationstabbed.cpp index 4b5fe4989a..d1679fd936 100644 --- a/indra/newview/llfloaternotificationstabbed.cpp +++ b/indra/newview/llfloaternotificationstabbed.cpp @@ -387,7 +387,8 @@ void LLFloaterNotificationsTabbed::onStoreToast(LLPanel* info_panel, LLUUID id) p.notification_name = notify->getName(); p.transaction_id = payload["transaction_id"]; p.group_id = payload["group_id"]; - p.fee = payload["fee"]; + p.fee = payload["fee"]; + p.use_offline_cap = payload["use_offline_cap"].asInteger(); p.subject = payload["subject"].asString(); p.message = payload["message"].asString(); p.sender = payload["sender_name"].asString(); diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 491671c46f..e76b3d118e 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -814,10 +814,11 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, LLSD payload; payload["transaction_id"] = session_id; - payload["group_id"] = from_id; + payload["group_id"] = from_group ? from_id : aux_id; payload["name"] = name; payload["message"] = message; payload["fee"] = membership_fee; + payload["use_offline_cap"] = session_id.isNull() && (offline == IM_OFFLINE); LLSD args; args["MESSAGE"] = message; @@ -1459,8 +1460,12 @@ void LLIMProcessing::requestOfflineMessages() // Auto-accepted inventory items may require the avatar object // to build a correct name. Likewise, inventory offers from // muted avatars require the mute list to properly mute. - if (cap_url.empty()) + if (cap_url.empty() + || gAgent.getRegionCapability("AcceptFriendship").empty() + || gAgent.getRegionCapability("AcceptGroupInvite").empty()) { + // Offline messages capability provides no session/transaction ids for message AcceptFriendship and IM_GROUP_INVITATION to work + // So make sure we have the caps before using it. requestOfflineMessagesLegacy(); } else @@ -1561,7 +1566,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) message_data["to_agent_id"].asUUID(), IM_OFFLINE, (EInstantMessage)message_data["dialog"].asInteger(), - LLUUID::null, // session id, fix this for friendship offers to work + LLUUID::null, // session id, since there is none we can only use frienship/group invite caps message_data["timestamp"].asInteger(), message_data["from_agent_name"].asString(), message_data["message"].asString(), diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 90a000c196..e4b6c3b554 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1406,7 +1406,9 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, } new_listener = new LLMeshBridge(inventory, root, uuid); break; - + case LLAssetType::AT_UNKNOWN: + new_listener = new LLUnknownItemBridge(inventory, root, uuid); + break; case LLAssetType::AT_IMAGE_TGA: case LLAssetType::AT_IMAGE_JPEG: //LL_WARNS() << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << LL_ENDL; @@ -6949,6 +6951,21 @@ const LLUUID &LLLinkFolderBridge::getFolderID() const return LLUUID::null; } +void LLUnknownItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + items.push_back(std::string("Properties")); + items.push_back(std::string("Rename")); + hide_context_entries(menu, items, disabled_items); +} + +LLUIImagePtr LLUnknownItemBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_UNKNOWN, mInvType); +} + + /******************************************************************************** ** ** BRIDGE ACTIONS @@ -7310,7 +7327,7 @@ void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_dequ menuentry_vec_t disabled_items; if (get_selection_item_uuids(selected_items, ids)) { - if (!LLAppearanceMgr::instance().canAddWearables(ids)) + if (!LLAppearanceMgr::instance().canAddWearables(ids) && canWearSelected(ids)) { disabled_items.push_back(std::string("Wearable Add")); } @@ -7318,4 +7335,17 @@ void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_dequ disable_context_entries_if_present(menu, disabled_items); } +bool LLFolderViewGroupedItemBridge::canWearSelected(uuid_vec_t item_ids) +{ + for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + LLAssetType::EType asset_type = item->getType(); + if (!item || (asset_type >= LLAssetType::AT_COUNT) || (asset_type <= LLAssetType::AT_NONE)) + { + return false; + } + } + return true; +} // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index fd5c0433b1..ce06e8fffc 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -575,6 +575,17 @@ protected: static std::string sPrefix; }; +class LLUnknownItemBridge : public LLItemBridge +{ +public: + LLUnknownItemBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLItemBridge(inventory, root, uuid) {} + virtual LLUIImagePtr getIcon() const; + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); +}; + class LLLinkFolderBridge : public LLItemBridge { public: @@ -747,6 +758,7 @@ class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel public: LLFolderViewGroupedItemBridge(); virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu); + bool canWearSelected(uuid_vec_t item_ids); }; #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index f64c39c3ad..9193613e9f 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -153,7 +153,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const // we're showing all folders, overriding filter if (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS) { - return !gInventory.isCategoryHidden(folder_id); + return true; } // when applying a filter, matching folders get their contents downloaded first diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp index 495180f087..d0df1c94d5 100644 --- a/indra/newview/llinventoryicon.cpp +++ b/indra/newview/llinventoryicon.cpp @@ -93,6 +93,7 @@ LLIconDictionary::LLIconDictionary() addEntry(LLInventoryType::ICONNAME_MESH, new IconEntry("Inv_Mesh")); addEntry(LLInventoryType::ICONNAME_INVALID, new IconEntry("Inv_Invalid")); + addEntry(LLInventoryType::ICONNAME_UNKNOWN, new IconEntry("Inv_Unknown")); addEntry(LLInventoryType::ICONNAME_NONE, new IconEntry("NONE")); } @@ -166,6 +167,8 @@ const std::string& LLInventoryIcon::getIconName(LLAssetType::EType asset_type, break; case LLAssetType::AT_MESH: idx = LLInventoryType::ICONNAME_MESH; + case LLAssetType::AT_UNKNOWN: + idx = LLInventoryType::ICONNAME_UNKNOWN; default: break; } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 76bf87cfe5..2b9607f7a2 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -342,27 +342,6 @@ LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const return category; } -bool LLInventoryModel::isCategoryHidden(const LLUUID& id) const -{ - bool res = false; - const LLViewerInventoryCategory* category = getCategory(id); - if (category) - { - LLFolderType::EType cat_type = category->getPreferredType(); - switch (cat_type) - { - case LLFolderType::FT_INBOX: - case LLFolderType::FT_OUTBOX: - case LLFolderType::FT_MARKETPLACE_LISTINGS: - res = true; - break; - default: - break; - } - } - return res; -} - S32 LLInventoryModel::getItemCount() const { return mItemMap.size(); @@ -1831,11 +1810,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) llassert(item); if(item) { - // This can happen if assettype enums from llassettype.h ever change. - // For example, there is a known backwards compatibility issue in some viewer prototypes prior to when - // the AT_LINK enum changed from 23 to 24. - if ((item->getType() == LLAssetType::AT_NONE) - || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) + if (item->getType() <= LLAssetType::AT_NONE) { LL_WARNS(LOG_INV) << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() @@ -1843,6 +1818,13 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) return; } + if (LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) + { + LL_WARNS(LOG_INV) << "Got unknown asset type for item [ name: " << item->getName() + << " type: " << item->getType() + << " inv-type: " << item->getInventoryType() << " ]." << LL_ENDL; + } + // This condition means that we tried to add a link without the baseobj being in memory. // The item will show up as a broken link. if (item->getIsBrokenLink()) @@ -2051,6 +2033,7 @@ bool LLInventoryModel::loadSkeleton( update_map_t child_counts; cat_array_t categories; item_array_t items; + changed_items_t categories_to_update; item_array_t possible_broken_links; cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. std::string inventory_filename = getInvCacheAddres(owner_id); @@ -2076,7 +2059,7 @@ bool LLInventoryModel::loadSkeleton( } } bool is_cache_obsolete = false; - if(loadFromFile(inventory_filename, categories, items, is_cache_obsolete)) + if (loadFromFile(inventory_filename, categories, items, categories_to_update, is_cache_obsolete)) { // We were able to find a cache of files. So, use what we // found to generate a set of categories we should add. We @@ -2094,6 +2077,12 @@ bool LLInventoryModel::loadSkeleton( continue; // cache corruption?? not sure why this happens -SJB } LLViewerInventoryCategory* tcat = *cit; + + if (categories_to_update.find(tcat->getUUID()) != categories_to_update.end()) + { + tcat->setVersion(NO_VERSION); + LL_WARNS() << "folder to update: " << tcat->getName() << LL_ENDL; + } // we can safely ignore anything loaded from file, but // not sent down in the skeleton. Must have been removed from inventory. @@ -2642,6 +2631,7 @@ bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const bool LLInventoryModel::loadFromFile(const std::string& filename, LLInventoryModel::cat_array_t& categories, LLInventoryModel::item_array_t& items, + LLInventoryModel::changed_items_t& cats_to_update, bool &is_cache_obsolete) { if(filename.empty()) @@ -2716,7 +2706,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } else { - items.push_back(inv_item); + if (inv_item->getType() == LLAssetType::AT_UNKNOWN) + { + cats_to_update.insert(inv_item->getParentUUID()); + } + else + { + items.push_back(inv_item); + } } } else diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 576c5e9e20..a4326aaeed 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -316,9 +316,7 @@ public: // Copy content of all folders of type "type" into folder "id" and delete/purge the empty folders // Note : This method has been designed for FT_OUTBOX (aka Merchant Outbox) but can be used for other categories void consolidateForType(const LLUUID& id, LLFolderType::EType type); - - bool isCategoryHidden(const LLUUID& id) const; - + private: mutable LLPointer<LLViewerInventoryItem> mLastItem; // cache recent lookups @@ -615,6 +613,7 @@ protected: static bool loadFromFile(const std::string& filename, cat_array_t& categories, item_array_t& items, + changed_items_t& cats_to_update, bool& is_cache_obsolete); static bool saveToFile(const std::string& filename, const cat_array_t& categories, diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 702675ad49..1481992f43 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -861,13 +861,37 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) if (!folder_view_item && parent_folder) { - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) + if (objectp->getType() <= LLAssetType::AT_NONE) + { + LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + << ((S32)objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << LL_ENDL; + return NULL; + } + + if (objectp->getType() >= LLAssetType::AT_COUNT) { - LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " - << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() - << LL_ENDL; - return NULL; + LL_WARNS() << "LLInventoryPanel::buildNewViews called with unknown objectp->mType : " + << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << LL_ENDL; + + LLInventoryItem* item = (LLInventoryItem*)objectp; + if (item) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_UNKNOWN, + LLAssetType::AT_UNKNOWN, + LLInventoryType::IT_UNKNOWN, + this, + &mInventoryViewModel, + mFolderRoot.get(), + item->getUUID(), + item->getFlags()); + + if (new_listener) + { + folder_view_item = createFolderViewItem(new_listener); + } + } } if ((objectp->getType() == LLAssetType::AT_CATEGORY) && diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index f2de8e54a0..a5bc75e6bd 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -312,38 +312,15 @@ void LLGroupInviteNotificationListItem::onClickJoinBtn() return; } - if(mParams.fee > 0) - { - LLSD args; - args["COST"] = llformat("%d", mParams.fee); - // Set the fee for next time to 0, so that we don't keep - // asking about a fee. - LLSD next_payload; - next_payload["group_id"]= mParams.group_id; - next_payload["transaction_id"]= mParams.transaction_id; - next_payload["fee"] = 0; - LLNotificationsUtil::add("JoinGroupCanAfford", args, next_payload); - } - else - { - send_improved_im(mParams.group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_ACCEPT, - mParams.transaction_id); - } + send_join_group_response(mParams.group_id, mParams.transaction_id, true, mParams.fee, mParams.use_offline_cap); + LLNotificationListItem::onClickCloseBtn(); } void LLGroupInviteNotificationListItem::onClickDeclineBtn() { - send_improved_im(mParams.group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_DECLINE, - mParams.transaction_id); + send_join_group_response(mParams.group_id, mParams.transaction_id, false, mParams.fee, mParams.use_offline_cap); + LLNotificationListItem::onClickCloseBtn(); } diff --git a/indra/newview/llnotificationlistitem.h b/indra/newview/llnotificationlistitem.h index 3dd52986b0..3d564fed0e 100644 --- a/indra/newview/llnotificationlistitem.h +++ b/indra/newview/llnotificationlistitem.h @@ -56,6 +56,7 @@ public: std::string message; std::string sender; S32 fee; + U8 use_offline_cap; LLDate time_stamp; LLDate received_time; LLSD inventory_offer; diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index a90a29a731..b2b6de94b3 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -1090,11 +1090,6 @@ void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) updates["name"] = new_name; update_inventory_item(inv_id, updates, NULL); mOutfitRenamePending.setNull(); - LLFloater* inv_floater = LLFloaterReg::getInstance("inventory"); - if (inv_floater) - { - inv_floater->closeFloater(); - } LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance"); if (appearance_floater) { @@ -1228,7 +1223,7 @@ void LLOutfitGallery::uploadOutfitImage(const std::vector<std::string>& filename LLFloaterPerms::getNextOwnerPerms("Uploads"), LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - upload_pending_name, callback, expected_upload_cost, nruserdata); + upload_pending_name, callback, expected_upload_cost, nruserdata, false); mOutfitLinkPending = outfit_id; } delete unit; diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 178b5db6c2..ce6834b4b3 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -303,6 +303,8 @@ void LLPanelGroupNotices::activate() { if(mNoticesList) mNoticesList->deleteAllItems(); + + mPrevSelectedNotice = LLUUID(); BOOL can_send = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_SEND); BOOL can_receive = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_RECEIVE); @@ -454,12 +456,18 @@ void LLPanelGroupNotices::refreshNotices() } +void LLPanelGroupNotices::clearNoticeList() +{ + mPrevSelectedNotice = mNoticesList->getStringUUIDSelectedItem(); + mNoticesList->deleteAllItems(); +} + void LLPanelGroupNotices::onClickRefreshNotices(void* data) { LL_DEBUGS() << "LLPanelGroupNotices::onClickGetPastNotices" << LL_ENDL; LLPanelGroupNotices* self = (LLPanelGroupNotices*)data; - self->mNoticesList->deleteAllItems(); + self->clearNoticeList(); LLMessageSystem* msg = gMessageSystem; msg->newMessage("GroupNoticesListRequest"); @@ -547,7 +555,6 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) LLSD row; row["id"] = id; - row["columns"][0]["column"] = "icon"; if (has_attachment) { @@ -575,7 +582,13 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) mNoticesList->setNeedsSort(save_sort); mNoticesList->updateSort(); - mNoticesList->selectFirstItem(); + if (mPanelViewNotice->getVisible()) + { + if (!mNoticesList->selectByID(mPrevSelectedNotice)) + { + mNoticesList->selectFirstItem(); + } + } } void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data) diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h index 04629b5f6b..46c8c241c6 100644 --- a/indra/newview/llpanelgroupnotices.h +++ b/indra/newview/llpanelgroupnotices.h @@ -65,6 +65,8 @@ public: void refreshNotices(); + void clearNoticeList(); + virtual void setGroupID(const LLUUID& id); private: @@ -113,6 +115,8 @@ private: LLOfferInfo* mInventoryOffer; + LLUUID mPrevSelectedNotice; + static std::map<LLUUID,LLPanelGroupNotices*> sInstances; }; diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 25cfb598e7..3665910c63 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -341,21 +341,17 @@ void LLPanelObject::getState( ) return; } - // can move or rotate only linked group with move permissions, or sub-object with move and modify perms - BOOL enable_move = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts")); - BOOL enable_scale = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && objectp->permModify(); - BOOL enable_rotate = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts")); - S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )) && (selected_count == 1); - if (LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() > 1) - { - enable_move = FALSE; - enable_scale = FALSE; - enable_rotate = FALSE; - } + bool enable_move; + bool enable_modify; + + LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); + + BOOL enable_scale = enable_modify; + BOOL enable_rotate = enable_move; // already accounts for a case of children, which needs permModify() as well LLVector3 vec; if (enable_move) diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index ddae109030..fce21fa30a 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -3676,6 +3676,39 @@ void LLSelectMgr::selectForceDelete() SEND_ONLY_ROOTS); } +BOOL LLSelectMgr::selectGetEditMoveLinksetPermissions(bool &move, bool &modify) +{ + move = true; + modify = true; + bool selecting_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++) + { + LLSelectNode* nodep = *iter; + LLViewerObject* object = nodep->getObject(); + if (!object || !nodep->mValid) + { + move = false; + modify = false; + return FALSE; + } + + LLViewerObject *root_object = object->getRootEdit(); + bool this_object_movable = false; + if (object->permMove() && !object->isPermanentEnforced() && + ((root_object == NULL) || !root_object->isPermanentEnforced()) && + (object->permModify() || selecting_linked_set)) + { + this_object_movable = true; + } + move = move && this_object_movable; + modify = modify && object->permModify(); + } + + return TRUE; +} + void LLSelectMgr::selectGetAggregateSaleInfo(U32 &num_for_sale, BOOL &is_for_sale_mixed, BOOL &is_sale_price_mixed, diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index e965dd80d5..87ac899325 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -675,6 +675,11 @@ public: // returns TRUE if all the nodes are valid. Accumulates // permissions in the parameter. BOOL selectGetPermissions(LLPermissions& perm); + + // returns TRUE if all the nodes are valid. Depends onto "edit linked" state + // Children in linksets are a bit special - they require not only move permission + // but also modify if "edit linked" is set, since you move them relative to parent + BOOL selectGetEditMoveLinksetPermissions(bool &move, bool &modify); // Get a bunch of useful sale information for the object(s) selected. // "_mixed" is true if not all objects have the same setting. diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index a486a29aa2..2503e2a5e2 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -613,7 +613,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup"); if(ctl) { - ctl->setTentative(TRUE); + ctl->setTentative(!ctl->getEnabled()); ctl->set(TRUE); } } diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index d0cff1464b..5a40af14a3 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1050,7 +1050,7 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) tid, LLAssetType::AT_TEXTURE, res_name, res_desc, 0, folder_type, inv_type, PERM_ALL, LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost)); + expected_upload_cost, !outfit_snapshot)); upload_new_resource(assetUploadInfo); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 7a4c41779a..cc02642203 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -246,6 +246,7 @@ static bool mLoginStatePastUI = false; const S32 DEFAULT_MAX_AGENT_GROUPS = 42; const S32 ALLOWED_MAX_AGENT_GROUPS = 500; +const F32 STATE_AGENT_WAIT_TIMEOUT = 240; //seconds boost::scoped_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState")); boost::scoped_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener()); @@ -1615,6 +1616,13 @@ bool idle_startup() LLStartUp::setStartupState( STATE_INVENTORY_SEND ); } display_startup(); + + if (!gAgentMovementCompleted && timeout.getElapsedTimeF32() > STATE_AGENT_WAIT_TIMEOUT) + { + LL_WARNS("AppInit") << "Backing up to login screen!" << LL_ENDL; + LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); + reset_login(); + } return FALSE; } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 6a8843cb44..5082e16685 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -609,8 +609,8 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); LL_DEBUGS("UserInput") << "hover handled by LLToolPie (inactive)" << LL_ENDL; } - else if ( (object && object->flagHandleTouch()) - || (parent && parent->flagHandleTouch())) + else if ((!object || !object->isAttachment() || object->getClickAction() != CLICK_ACTION_DISABLED) + && ((object && object->flagHandleTouch()) || (parent && parent->flagHandleTouch()))) { show_highlight = true; gViewerWindow->setCursor(UI_CURSOR_HAND); diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index ad0c1734f9..f07c14d01f 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -84,6 +84,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_PERSON, new ViewerAssetEntry(DAD_PERSON)); + addEntry(LLViewerAssetType::AT_UNKNOWN, new ViewerAssetEntry(DAD_NONE)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); }; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 4e13eceb55..97fbb8c601 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -60,7 +60,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, LLAssetType::EType assetType, std::string name, std::string description, S32 compressionInfo, LLFolderType::EType destinationType, LLInventoryType::EType inventoryType, U32 nextOWnerPerms, - U32 groupPerms, U32 everyonePerms, S32 expectedCost) : + U32 groupPerms, U32 everyonePerms, S32 expectedCost, bool showInventory) : mTransactionId(transactId), mAssetType(assetType), mName(name), @@ -72,6 +72,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, mGroupPerms(groupPerms), mEveryonePerms(everyonePerms), mExpectedUploadCost(expectedCost), + mShowInventory(showInventory), mFolderId(LLUUID::null), mItemId(LLUUID::null), mAssetId(LLAssetID::null) @@ -81,7 +82,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, LLResourceUploadInfo::LLResourceUploadInfo(std::string name, std::string description, S32 compressionInfo, LLFolderType::EType destinationType, LLInventoryType::EType inventoryType, - U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, S32 expectedCost): + U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, S32 expectedCost, bool showInventory) : mName(name), mDescription(description), mCompressionInfo(compressionInfo), @@ -91,6 +92,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(std::string name, mGroupPerms(groupPerms), mEveryonePerms(everyonePerms), mExpectedUploadCost(expectedCost), + mShowInventory(showInventory), mTransactionId(), mAssetType(LLAssetType::AT_NONE), mFolderId(LLUUID::null), @@ -112,6 +114,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLAssetID assetId, LLAssetType::EType mGroupPerms(0), mEveryonePerms(0), mExpectedUploadCost(0), + mShowInventory(true), mTransactionId(), mFolderId(LLUUID::null), mItemId(LLUUID::null) @@ -331,10 +334,11 @@ LLNewFileResourceUploadInfo::LLNewFileResourceUploadInfo( U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, - S32 expectedCost) : + S32 expectedCost, + bool show_inventory) : LLResourceUploadInfo(name, description, compressionInfo, destinationType, inventoryType, - nextOWnerPerms, groupPerms, everyonePerms, expectedCost), + nextOWnerPerms, groupPerms, everyonePerms, expectedCost, show_inventory), mFileName(fileName) { } diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h index 43e23a0d42..ee1806b782 100644 --- a/indra/newview/llviewerassetupload.h +++ b/indra/newview/llviewerassetupload.h @@ -53,7 +53,8 @@ public: U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, - S32 expectedCost); + S32 expectedCost, + bool showInventory = true); virtual ~LLResourceUploadInfo() { } @@ -79,7 +80,7 @@ public: S32 getExpectedUploadCost() const { return mExpectedUploadCost; }; virtual bool showUploadDialog() const { return true; } - virtual bool showInventoryPanel() const { return true; } + virtual bool showInventoryPanel() const { return mShowInventory; } virtual std::string getDisplayName() const; @@ -97,7 +98,8 @@ protected: U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, - S32 expectedCost); + S32 expectedCost, + bool showInventory = true); LLResourceUploadInfo( LLAssetID assetId, @@ -130,6 +132,7 @@ private: LLUUID mFolderId; LLUUID mItemId; LLAssetID mAssetId; + bool mShowInventory; }; //------------------------------------------------------------------------- @@ -146,7 +149,8 @@ public: U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, - S32 expectedCost); + S32 expectedCost, + bool show_inventory = true); virtual LLSD prepareUpload(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index f7250ffb66..ec851ddaf9 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -134,6 +134,7 @@ #include "llpathfindingmanager.h" #include "llstartup.h" #include "boost/unordered_map.hpp" +#include <boost/regex.hpp> #include "llcleanup.h" using namespace LLAvatarAppearanceDefines; @@ -8025,7 +8026,12 @@ void handle_report_bug(const LLSD& param) LLUIString url(param.asString()); LLStringUtil::format_map_t replace; - replace["[ENVIRONMENT]"] = LLURI::escape(LLAppViewer::instance()->getViewerInfoString(true)); + std::string environment = LLAppViewer::instance()->getViewerInfoString(true); + boost::regex regex; + regex.assign("</?nolink>"); + std::string stripped_env = boost::regex_replace(environment, regex, ""); + + replace["[ENVIRONMENT]"] = LLURI::escape(stripped_env); LLSLURL location_url; LLAgentUI::buildSLURL(location_url); replace["[LOCATION]"] = LLURI::escape(location_url.getSLURLString()); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index cf1c442ce9..d2a5578568 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -689,7 +689,8 @@ LLUUID upload_new_resource( const std::string& display_name, LLAssetStorage::LLStoreAssetCallback callback, S32 expected_upload_cost, - void *userdata) + void *userdata, + bool show_inventory) { LLResourceUploadInfo::ptr_t uploadInfo(new LLNewFileResourceUploadInfo( @@ -697,7 +698,7 @@ LLUUID upload_new_resource( name, desc, compression_info, destination_folder_type, inv_type, next_owner_perms, group_perms, everyone_perms, - expected_upload_cost)); + expected_upload_cost, show_inventory)); upload_new_resource(uploadInfo, callback, userdata); return LLUUID::null; diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h index 35f86f606b..4e8348b5e5 100644 --- a/indra/newview/llviewermenufile.h +++ b/indra/newview/llviewermenufile.h @@ -55,7 +55,8 @@ LLUUID upload_new_resource( const std::string& display_name, LLAssetStorage::LLStoreAssetCallback callback, S32 expected_upload_cost, - void *userdata); + void *userdata, + bool show_inventory = true); void upload_new_resource( LLResourceUploadInfo::ptr_t &uploadInfo, diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 850d455c36..a2b4a6a91d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -169,7 +169,8 @@ void accept_friendship_coro(std::string url, LLSD notification) url += "?from=" + payload["from_id"].asString(); url += "&agent_name=\"" + LLURI::escape(gAgentAvatarp->getFullname()) + "\""; - LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + LLSD data; + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, data); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -199,20 +200,20 @@ void accept_friendship_coro(std::string url, LLSD notification) void decline_friendship_coro(std::string url, LLSD notification, S32 option) { - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); - LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("friendshipResponceErrorProcessing", httpPolicy)); - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); if (url.empty()) { LL_WARNS("Friendship") << "Empty capability!" << LL_ENDL; return; } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("friendshipResponceErrorProcessing", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLSD payload = notification["payload"]; url += "?from=" + payload["from_id"].asString(); - LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + LLSD result = httpAdapter->deleteAndSuspend(httpRequest, url); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -732,6 +733,122 @@ void send_sound_trigger(const LLUUID& sound_id, F32 gain) static LLSD sSavedGroupInvite; static LLSD sSavedResponse; +void response_group_invitation_coro(std::string url, LLUUID group_id, bool notify_and_update) +{ + if (url.empty()) + { + LL_WARNS("GroupInvite") << "Empty capability!" << LL_ENDL; + return; + } + + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("responseGroupInvitation", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD payload; + payload["group"] = group_id; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, payload); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("GroupInvite") << "HTTP status, " << status.toTerseString() << + ". Group " << group_id << " invitation response processing failed." << LL_ENDL; + } + else + { + if (!result.has("success") || result["success"].asBoolean() == false) + { + LL_WARNS("GroupInvite") << "Server failed to process group " << group_id << " invitation response. " << httpResults << LL_ENDL; + } + else + { + LL_DEBUGS("GroupInvite") << "Successfully sent response to group " << group_id << " invitation" << LL_ENDL; + if (notify_and_update) + { + LLNotificationsUtil::add("JoinGroupSuccess"); + gAgent.sendAgentDataUpdateRequest(); + + LLGroupMgr::getInstance()->clearGroupData(group_id); + // refresh the floater for this group, if any. + LLGroupActions::refresh(group_id); + } + } + } +} + +void send_join_group_response(LLUUID group_id, LLUUID transaction_id, bool accept_invite, S32 fee, bool use_offline_cap, LLSD &payload) +{ + if (accept_invite && fee > 0) + { + // If there is a fee to join this group, make + // sure the user is sure they want to join. + LLSD args; + args["COST"] = llformat("%d", fee); + // Set the fee for next time to 0, so that we don't keep + // asking about a fee. + LLSD next_payload = payload; + next_payload["fee"] = 0; + LLNotificationsUtil::add("JoinGroupCanAfford", + args, + next_payload); + } + else if (use_offline_cap) + { + std::string url; + if (accept_invite) + { + url = gAgent.getRegionCapability("AcceptGroupInvite"); + } + else + { + url = gAgent.getRegionCapability("DeclineGroupInvite"); + } + + if (!url.empty()) + { + LL_DEBUGS("GroupInvite") << "Capability url: " << url << LL_ENDL; + LLCoros::instance().launch("LLMessageSystem::acceptGroupInvitation", + boost::bind(response_group_invitation_coro, url, group_id, accept_invite)); + } + else + { + // if sim has no this cap, we can do nothing - regular request will fail + LL_WARNS("GroupInvite") << "No capability, can't reply to offline invitation!" << LL_ENDL; + } + } + else + { + LL_DEBUGS("GroupInvite") << "Replying to group invite via IM message" << LL_ENDL; + + EInstantMessage type = accept_invite ? IM_GROUP_INVITATION_ACCEPT : IM_GROUP_INVITATION_DECLINE; + + send_improved_im(group_id, + std::string("name"), + std::string("message"), + IM_ONLINE, + type, + transaction_id); + } +} + +void send_join_group_response(LLUUID group_id, LLUUID transaction_id, bool accept_invite, S32 fee, bool use_offline_cap) +{ + LLSD payload; + if (accept_invite) + { + payload["group_id"] = group_id; + payload["transaction_id"] = transaction_id; + payload["fee"] = fee; + payload["use_offline_cap"] = use_offline_cap; + } + send_join_group_response(group_id, transaction_id, accept_invite, fee, use_offline_cap, payload); +} + bool join_group_response(const LLSD& notification, const LLSD& response) { // A bit of variable saving and restoring is used to deal with the case where your group list is full and you @@ -770,6 +887,7 @@ bool join_group_response(const LLSD& notification, const LLSD& response) std::string name = notification_adjusted["payload"]["name"].asString(); std::string message = notification_adjusted["payload"]["message"].asString(); S32 fee = notification_adjusted["payload"]["fee"].asInteger(); + U8 use_offline_cap = notification_adjusted["payload"]["use_offline_cap"].asInteger(); if (option == 2 && !group_id.isNull()) { @@ -798,42 +916,7 @@ bool join_group_response(const LLSD& notification, const LLSD& response) return false; } } - - if (accept_invite) - { - // If there is a fee to join this group, make - // sure the user is sure they want to join. - if (fee > 0) - { - LLSD args; - args["COST"] = llformat("%d", fee); - // Set the fee for next time to 0, so that we don't keep - // asking about a fee. - LLSD next_payload = notification_adjusted["payload"]; - next_payload["fee"] = 0; - LLNotificationsUtil::add("JoinGroupCanAfford", - args, - next_payload); - } - else - { - send_improved_im(group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_ACCEPT, - transaction_id); - } - } - else - { - send_improved_im(group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_DECLINE, - transaction_id); - } + send_join_group_response(group_id, transaction_id, accept_invite, fee, use_offline_cap, notification_adjusted["payload"]); sSavedGroupInvite[id] = LLSD::emptyMap(); sSavedResponse[id] = LLSD::emptyMap(); @@ -1740,7 +1823,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& //don't spam them if they are getting flooded if (check_offer_throttle(mFromName, true)) { - log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + log_message = "<nolink>" + chatHistory_string + "</nolink> " + LLTrans::getString("InvOfferGaveYou") + " " + getSanitizedDescription() + LLTrans::getString("."); LLSD args; args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessageTip", args); @@ -1925,7 +2008,7 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const //don't spam them if they are getting flooded if (check_offer_throttle(mFromName, true)) { - log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + log_message = "<nolink>" + chatHistory_string + "</nolink> " + LLTrans::getString("InvOfferGaveYou") + " " + getSanitizedDescription() + LLTrans::getString("."); LLSD args; args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessageTip", args); @@ -1998,6 +2081,23 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const return false; } +std::string LLOfferInfo::getSanitizedDescription() +{ + // currently we get description from server as: 'Object' ( Location ) + // object name shouldn't be shown as a hyperlink + std::string description = mDesc; + + std::size_t start = mDesc.find_first_of("'"); + std::size_t end = mDesc.find_last_of("'"); + if ((start != std::string::npos) && (end != std::string::npos)) + { + description.insert(start, "<nolink>"); + description.insert(end + 8, "</nolink>"); + } + return description; +} + + void LLOfferInfo::initRespondFunctionMap() { if(mRespondFunctions.empty()) diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index b0eaa37541..913abef2be 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -66,6 +66,11 @@ enum InventoryOfferResponse BOOL can_afford_transaction(S32 cost); void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group = FALSE, S32 trx_type = TRANS_GIFT, const std::string& desc = LLStringUtil::null); +void send_join_group_response(LLUUID group_id, + LLUUID transaction_id, + bool accept_invite, + S32 fee, + bool use_offline_cap); void process_logout_reply(LLMessageSystem* msg, void**); void process_layer_data(LLMessageSystem *mesgsys, void **user_data); @@ -257,6 +262,7 @@ public: private: void initRespondFunctionMap(); + std::string getSanitizedDescription(); typedef boost::function<bool (const LLSD&, const LLSD&)> respond_function_t; typedef std::map<std::string, respond_function_t> respond_function_map_t; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b759c2a3ab..4f0460da29 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2817,7 +2817,8 @@ void LLViewerRegion::unpackRegionHandshake() void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) { capabilityNames.append("AbuseCategories"); - //capabilityNames.append("AcceptFriendship"); + capabilityNames.append("AcceptFriendship"); + capabilityNames.append("AcceptGroupInvite"); // ReadOfflineMsgs recieved messages only!!! capabilityNames.append("AgentPreferences"); capabilityNames.append("AgentState"); capabilityNames.append("AttachmentResources"); @@ -2827,7 +2828,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ChatSessionRequest"); capabilityNames.append("CopyInventoryFromNotecard"); capabilityNames.append("CreateInventoryCategory"); - //capabilityNames.append("DeclineFriendship"); + capabilityNames.append("DeclineFriendship"); + capabilityNames.append("DeclineGroupInvite"); // ReadOfflineMsgs recieved messages only!!! capabilityNames.append("DispatchRegionInfo"); capabilityNames.append("DirectDelivery"); capabilityNames.append("EnvironmentSettings"); @@ -2878,7 +2880,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); capabilityNames.append("ProvisionVoiceAccountRequest"); - //capabilityNames.append("ReadOfflineMsgs"); + capabilityNames.append("ReadOfflineMsgs"); // Requires to respond reliably: AcceptFriendship, AcceptGroupInvite, DeclineFriendship, DeclineGroupInvite capabilityNames.append("RemoteParcelRequest"); capabilityNames.append("RenderMaterials"); capabilityNames.append("RequestTextureDownload"); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index cef19c9c2d..01ec703fe6 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -3826,37 +3826,22 @@ void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, { if( !LLSelectMgr::getInstance()->getSelection()->isEmpty() ) { - BOOL moveable_object_selected = FALSE; - BOOL all_selected_objects_move = TRUE; - BOOL all_selected_objects_modify = TRUE; - BOOL selecting_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - - for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); - iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++) - { - LLSelectNode* nodep = *iter; - LLViewerObject* object = nodep->getObject(); - LLViewerObject *root_object = (object == NULL) ? NULL : object->getRootEdit(); - BOOL this_object_movable = FALSE; - if (object->permMove() && !object->isPermanentEnforced() && - ((root_object == NULL) || !root_object->isPermanentEnforced()) && - (object->permModify() || selecting_linked_set)) - { - moveable_object_selected = TRUE; - this_object_movable = TRUE; - } - all_selected_objects_move = all_selected_objects_move && this_object_movable; - all_selected_objects_modify = all_selected_objects_modify && object->permModify(); - } + bool all_selected_objects_move; + bool all_selected_objects_modify; + // Note: This might be costly to do on each frame and when a lot of objects are selected + // we might be better off with some kind of memory for selection and/or states, consider + // optimizing, perhaps even some kind of selection generation at level of LLSelectMgr to + // make whole viewer benefit. + LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(all_selected_objects_move, all_selected_objects_modify); BOOL draw_handles = TRUE; - if (tool == LLToolCompTranslate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) + if (tool == LLToolCompTranslate::getInstance() && !all_selected_objects_move) { draw_handles = FALSE; } - if (tool == LLToolCompRotate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) + if (tool == LLToolCompRotate::getInstance() && !all_selected_objects_move) { draw_handles = FALSE; } diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index c58e98b3fb..f971554c9d 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -74,10 +74,18 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& sess LLVoiceChannel::~LLVoiceChannel() { - // Must check instance exists here, the singleton MAY have already been destroyed. - if(LLVoiceClient::instanceExists()) + if (sSuspendedVoiceChannel == this) { - LLVoiceClient::getInstance()->removeObserver(this); + sSuspendedVoiceChannel = NULL; + } + if (sCurrentVoiceChannel == this) + { + sCurrentVoiceChannel = NULL; + // Must check instance exists here, the singleton MAY have already been destroyed. + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } } sVoiceChannelMap.erase(mSessionID); diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index b816225b07..768db047a4 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -268,6 +268,12 @@ bool LLWeb::useExternalBrowser(const std::string &url) boost::match_results<std::string::const_iterator> matches; return !(boost::regex_search(uri_string, matches, pattern)); } + else + { + boost::regex pattern = boost::regex("^mailto:", boost::regex::perl | boost::regex::icase); + boost::match_results<std::string::const_iterator> matches; + return boost::regex_search(url, matches, pattern); + } return false; #endif } diff --git a/indra/newview/skins/default/textures/icons/Inv_UnknownObject.png b/indra/newview/skins/default/textures/icons/Inv_UnknownObject.png Binary files differnew file mode 100644 index 0000000000..10f2b31cb5 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Inv_UnknownObject.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index d757e39366..2540ee148d 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -324,6 +324,7 @@ with the same filename but different name <texture name="Inv_Undershirt" file_name="icons/Inv_Undershirt.png" preload="false" /> <texture name="Inv_Link" file_name="icons/Inv_Link.png" preload="false" /> <texture name="Inv_Invalid" file_name="icons/Inv_Invalid.png" preload="false" /> + <texture name="Inv_Unknown" file_name="icons/Inv_UnknownObject.png" preload="false" /> <texture name="Inv_VersionFolderClosed" file_name="icons/Inv_VersionFolderClosed.png" preload="false" /> <texture name="Inv_VersionFolderOpen" file_name="icons/Inv_VersionFolderOpen.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 3e66007627..45f14a1192 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -70,7 +70,7 @@ UI-Skalierung: [UI_SCALE] Sichtweite: [DRAW_DISTANCE] m Bandbreite: [NET_BANDWITH] kbit/s LOD-Faktor: [LOD_FACTOR] -Darstellungsqualität: [RENDER_QUALITY] / 7 +Darstellungsqualität: [RENDER_QUALITY] Erweitertes Beleuchtungsmodell: [GPU_SHADERS] Texturspeicher: [TEXTURE_MEMORY] MB Erstellungszeit VFS (Cache): [VFS_TIME] diff --git a/indra/newview/skins/default/xui/en/floater_outgoing_call.xml b/indra/newview/skins/default/xui/en/floater_outgoing_call.xml index d714cc613e..ae1fb4cccd 100644 --- a/indra/newview/skins/default/xui/en/floater_outgoing_call.xml +++ b/indra/newview/skins/default/xui/en/floater_outgoing_call.xml @@ -57,7 +57,8 @@ top="27" visible="false" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> Connecting to [CALLEE_NAME] </text> <text @@ -68,7 +69,8 @@ Connecting to [CALLEE_NAME] name="calling" top="27" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> Calling [CALLEE_NAME] </text> <text @@ -90,7 +92,8 @@ No Answer. Please try again later. name="nearby" top="27" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> You have been disconnected from [VOICE_CHANNEL_NAME]. [RECONNECT_NEARBY] </text> <text @@ -101,7 +104,8 @@ No Answer. Please try again later. name="nearby_P2P_by_other" top="27" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> Your call has ended. [RECONNECT_NEARBY] </text> <text @@ -112,7 +116,8 @@ No Answer. Please try again later. name="nearby_P2P_by_agent" top="27" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> You have ended the call. [RECONNECT_NEARBY] </text> <text @@ -123,7 +128,8 @@ No Answer. Please try again later. name="leaving" top="62" width="315" - word_wrap="true"> + word_wrap="true" + parse_urls="false"> Leaving [CURRENT_CHAT]. </text> <button diff --git a/indra/newview/skins/default/xui/en/panel_group_notify.xml b/indra/newview/skins/default/xui/en/panel_group_notify.xml index 4121acdfb0..60e5a03d51 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notify.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notify.xml @@ -43,6 +43,7 @@ layout="topleft" left_pad="10" name="title" + parse_urls="false" text_color="GroupNotifyTextColor" top="5" use_ellipses="true" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 882fbaf634..f5f4b4acab 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -53,7 +53,7 @@ UI Scaling: [UI_SCALE] Draw distance: [DRAW_DISTANCE]m Bandwidth: [NET_BANDWITH]kbit/s LOD factor: [LOD_FACTOR] -Render quality: [RENDER_QUALITY] / 7 +Render quality: [RENDER_QUALITY] Advanced Lighting Model: [GPU_SHADERS] Texture memory: [TEXTURE_MEMORY]MB VFS (cache) creation time: [VFS_TIME] diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 341c6d2fe8..8e795de2d6 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -62,7 +62,7 @@ Ajuste de escala de IU: [UI_SCALE] Distancia de dibujo: [DRAW_DISTANCE]m Ancho de banda: [NET_BANDWITH]kbit/s Factor de LOD: [LOD_FACTOR] -Calidad de renderizado: [RENDER_QUALITY] / 7 +Calidad de renderizado: [RENDER_QUALITY] Modelo de iluminación avanzado: [GPU_SHADERS] Memoria de textura: [TEXTURE_MEMORY]MB Tiempo de creación de VFS (caché): [VFS_TIME] diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index d76beee93d..23d93e57bf 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -71,7 +71,7 @@ Ajustement de la taille de la police : [FONT_SIZE_ADJUSTMENT] pts Limite d’affichage : [DRAW_DISTANCE] m Bande passante : [NET_BANDWITH] kbit/s Facteur LOD (niveau de détail) : [LOD_FACTOR] -Qualité de rendu : [RENDER_QUALITY] / 7 +Qualité de rendu : [RENDER_QUALITY] Modèle d’éclairage avancé : [GPU_SHADERS] Mémoire textures : [TEXTURE_MEMORY] Mo Durée de création VFS (cache) : [VFS_TIME] diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index ad74e16170..e2610ef057 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -68,7 +68,7 @@ Scala UI: [UI_SCALE] Distanza visualizzazione: [DRAW_DISTANCE] m Larghezza banda: [NET_BANDWITH] kbit/s Fattore livello di dettaglio: [LOD_FACTOR] -Qualità di rendering: [RENDER_QUALITY] / 7 +Qualità di rendering: [RENDER_QUALITY] Modello illuminazione avanzato: [GPU_SHADERS] Memoria texture: [TEXTURE_MEMORY] MB Data/ora creazione VFS (cache): [VFS_TIME] @@ -1505,7 +1505,7 @@ Se continui a ricevere questo messaggio, contatta l'assistenza Second Life Trascina le cartelle in questa area per metterle in vendita su [[MARKETPLACE_DASHBOARD_URL] Marketplace]. </string> <string name="InventoryItemsCount"> - ( [ITEM_COUNT] oggetti ) + ( [ITEMS_COUNT] oggetti ) </string> <string name="Marketplace Validation Warning Stock"> la cartella di magazzino deve essere inclusa in una cartella di versione diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 5ca7ddd92c..7faf463fd4 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -71,7 +71,7 @@ UI スケーリング: [UI_SCALE] 描画距離:[DRAW_DISTANCE]m 帯域幅:[NET_BANDWITH]kbit/s LOD 係数: [LOD_FACTOR] -表示品質: [RENDER_QUALITY] / 7 +表示品質: [RENDER_QUALITY] 高度なライティングモデル: [GPU_SHADERS] テクスチャメモリ: [TEXTURE_MEMORY]MB VFS(キャッシュ)作成時間: [VFS_TIME] diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index ee048e28e3..046e7db47c 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -62,7 +62,7 @@ Escala de interface: [UI_SCALE] Dist. máxima: [DRAW_DISTANCE]m Largura de banda: [NET_BANDWITH]kbit/s Fator LOD: [LOD_FACTOR] -Qualidade de renderização: [RENDER_QUALITY] / 7 +Qualidade de renderização: [RENDER_QUALITY] Modelo de iluminação avançado: [GPU_SHADERS] Memória de textura: [TEXTURE_MEMORY]MB Tempo de criação de VFS (cache): [VFS_TIME] diff --git a/indra/newview/skins/default/xui/ru/floater_tos.xml b/indra/newview/skins/default/xui/ru/floater_tos.xml index 3f2b5747d5..7196a04de1 100644 --- a/indra/newview/skins/default/xui/ru/floater_tos.xml +++ b/indra/newview/skins/default/xui/ru/floater_tos.xml @@ -16,6 +16,6 @@ <text name="agree_list"> Я прочитал и согласен с Условиями и положениями по конфиденциальности Пользовательского соглашения, включая требования по разрешению разногласий Second Life. </text> - <button label="Продолжить" label_selected="Продолжить" name="Continue"/> + <button label="Продолжить" label_selected="Продолжить" name="Continue" top_delta="45"/> <button label="Отмена" label_selected="Отмена" name="Cancel"/> </floater> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 95225da7d0..267c717189 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -71,7 +71,7 @@ SLURL: <nolink>[SLURL]</nolink> Дальность отрисовки: [DRAW_DISTANCE] м Ширина канала: [NET_BANDWITH] кбит/с Коэффициент детализации: [LOD_FACTOR] -Качество визуализации: [RENDER_QUALITY] / 7 +Качество визуализации: [RENDER_QUALITY] Расширенная модель освещения: [GPU_SHADERS] Память текстур: [TEXTURE_MEMORY] МБ Время создания VFS (кэш): [VFS_TIME] diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 6850c67df3..6542e61475 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -71,7 +71,7 @@ Kullanıcı Arayüzü Ölçekleme: [UI_SCALE] Çizme mesafesi: [DRAW_DISTANCE] m Bant genişliği: [NET_BANDWITH] kbit/sn Ayrıntı seviyesi faktörü: [LOD_FACTOR] -İşleme kalitesi: [RENDER_QUALITY] / 7 +İşleme kalitesi: [RENDER_QUALITY] Gelişmiş Aydınlatma Modeli: [GPU_SHADERS] Doku belleği: [TEXTURE_MEMORY]MB VFS (önbellek) oluşturma zamanı: [VFS_TIME] diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index e4f9c5d433..24d8dc60cb 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -71,7 +71,7 @@ 描繪距離:[DRAW_DISTANCE]公尺 頻寬:[NET_BANDWITH]千位元/秒 細節層次率:[LOD_FACTOR] -呈像品質:[RENDER_QUALITY] / 7 +呈像品質:[RENDER_QUALITY] 進階照明模型:[GPU_SHADERS] 材質記憶體:[TEXTURE_MEMORY]MB VFS(快取)建立時間:[VFS_TIME] diff --git a/scripts/content_tools/anim_tool.py b/scripts/content_tools/anim_tool.py index 77bf731ae6..3496617b21 100644 --- a/scripts/content_tools/anim_tool.py +++ b/scripts/content_tools/anim_tool.py @@ -1,14 +1,22 @@ -#!runpy.sh - +#!/usr/bin/python """\ - -This module contains tools for manipulating the .anim files supported -for Second Life animation upload. Note that this format is unrelated -to any non-Second Life formats of the same name. - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ +@file anim_tool.py +@author Brad Payne, Nat Goodspeed +@date 2015-09-15 +@brief This module contains tools for manipulating the .anim files supported + for Second Life animation upload. Note that this format is unrelated + to any non-Second Life formats of the same name. + + This code is a Python translation of the logic in + LLKeyframeMotion::serialize() and deserialize(): + https://bitbucket.org/lindenlab/viewer-release/src/827a910542a9af0a39b0ca03663c02e5c83869ea/indra/llcharacter/llkeyframemotion.cpp?at=default&fileviewer=file-view-default#llkeyframemotion.cpp-1864 + https://bitbucket.org/lindenlab/viewer-release/src/827a910542a9af0a39b0ca03663c02e5c83869ea/indra/llcharacter/llkeyframemotion.cpp?at=default&fileviewer=file-view-default#llkeyframemotion.cpp-1220 + save that there is no support for old-style .anim files, permitting + simpler code. + +$LicenseInfo:firstyear=2015&license=viewerlgpl$ Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. +Copyright (C) 2015, Linden Research, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -28,63 +36,85 @@ Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA $/LicenseInfo$ """ -import sys -import os -import struct -import StringIO import math -import argparse +import os import random -from lxml import etree +from cStringIO import StringIO +import struct +import sys +from xml.etree import ElementTree + +class Error(Exception): + pass + +class BadFormat(Error): + """ + Something went wrong trying to read the specified .anim file. + """ + pass + +class ExtraneousData(BadFormat): + """ + Specifically, the .anim file in question contains more data than needed. + This could happen if the file isn't a .anim at all, and it 'just happens' + to read properly otherwise -- e.g. a block of all zero bytes could look + like empty name strings, empty arrays etc. That could be a legitimate + error -- or it could be due to a sloppy tool. Break this exception out + separately so caller can distinguish if desired. + """ + pass U16MAX = 65535 -OOU16MAX = 1.0/(float)(U16MAX) +# One Over U16MAX, for scaling +OOU16MAX = 1.0/float(U16MAX) LL_MAX_PELVIS_OFFSET = 5.0 class FilePacker(object): def __init__(self): - self.data = StringIO.StringIO() - self.offset = 0 + self.buffer = StringIO() def write(self,filename): - f = open(filename,"wb") - f.write(self.data.getvalue()) - f.close() + with open(filename,"wb") as f: + f.write(self.buffer.getvalue()) def pack(self,fmt,*args): buf = struct.pack(fmt, *args) - self.offset += struct.calcsize(fmt) - self.data.write(buf) + self.buffer.write(buf) def pack_string(self,str,size=0): - buf = str + "\000" - if size and (len(buf) < size): - buf += "\000" * (size-len(buf)) - self.data.write(buf) + # If size == 0, caller doesn't care, just wants a terminating nul byte + size = size or (len(str) + 1) + # Nonzero size means a fixed-length field. If the passed string (plus + # its terminating nul) exceeds that fixed length, we'll have to + # truncate. But make sure we still leave room for the final nul byte! + str = str[:size-1] + # Now pad what's left of str out to 'size' with nul bytes. + buf = str + ("\000" * (size-len(str))) + self.buffer.write(buf) class FileUnpacker(object): def __init__(self, filename): - f = open(filename,"rb") - self.data = f.read() + with open(filename,"rb") as f: + self.buffer = f.read() self.offset = 0 def unpack(self,fmt): - result = struct.unpack_from(fmt, self.data, self.offset) + result = struct.unpack_from(fmt, self.buffer, self.offset) self.offset += struct.calcsize(fmt) return result def unpack_string(self, size=0): - result = "" - i = 0 - while (self.data[self.offset+i] != "\000"): - result += self.data[self.offset+i] - i += 1 - i += 1 + # Nonzero size means we must consider exactly the next 'size' + # characters in self.buffer. if size: - # fixed-size field for the string - i = size - self.offset += i + self.offset += size + # but stop at the first nul byte + return self.buffer[self.offset-size:self.offset].split("\000", 1)[0] + # Zero size means consider everything until the next nul character. + result = self.buffer[self.offset:].split("\000", 1)[0] + # don't forget to skip the nul byte too + self.offset += len(result) + 1 return result # translated from the C++ version in lldefs.h @@ -108,7 +138,7 @@ def F32_to_U16(val, lower, upper): # translated from the C++ version in llquantize.h def U16_to_F32(ival, lower, upper): if ival < 0 or ival > U16MAX: - raise Exception("U16 out of range: "+ival) + raise ValueError("U16 out of range: %s" % ival) val = ival*OOU16MAX delta = (upper - lower) val *= delta @@ -121,71 +151,100 @@ def U16_to_F32(ival, lower, upper): val = 0.0 return val; -class BadFormat(Exception): - pass - class RotKey(object): - def __init__(self): - pass - - def unpack(self, anim, fup): - (self.time_short, ) = fup.unpack("<H") - self.time = U16_to_F32(self.time_short, 0.0, anim.duration) + def __init__(self, time, duration, rot): + """ + This constructor instantiates a RotKey object from scratch, as it + were, converting from float time to time_short. + """ + self.time = time + self.time_short = F32_to_U16(time, 0.0, duration) \ + if time is not None else None + self.rotation = rot + + @staticmethod + def unpack(duration, fup): + """ + This staticmethod constructs a RotKey by loadingfrom a FileUnpacker. + """ + # cheat the other constructor + this = RotKey(None, None, None) + # load time_short directly from the file + (this.time_short, ) = fup.unpack("<H") + # then convert to float time + this.time = U16_to_F32(this.time_short, 0.0, duration) + # convert each coordinate of the rotation from short to float (x,y,z) = fup.unpack("<HHH") - self.rotation = [U16_to_F32(i, -1.0, 1.0) for i in (x,y,z)] + this.rotation = [U16_to_F32(i, -1.0, 1.0) for i in (x,y,z)] + return this def dump(self, f): - print >>f, " rot_key: t",self.time,"st",self.time_short,"rot",",".join([str(f) for f in self.rotation]) + print >>f, " rot_key: t %.3f" % self.time,"st",self.time_short,"rot",",".join("%.3f" % f for f in self.rotation) - def pack(self, anim, fp): - if not hasattr(self,"time_short"): - self.time_short = F32_to_U16(self.time, 0.0, anim.duration) + def pack(self, fp): fp.pack("<H",self.time_short) (x,y,z) = [F32_to_U16(v, -1.0, 1.0) for v in self.rotation] fp.pack("<HHH",x,y,z) class PosKey(object): - def __init__(self): - pass - - def unpack(self, anim, fup): - (self.time_short, ) = fup.unpack("<H") - self.time = U16_to_F32(self.time_short, 0.0, anim.duration) + def __init__(self, time, duration, pos): + """ + This constructor instantiates a PosKey object from scratch, as it + were, converting from float time to time_short. + """ + self.time = time + self.time_short = F32_to_U16(time, 0.0, duration) \ + if time is not None else None + self.position = pos + + @staticmethod + def unpack(duration, fup): + """ + This staticmethod constructs a PosKey by loadingfrom a FileUnpacker. + """ + # cheat the other constructor + this = PosKey(None, None, None) + # load time_short directly from the file + (this.time_short, ) = fup.unpack("<H") + # then convert to float time + this.time = U16_to_F32(this.time_short, 0.0, duration) + # convert each coordinate of the rotation from short to float (x,y,z) = fup.unpack("<HHH") - self.position = [U16_to_F32(i, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET) for i in (x,y,z)] + this.position = [U16_to_F32(i, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET) + for i in (x,y,z)] + return this def dump(self, f): - print >>f, " pos_key: t",self.time,"pos ",",".join([str(f) for f in self.position]) + print >>f, " pos_key: t %.3f" % self.time,"pos ",",".join("%.3f" % f for f in self.position) - def pack(self, anim, fp): - if not hasattr(self,"time_short"): - self.time_short = F32_to_U16(self.time, 0.0, anim.duration) + def pack(self, fp): fp.pack("<H",self.time_short) (x,y,z) = [F32_to_U16(v, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET) for v in self.position] fp.pack("<HHH",x,y,z) class Constraint(object): - def __init__(self): - pass - - def unpack(self, anim, fup): - (self.chain_length, self.constraint_type) = fup.unpack("<BB") - self.source_volume = fup.unpack_string(16) - self.source_offset = fup.unpack("<fff") - self.target_volume = fup.unpack_string(16) - self.target_offset = fup.unpack("<fff") - self.target_dir = fup.unpack("<fff") - fmt = "<ffff" - (self.ease_in_start, self.ease_in_stop, self.ease_out_start, self.ease_out_stop) = fup.unpack("<ffff") - - def pack(self, anim, fp): + @staticmethod + def unpack(duration, fup): + this = Constraint() + (this.chain_length, this.constraint_type) = fup.unpack("<BB") + this.source_volume = fup.unpack_string(16) + this.source_offset = fup.unpack("<fff") + this.target_volume = fup.unpack_string(16) + this.target_offset = fup.unpack("<fff") + this.target_dir = fup.unpack("<fff") + (this.ease_in_start, this.ease_in_stop, this.ease_out_start, this.ease_out_stop) = \ + fup.unpack("<ffff") + return this + + def pack(self, fp): fp.pack("<BB", self.chain_length, self.constraint_type) fp.pack_string(self.source_volume, 16) fp.pack("<fff", *self.source_offset) fp.pack_string(self.target_volume, 16) fp.pack("<fff", *self.target_offset) fp.pack("<fff", *self.target_dir) - fp.pack("<ffff", self.ease_in_start, self.ease_in_stop, self.ease_out_start, self.ease_out_stop) + fp.pack("<ffff", self.ease_in_start, self.ease_in_stop, + self.ease_out_start, self.ease_out_stop) def dump(self, f): print >>f, " constraint:" @@ -202,30 +261,26 @@ class Constraint(object): print >>f, " ease_out_stop",self.ease_out_stop class Constraints(object): - def __init__(self): - pass - - def unpack(self, anim, fup): - (self.num_constraints, ) = fup.unpack("<i") - self.constraints = [] - for i in xrange(self.num_constraints): - constraint = Constraint() - constraint.unpack(anim, fup) - self.constraints.append(constraint) - - def pack(self, anim, fp): - fp.pack("<i",self.num_constraints) + @staticmethod + def unpack(duration, fup): + this = Constraints() + (num_constraints, ) = fup.unpack("<i") + this.constraints = [Constraint.unpack(duration, fup) + for i in xrange(num_constraints)] + return this + + def pack(self, fp): + fp.pack("<i",len(self.constraints)) for c in self.constraints: - c.pack(anim,fp) + c.pack(fp) def dump(self, f): - print >>f, "constraints:",self.num_constraints + print >>f, "constraints:",len(self.constraints) for c in self.constraints: c.dump(f) class PositionCurve(object): def __init__(self): - self.num_pos_keys = 0 self.keys = [] def is_static(self): @@ -236,28 +291,27 @@ class PositionCurve(object): return False return True - def unpack(self, anim, fup): - (self.num_pos_keys, ) = fup.unpack("<i") - self.keys = [] - for k in xrange(0,self.num_pos_keys): - pos_key = PosKey() - pos_key.unpack(anim, fup) - self.keys.append(pos_key) + @staticmethod + def unpack(duration, fup): + this = PositionCurve() + (num_pos_keys, ) = fup.unpack("<i") + this.keys = [PosKey.unpack(duration, fup) + for k in xrange(num_pos_keys)] + return this - def pack(self, anim, fp): - fp.pack("<i",self.num_pos_keys) + def pack(self, fp): + fp.pack("<i",len(self.keys)) for k in self.keys: - k.pack(anim, fp) + k.pack(fp) def dump(self, f): print >>f, " position_curve:" - print >>f, " num_pos_keys", self.num_pos_keys - for k in xrange(0,self.num_pos_keys): - self.keys[k].dump(f) + print >>f, " num_pos_keys", len(self.keys) + for k in self.keys: + k.dump(f) class RotationCurve(object): def __init__(self): - self.num_rot_keys = 0 self.keys = [] def is_static(self): @@ -268,42 +322,46 @@ class RotationCurve(object): return False return True - def unpack(self, anim, fup): - (self.num_rot_keys, ) = fup.unpack("<i") - self.keys = [] - for k in xrange(0,self.num_rot_keys): - rot_key = RotKey() - rot_key.unpack(anim, fup) - self.keys.append(rot_key) + @staticmethod + def unpack(duration, fup): + this = RotationCurve() + (num_rot_keys, ) = fup.unpack("<i") + this.keys = [RotKey.unpack(duration, fup) + for k in xrange(num_rot_keys)] + return this - def pack(self, anim, fp): - fp.pack("<i",self.num_rot_keys) + def pack(self, fp): + fp.pack("<i",len(self.keys)) for k in self.keys: - k.pack(anim, fp) + k.pack(fp) def dump(self, f): print >>f, " rotation_curve:" - print >>f, " num_rot_keys", self.num_rot_keys - for k in xrange(0,self.num_rot_keys): - self.keys[k].dump(f) + print >>f, " num_rot_keys", len(self.keys) + for k in self.keys: + k.dump(f) class JointInfo(object): - def __init__(self): - pass - - def unpack(self, anim, fup): - self.joint_name = fup.unpack_string() - (self.joint_priority, ) = fup.unpack("<i") + def __init__(self, name, priority): + self.joint_name = name + self.joint_priority = priority self.rotation_curve = RotationCurve() - self.rotation_curve.unpack(anim, fup) self.position_curve = PositionCurve() - self.position_curve.unpack(anim, fup) - def pack(self, anim, fp): + @staticmethod + def unpack(duration, fup): + this = JointInfo(None, None) + this.joint_name = fup.unpack_string() + (this.joint_priority, ) = fup.unpack("<i") + this.rotation_curve = RotationCurve.unpack(duration, fup) + this.position_curve = PositionCurve.unpack(duration, fup) + return this + + def pack(self, fp): fp.pack_string(self.joint_name) fp.pack("<i", self.joint_priority) - self.rotation_curve.pack(anim, fp) - self.position_curve.pack(anim, fp) + self.rotation_curve.pack(fp) + self.position_curve.pack(fp) def dump(self, f): print >>f, "joint:" @@ -313,13 +371,26 @@ class JointInfo(object): self.position_curve.dump(f) class Anim(object): - def __init__(self, filename=None): + def __init__(self, filename=None, verbose=False): + # set this FIRST as it's consulted by read() and unpack() + self.verbose = verbose if filename: self.read(filename) def read(self, filename): fup = FileUnpacker(filename) - self.unpack(fup) + try: + self.unpack(fup) + except struct.error as err: + raise BadFormat("error reading %s: %s" % (filename, err)) + # By the end of streaming data in from our FileUnpacker, we should + # have consumed the entire thing. If there's excess data, it's + # entirely possible that this is a garbage file that happens to + # resemble a valid degenerate .anim file, e.g. with zero counts of + # things. + if fup.offset != len(fup.buffer): + raise ExtraneousData("extraneous data in %s; is it really a Linden .anim file?" % + filename) # various validity checks could be added - see LLKeyframeMotion::deserialize() def unpack(self,fup): @@ -333,27 +404,57 @@ class Anim(object): else: raise BadFormat("Bad combination of version, sub_version: %d %d" % (self.version, self.sub_version)) + # Also consult BVH conversion code for stricter checks + + # C++ deserialize() checks self.base_priority against + # LLJoint::ADDITIVE_PRIORITY and LLJoint::USE_MOTION_PRIORITY, + # possibly sets self.max_priority + # checks self.duration against MAX_ANIM_DURATION !! + # checks self.emote_name != str(self.ID) + # checks self.hand_pose against LLHandMotion::NUM_HAND_POSES !! + # checks 0 < num_joints <= LL_CHARACTER_MAX_JOINTS (no need -- + # validate names) + # checks each joint_name neither "mScreen" nor "mRoot" ("attempted to + # animate special joint") !! + # checks each joint_name can be found in mCharacter + # checks each joint_priority >= LLJoint::USE_MOTION_PRIORITY + # tracks max observed joint_priority, excluding USE_MOTION_PRIORITY + # checks each 0 <= RotKey.time <= self.duration !! + # checks each RotKey.rotation.isFinite() !! + # checks each PosKey.position.isFinite() !! + # checks 0 <= num_constraints <= MAX_CONSTRAINTS !! + # checks each Constraint.chain_length <= num_joints + # checks each Constraint.constraint_type < NUM_CONSTRAINT_TYPES !! + # checks each Constraint.source_offset.isFinite() !! + # checks each Constraint.target_offset.isFinite() !! + # checks each Constraint.target_dir.isFinite() !! + # from https://bitbucket.org/lindenlab/viewer-release/src/827a910542a9af0a39b0ca03663c02e5c83869ea/indra/llcharacter/llkeyframemotion.cpp?at=default&fileviewer=file-view-default#llkeyframemotion.cpp-1812 : + # find joint to which each Constraint's collision volume is attached; + # for each link in Constraint.chain_length, walk to joint's parent, + # find that parent in list of joints, set its index in index list + self.emote_name = fup.unpack_string() - (self.loop_in_point, self.loop_out_point, self.loop, self.ease_in_duration, self.ease_out_duration, self.hand_pose, self.num_joints) = fup.unpack("@ffiffII") + (self.loop_in_point, self.loop_out_point, self.loop, + self.ease_in_duration, self.ease_out_duration, self.hand_pose, num_joints) = \ + fup.unpack("@ffiffII") - self.joints = [] - for j in xrange(0,self.num_joints): - joint_info = JointInfo() - joint_info.unpack(self, fup) - self.joints.append(joint_info) - print "unpacked joint",joint_info.joint_name - self.constraints = Constraints() - self.constraints.unpack(self, fup) - self.data = fup.data + self.joints = [JointInfo.unpack(self.duration, fup) + for j in xrange(num_joints)] + if self.verbose: + for joint_info in self.joints: + print "unpacked joint",joint_info.joint_name + self.constraints = Constraints.unpack(self.duration, fup) + self.buffer = fup.buffer def pack(self, fp): fp.pack("@HHhf", self.version, self.sub_version, self.base_priority, self.duration) fp.pack_string(self.emote_name, 0) - fp.pack("@ffiffII", self.loop_in_point, self.loop_out_point, self.loop, self.ease_in_duration, self.ease_out_duration, self.hand_pose, self.num_joints) + fp.pack("@ffiffII", self.loop_in_point, self.loop_out_point, self.loop, + self.ease_in_duration, self.ease_out_duration, self.hand_pose, len(self.joints)) for j in self.joints: - j.pack(anim, fp) - self.constraints.pack(anim, fp) + j.pack(fp) + self.constraints.pack(fp) def dump(self, filename="-"): if filename=="-": @@ -370,7 +471,7 @@ class Anim(object): print >>f, "ease_in_duration: ", self.ease_in_duration print >>f, "ease_out_duration: ", self.ease_out_duration print >>f, "hand_pose", self.hand_pose - print >>f, "num_joints", self.num_joints + print >>f, "num_joints", len(self.joints) for j in self.joints: j.dump(f) self.constraints.dump(f) @@ -382,10 +483,9 @@ class Anim(object): def write_src_data(self, filename): print "write file",filename - f = open(filename,"wb") - f.write(self.data) - f.close() - + with open(filename,"wb") as f: + f.write(self.buffer) + def find_joint(self, name): joints = [j for j in self.joints if j.joint_name == name] if joints: @@ -395,91 +495,71 @@ class Anim(object): def add_joint(self, name, priority): if not self.find_joint(name): - j = JointInfo() - j.joint_name = name - j.joint_priority = priority - j.rotation_curve = RotationCurve() - j.position_curve = PositionCurve() - self.joints.append(j) - self.num_joints = len(self.joints) + self.joints.append(JointInfo(name, priority)) def delete_joint(self, name): j = self.find_joint(name) if j: - if args.verbose: + if self.verbose: print "removing joint", name - anim.joints.remove(j) - anim.num_joints = len(self.joints) + self.joints.remove(j) else: - if args.verbose: + if self.verbose: print "joint not found to remove", name def summary(self): nj = len(self.joints) nz = len([j for j in self.joints if j.joint_priority > 0]) - nstatic = len([j for j in self.joints if j.rotation_curve.is_static() and j.position_curve.is_static()]) + nstatic = len([j for j in self.joints + if j.rotation_curve.is_static() + and j.position_curve.is_static()]) print "summary: %d joints, non-zero priority %d, static %d" % (nj, nz, nstatic) def add_pos(self, joint_names, positions): js = [joint for joint in self.joints if joint.joint_name in joint_names] for j in js: - if args.verbose: + if self.verbose: print "adding positions",j.joint_name,positions j.joint_priority = 4 - j.position_curve.num_pos_keys = len(positions) - j.position_curve.keys = [] - for i,pos in enumerate(positions): - key = PosKey() - key.time = self.duration * i / (len(positions) - 1) - key.time_short = F32_to_U16(key.time, 0.0, self.duration) - key.position = pos - j.position_curve.keys.append(key) + j.position_curve.keys = [PosKey(self.duration * i / (len(positions) - 1), + self.duration, + pos) + for i,pos in enumerate(positions)] def add_rot(self, joint_names, rotations): js = [joint for joint in self.joints if joint.joint_name in joint_names] for j in js: print "adding rotations",j.joint_name j.joint_priority = 4 - j.rotation_curve.num_rot_keys = len(rotations) - j.rotation_curve.keys = [] - for i,pos in enumerate(rotations): - key = RotKey() - key.time = self.duration * i / (len(rotations) - 1) - key.time_short = F32_to_U16(key.time, 0.0, self.duration) - key.rotation = pos - j.rotation_curve.keys.append(key) + j.rotation_curve.keys = [RotKey(self.duration * i / (len(rotations) - 1), + self.duration, + rot) + for i,rot in enumerate(rotations)] def twistify(anim, joint_names, rot1, rot2): js = [joint for joint in anim.joints if joint.joint_name in joint_names] for j in js: print "twisting",j.joint_name - print j.rotation_curve.num_rot_keys + print len(j.rotation_curve.keys) j.joint_priority = 4 - j.rotation_curve.num_rot_keys = 2 - j.rotation_curve.keys = [] - key1 = RotKey() - key1.time_short = 0 - key1.time = U16_to_F32(key1.time_short, 0.0, anim.duration) - key1.rotation = rot1 - key2 = RotKey() - key2.time_short = U16MAX - key2.time = U16_to_F32(key2.time_short, 0.0, anim.duration) - key2.rotation = rot2 - j.rotation_curve.keys.append(key1) - j.rotation_curve.keys.append(key2) + # Set the joint(s) to rot1 at time 0, rot2 at the full duration. + j.rotation_curve.keys = [ + RotKey(0.0, anim.duration, rot1), + RotKey(anim.duration, anim.duration, rot2)] def float_triple(arg): vals = arg.split() if len(vals)==3: return [float(x) for x in vals] else: - raise Exception("arg %s does not resolve to a float triple" % arg) + raise ValueError("arg %s does not resolve to a float triple" % arg) def get_joint_by_name(tree,name): if tree is None: return None - matches = [elt for elt in tree.getroot().iter() if \ - elt.get("name")==name and elt.tag in ["bone", "collision_volume", "attachment_point"]] + matches = [elt for elt in tree.getroot().iter() + if elt.get("name")==name + and elt.tag in ["bone", "collision_volume", "attachment_point"]] if len(matches)==1: return matches[0] elif len(matches)>1: @@ -496,121 +576,135 @@ def get_elt_pos(elt): else: return (0.0, 0.0, 0.0) -def resolve_joints(names, skel_tree, lad_tree): - print "resolve joints, no_hud is",args.no_hud +def resolve_joints(names, skel_tree, lad_tree, no_hud=False): + print "resolve joints, no_hud is",no_hud if skel_tree and lad_tree: all_elts = [elt for elt in skel_tree.getroot().iter()] all_elts.extend([elt for elt in lad_tree.getroot().iter()]) - matches = [] + matches = set() for elt in all_elts: if elt.get("name") is None: continue #print elt.get("name"),"hud",elt.get("hud") - if args.no_hud and elt.get("hud"): + if no_hud and elt.get("hud"): #print "skipping hud joint", elt.get("name") continue if elt.get("name") in names or elt.tag in names: - matches.append(elt.get("name")) - return list(set(matches)) + matches.add(elt.get("name")) + return list(matches) else: return names -if __name__ == "__main__": +def main(*argv): + import argparse # default search location for config files is defined relative to # the script location; assuming they live in the same viewer repo + # Use sys.argv[0] because (a) this script lives where it lives regardless + # of what our caller passes and (b) we don't expect our caller to pass the + # script name anyway. pathname = os.path.dirname(sys.argv[0]) - path_to_skel = os.path.join(os.path.abspath(pathname),"..","..","indra","newview","character") + # we're in scripts/content_tools; hop back to base of repository clone + path_to_skel = os.path.join(os.path.abspath(pathname),os.pardir,os.pardir, + "indra","newview","character") parser = argparse.ArgumentParser(description="process SL animations") parser.add_argument("--verbose", help="verbose flag", action="store_true") - parser.add_argument("--dump", help="dump to specified file") + parser.add_argument("--dump", metavar="FILEPATH", help="dump to specified file") parser.add_argument("--rot", help="specify sequence of rotations", type=float_triple, nargs="+") - parser.add_argument("--rand_pos", help="request random positions", action="store_true") + parser.add_argument("--rand_pos", help="request NUM random positions (default %(default)s)", + metavar="NUM", type=int, default=2) parser.add_argument("--reset_pos", help="request original positions", action="store_true") parser.add_argument("--pos", help="specify sequence of positions", type=float_triple, nargs="+") - parser.add_argument("--num_pos", help="number of positions to create", type=int, default=2) - parser.add_argument("--delete_joints", help="specify joints to be deleted", nargs="+") - parser.add_argument("--joints", help="specify joints to be added or modified", nargs="+") + parser.add_argument("--delete_joints", help="specify joints to be deleted", nargs="+", + metavar="JOINT") + parser.add_argument("--joints", help="specify joints to be added or modified", nargs="+", + metavar="JOINT") parser.add_argument("--summary", help="print summary of the output animation", action="store_true") - parser.add_argument("--skel", help="name of the avatar_skeleton file", default= os.path.join(path_to_skel,"avatar_skeleton.xml")) - parser.add_argument("--lad", help="name of the avatar_lad file", default= os.path.join(path_to_skel,"avatar_lad.xml")) - parser.add_argument("--set_version", nargs=2, type=int, help="set version and sub-version to specified values") + parser.add_argument("--skel", help="name of the avatar_skeleton file (default %(default)s)", + default=os.path.join(path_to_skel,"avatar_skeleton.xml"), + metavar="FILEPATH") + parser.add_argument("--lad", help="name of the avatar_lad file (default %(default)s)", + default=os.path.join(path_to_skel,"avatar_lad.xml"), + metavar="FILEPATH") + parser.add_argument("--set_version", nargs=2, type=int, + help="set version and sub-version to specified values", + metavar=("VERSION", "SUB-VERSION")) parser.add_argument("--no_hud", help="omit hud joints from list of attachments", action="store_true") parser.add_argument("--base_priority", help="set base priority", type=int) parser.add_argument("--joint_priority", help="set joint priority for all joints", type=int) parser.add_argument("infilename", help="name of a .anim file to input") parser.add_argument("outfilename", nargs="?", help="name of a .anim file to output") - args = parser.parse_args() + args = parser.parse_args(argv) - print "anim_tool.py: " + " ".join(sys.argv) + print "anim_tool.py: " + " ".join(argv) print "dump is", args.dump print "infilename",args.infilename,"outfilename",args.outfilename print "rot",args.rot print "pos",args.pos print "joints",args.joints - try: - anim = Anim(args.infilename) - skel_tree = None - lad_tree = None - joints = [] - if args.skel: - skel_tree = etree.parse(args.skel) - if skel_tree is None: - print "failed to parse",args.skel - exit(1) - if args.lad: - lad_tree = etree.parse(args.lad) - if lad_tree is None: - print "failed to parse",args.lad - exit(1) - if args.joints: - joints = resolve_joints(args.joints, skel_tree, lad_tree) - if args.verbose: - print "joints resolved to",joints - for name in joints: - anim.add_joint(name,0) - if args.delete_joints: - for name in args.delete_joints: - anim.delete_joint(name) - if joints and args.rot: - anim.add_rot(joints, args.rot) - if joints and args.pos: - anim.add_pos(joints, args.pos) - if joints and args.rand_pos: - for joint in joints: - pos_array = list(tuple(random.uniform(-1,1) for i in xrange(3)) for j in xrange(args.num_pos)) - pos_array.append(pos_array[0]) - anim.add_pos([joint], pos_array) - if joints and args.reset_pos: - for joint in joints: - elt = get_joint_by_name(skel_tree,joint) - if elt is None: - elt = get_joint_by_name(lad_tree,joint) - if elt is not None: - pos_array = [] - pos_array.append(get_elt_pos(elt)) - pos_array.append(pos_array[0]) - anim.add_pos([joint], pos_array) - else: - print "no elt or no pos data for",joint - if args.set_version: - anim.version = args.set_version[0] - anim.sub_version = args.set_version[1] - if args.base_priority is not None: - print "set base priority",args.base_priority - anim.base_priority = args.base_priority - if args.joint_priority is not None: - print "set joint priority",args.joint_priority - for joint in anim.joints: - joint.joint_priority = args.joint_priority - if args.dump: - anim.dump(args.dump) - if args.summary: - anim.summary() - if args.outfilename: - anim.write(args.outfilename) - except: - raise + anim = Anim(args.infilename, args.verbose) + skel_tree = None + lad_tree = None + joints = [] + if args.skel: + skel_tree = ElementTree.parse(args.skel) + if skel_tree is None: + raise Error("failed to parse " + args.skel) + if args.lad: + lad_tree = ElementTree.parse(args.lad) + if lad_tree is None: + raise Error("failed to parse " + args.lad) + if args.joints: + joints = resolve_joints(args.joints, skel_tree, lad_tree, args.no_hud) + if args.verbose: + print "joints resolved to",joints + for name in joints: + anim.add_joint(name,0) + if args.delete_joints: + for name in args.delete_joints: + anim.delete_joint(name) + if joints and args.rot: + anim.add_rot(joints, args.rot) + if joints and args.pos: + anim.add_pos(joints, args.pos) + if joints and args.rand_pos: + # pick a random sequence of positions for each joint specified + for joint in joints: + # generate a list of rand_pos triples + pos_array = [tuple(random.uniform(-1,1) for i in xrange(3)) + for j in xrange(args.rand_pos)] + # close the loop by cycling back to the first entry + pos_array.append(pos_array[0]) + anim.add_pos([joint], pos_array) + if joints and args.reset_pos: + for joint in joints: + elt = get_joint_by_name(skel_tree,joint) or get_joint_by_name(lad_tree,joint) + if elt is not None: + anim.add_pos([joint], 2*[get_elt_pos(elt)]) + else: + print "no elt or no pos data for",joint + if args.set_version: + anim.version, anim.sub_version = args.set_version + if args.base_priority is not None: + print "set base priority",args.base_priority + anim.base_priority = args.base_priority + # --joint_priority sets priority for ALL joints, not just the explicitly- + # specified ones + if args.joint_priority is not None: + print "set joint priority",args.joint_priority + for joint in anim.joints: + joint.joint_priority = args.joint_priority + if args.dump: + anim.dump(args.dump) + if args.summary: + anim.summary() + if args.outfilename: + anim.write(args.outfilename) +if __name__ == "__main__": + try: + sys.exit(main(*sys.argv[1:])) + except Error as err: + sys.exit("%s: %s" % (err.__class__.__name__, err)) |