summaryrefslogtreecommitdiff
path: root/indra/newview/llwearable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llwearable.cpp')
-rw-r--r--indra/newview/llwearable.cpp952
1 files changed, 952 insertions, 0 deletions
diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp
new file mode 100644
index 0000000000..ebe3c69c5c
--- /dev/null
+++ b/indra/newview/llwearable.cpp
@@ -0,0 +1,952 @@
+/**
+ * @file llwearable.cpp
+ * @brief LLWearable class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "imageids.h"
+#include "llassetstorage.h"
+#include "lldbstrings.h"
+#include "lldir.h"
+#include "llquantize.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llfloatercustomize.h"
+#include "llinventorymodel.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+
+//#include "viewer.h"
+//#include "llvfs.h"
+
+// static
+S32 LLWearable::sCurrentDefinitionVersion = 1;
+
+// static
+const char* LLWearable::sTypeName[ WT_COUNT ] =
+{
+ "shape",
+ "skin",
+ "hair",
+ "eyes",
+ "shirt",
+ "pants",
+ "shoes",
+ "socks",
+ "jacket",
+ "gloves",
+ "undershirt",
+ "underpants",
+ "skirt"
+};
+
+// static
+const char* LLWearable::sTypeLabel[ WT_COUNT ] =
+{
+ "Shape",
+ "Skin",
+ "Hair",
+ "Eyes",
+ "Shirt",
+ "Pants",
+ "Shoes",
+ "Socks",
+ "Jacket",
+ "Gloves",
+ "Undershirt",
+ "Underpants",
+ "Skirt"
+};
+
+
+// static
+LLAssetType::EType LLWearable::typeToAssetType(EWearableType wearable_type)
+{
+ switch( wearable_type )
+ {
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return LLAssetType::AT_BODYPART;
+ case WT_SHIRT:
+ case WT_PANTS:
+ case WT_SHOES:
+ case WT_SOCKS:
+ case WT_JACKET:
+ case WT_GLOVES:
+ case WT_UNDERSHIRT:
+ case WT_UNDERPANTS:
+ case WT_SKIRT:
+ return LLAssetType::AT_CLOTHING;
+ default:
+ return LLAssetType::AT_NONE;
+ }
+}
+
+
+LLWearable::LLWearable(const LLTransactionID& transaction_id) :
+ mDefinitionVersion(LLWearable::sCurrentDefinitionVersion),
+ mType(WT_SHAPE)
+{
+ mTransactionID = transaction_id;
+ mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+}
+
+LLWearable::LLWearable(const LLAssetID& asset_id) :
+ mDefinitionVersion( LLWearable::sCurrentDefinitionVersion ),
+ mType(WT_SHAPE)
+{
+ mAssetID = asset_id;
+ mTransactionID.setNull();
+}
+
+LLWearable::~LLWearable()
+{
+ mVisualParamMap.deleteAllData();
+ mTEMap.deleteAllData();
+}
+
+
+// static
+EWearableType LLWearable::typeNameToType( const LLString& type_name )
+{
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( type_name == LLWearable::sTypeName[ i ] )
+ {
+ return (EWearableType)i;
+ }
+ }
+ return WT_INVALID;
+}
+
+
+const char* terse_F32_to_string( F32 f, char s[MAX_STRING] )
+{
+ char* r = s;
+ S32 len = sprintf( s, "%.2f", f );
+
+ // "1.20" -> "1.2"
+ // "24.00" -> "24."
+ while( '0' == r[len - 1] )
+ {
+ len--;
+ r[len] = '\0';
+ }
+
+ if( '.' == r[len - 1] )
+ {
+ // "24." -> "24"
+ len--;
+ r[len] = '\0';
+ }
+ else
+ if( ('-' == r[0]) && ('0' == r[1]) )
+ {
+ // "-0.59" -> "-.59"
+ r++;
+ r[0] = '-';
+ }
+ else
+ if( '0' == r[0] )
+ {
+ // "0.59" -> ".59"
+ r++;
+ }
+
+ return r;
+}
+
+BOOL LLWearable::exportFile( FILE* file )
+{
+ // header and version
+ if( fprintf( file, "LLWearable version %d\n", mDefinitionVersion ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // name
+ if( fprintf( file, "%s\n", mName.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // description
+ if( fprintf( file, "%s\n", mDescription.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // permissions
+ if( !mPermissions.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ if( !mSaleInfo.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // wearable type
+ S32 type = (S32)mType;
+ if( fprintf( file, "type %d\n", type ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // parameters
+ S32 num_parameters = mVisualParamMap.getLength();
+ if( fprintf( file, "parameters %d\n", num_parameters ) < 0 )
+ {
+ return FALSE;
+ }
+
+ char s[ MAX_STRING ];
+ for( F32* param_weightp = mVisualParamMap.getFirstData(); param_weightp; param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ if( fprintf( file, "%d %s\n", param_id, terse_F32_to_string( *param_weightp, s ) ) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ // texture entries
+ S32 num_textures = mTEMap.getLength();
+ if( fprintf( file, "textures %d\n", num_textures ) < 0 )
+ {
+ return FALSE;
+ }
+
+ for( LLUUID* image_id = mTEMap.getFirstData(); image_id; image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ char image_id_string[UUID_STR_LENGTH];
+ image_id->toString( image_id_string );
+ if( fprintf( file, "%d %s\n", te, image_id_string) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL LLWearable::importFile( FILE* file )
+{
+ char text_buffer[2048];
+ S32 fields_read = 0;
+
+ // read header and version
+ fields_read = fscanf( file, "LLWearable version %d\n", &mDefinitionVersion );
+ if( fields_read != 1 )
+ {
+ // Shouldn't really log the asset id for security reasons, but
+ // we need it in this case.
+ llwarns << "Bad Wearable asset header: " << mAssetID << llendl;
+ //gVFS->dumpMap();
+ return FALSE;
+ }
+
+ if( mDefinitionVersion > LLWearable::sCurrentDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ return FALSE;
+ }
+
+ // name
+ char next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no name
+ mName = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mName = text_buffer;
+ LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN );
+ }
+
+ // description
+ next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no description
+ mDescription = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mDescription = text_buffer;
+ LLString::truncate(mDescription, DB_INV_ITEM_DESC_STR_LEN );
+ }
+
+ // permissions
+ S32 perm_version;
+ fields_read = fscanf( file, " permissions %d\n", &perm_version );
+ if( (fields_read != 1) || (perm_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing permissions" << llendl;
+ return FALSE;
+ }
+ if( !mPermissions.importFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ S32 sale_info_version;
+ fields_read = fscanf( file, " sale_info %d\n", &sale_info_version );
+ if( (fields_read != 1) || (sale_info_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing sale_info" << llendl;
+ return FALSE;
+ }
+ // Sale info used to contain next owner perm. It is now in the
+ // permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it should pick
+ // up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ if( !mSaleInfo.importFile(file, has_perm_mask, perm_mask) )
+ {
+ return FALSE;
+ }
+ if(has_perm_mask)
+ {
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+
+ // wearable type
+ S32 type = -1;
+ fields_read = fscanf( file, "type %d\n", &type );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: bad type" << llendl;
+ return FALSE;
+ }
+ if( 0 <= type && type < WT_COUNT )
+ {
+ mType = (EWearableType)type;
+ }
+ else
+ {
+ llwarns << "Bad Wearable asset: bad type #" << type << llendl;
+ return FALSE;
+ }
+
+
+ // parameters header
+ S32 num_parameters = 0;
+ fields_read = fscanf( file, "parameters %d\n", &num_parameters );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing parameters block" << llendl;
+ return FALSE;
+ }
+
+ // parameters
+ S32 i;
+ for( i = 0; i < num_parameters; i++ )
+ {
+ S32 param_id = 0;
+ F32 param_weight = 0.f;
+ fields_read = fscanf( file, "%d %f\n", &param_id, &param_weight );
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad parameter, #" << i << llendl;
+ return FALSE;
+ }
+ mVisualParamMap.addData( param_id, new F32(param_weight) );
+ }
+
+ // textures header
+ S32 num_textures = 0;
+ fields_read = fscanf( file, "textures %d\n", &num_textures);
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing textures block" << llendl;
+ return FALSE;
+ }
+
+ // textures
+ for( i = 0; i < num_textures; i++ )
+ {
+ S32 te = 0;
+ fields_read = fscanf( file, "%d %s\n", &te, text_buffer);
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad texture, #" << i << llendl;
+ return FALSE;
+ }
+
+ if( !LLUUID::validate( text_buffer ) )
+ {
+ llwarns << "Bad Wearable asset: bad texture uuid: " << text_buffer << llendl;
+ return FALSE;
+ }
+
+ mTEMap.addData( te, new LLUUID( text_buffer ) );
+ }
+
+ return TRUE;
+}
+
+
+// Avatar parameter and texture definitions can change over time.
+// This function returns true if parameters or textures have been added or removed
+// since this wearable was created.
+BOOL LLWearable::isOldVersion()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion < mDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ llassert(0);
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion != mDefinitionVersion )
+ {
+ return TRUE;
+ }
+
+ S32 param_count = 0;
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ param_count++;
+ if( !mVisualParamMap.checkKey( param->getID() ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( param_count != mVisualParamMap.getLength() )
+ {
+ return TRUE;
+ }
+
+
+ S32 te_count = 0;
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ te_count++;
+ if( !mTEMap.checkKey( te ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( te_count != mTEMap.getLength() )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// Avatar parameter and texture definitions can change over time.
+// * If parameters or textures have been REMOVED since the wearable was created,
+// they're just ignored, so we consider the wearable clean even though isOldVersion()
+// will return true.
+// * If parameters or textures have been ADDED since the wearable was created,
+// they are taken to have default values, so we consider the wearable clean
+// only if those values are the same as the defaults.
+BOOL LLWearable::isDirty()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ F32* weightp = mVisualParamMap.getIfThere( param->getID() );
+ F32 weight;
+ if( weightp )
+ {
+ weight = llclamp( *weightp, param->getMinWeight(), param->getMaxWeight() );
+ }
+ else
+ {
+ weight = param->getDefaultWeight();
+ }
+
+ U8 a = F32_to_U8( param->getWeight(), param->getMinWeight(), param->getMaxWeight() );
+ U8 b = F32_to_U8( weight, param->getMinWeight(), param->getMaxWeight() );
+ if( a != b )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* avatar_image = avatar->getTEImage( te );
+ if( !avatar_image )
+ {
+ llassert( 0 );
+ continue;
+ }
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ if( avatar_image->getID() != image_id )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // if( mDescription != gFloaterCustomize->getWearableDescription( mType ) )
+ // {
+ // return TRUE;
+ // }
+ //}
+
+ return FALSE;
+}
+
+
+void LLWearable::setParamsToDefaults()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType ) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getDefaultWeight() ) );
+ }
+ }
+}
+
+void LLWearable::setTexturesToDefaults()
+{
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ mTEMap.addData( te, new LLUUID( LLVOAvatar::getDefaultTEImageID( te ) ) );
+ }
+ }
+}
+
+// Updates the user's avatar's appearance
+void LLWearable::writeToAvatar( BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ ESex old_sex = avatar->getSex();
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ F32* weight = mVisualParamMap.getIfThere( param_id );
+ if( weight )
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(*weight, set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, *weight, set_by_user );
+ }
+ }
+ else
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(param->getDefaultWeight(), set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+ }
+ }
+
+ // only interpolate with user-originated changes
+ if (set_by_user)
+ {
+ avatar->startAppearanceAnimation(TRUE, TRUE);
+ }
+
+ // Pull texture entries
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ LLViewerImage* image = gImageList.getImage( image_id );
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ avatar->updateVisualParams();
+
+ if( gFloaterCustomize )
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(gAgent.getWearableItem(mType));
+ U32 perm_mask = PERM_NONE;
+ BOOL is_complete = FALSE;
+ if(item)
+ {
+ perm_mask = item->getPermissions().getMaskOwner();
+ is_complete = item->isComplete();
+ if(!is_complete)
+ {
+ item->fetchFromServer();
+ }
+ }
+ gFloaterCustomize->setWearable(mType, this, perm_mask, is_complete);
+ LLFloaterCustomize::setCurrentWearableType( mType );
+ }
+
+ ESex new_sex = avatar->getSex();
+ if( old_sex != new_sex )
+ {
+ avatar->updateSexDependentLayerSets( set_by_user );
+ }
+
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+// Updates the user's avatar's appearance, replacing this wearables' parameters and textures with default values.
+// static
+void LLWearable::removeFromAvatar( EWearableType type, BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ // You can't just remove body parts.
+ if( (type == WT_SHAPE) ||
+ (type == WT_SKIN) ||
+ (type == WT_HAIR) ||
+ (type == WT_EYES) )
+ {
+ return;
+ }
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == type) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+
+ // Pull textures
+ LLViewerImage* image = gImageList.getImage( IMG_DEFAULT_AVATAR );
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->setWearable(type, NULL, PERM_ALL, TRUE);
+ }
+
+ avatar->updateVisualParams();
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+
+
+// Updates asset from the user's avatar
+void LLWearable::readFromAvatar()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getWeight() ) );
+ }
+ }
+
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* image = avatar->getTEImage( te );
+ if( image )
+ {
+ mTEMap.addData( te, new LLUUID( image->getID() ) );
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // mDescription = gFloaterCustomize->getWearableDescription( mType );
+ //}
+}
+
+// Does not copy mAssetID.
+// Definition version is current: removes obsolete enties and creates default values for new ones.
+void LLWearable::copyDataFrom( LLWearable* src )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mName = src->mName;
+ mDescription = src->mDescription;
+ mPermissions = src->mPermissions;
+ mSaleInfo = src->mSaleInfo;
+ mType = src->mType;
+
+ // Deep copy of mVisualParamMap (copies only those params that are current, filling in defaults where needed)
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 id = param->getID();
+ F32* weightp = src->mVisualParamMap.getIfThere( id );
+ F32 weight = weightp ? *weightp : param->getDefaultWeight();
+ mVisualParamMap.addData( id, new F32( weight ) );
+ }
+ }
+
+ // Deep copy of mTEMap (copies only those tes that are current, filling in defaults where needed)
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = src->mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ mTEMap.addData( te, new LLUUID( image_id ) );
+ }
+ }
+}
+
+struct LLWearableSaveData
+{
+ EWearableType mType;
+};
+
+void LLWearable::saveNewAsset()
+{
+// llinfos << "LLWearable::saveNewAsset() type: " << getTypeName() << llendl;
+ //dump();
+
+ char new_asset_id_string[UUID_STR_LENGTH];
+ mAssetID.toString(new_asset_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ BOOL successful_save = FALSE;
+ if(fp && exportFile(fp))
+ {
+ successful_save = TRUE;
+ }
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(!successful_save)
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save '%s' to wearable file.",
+ mName.c_str());
+ llwarns << buffer << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = mName;
+ gViewerWindow->alertXml("CannotSaveWearableOutOfSpace", args);
+ return;
+ }
+
+ // save it out to database
+ if( gAssetStorage )
+ {
+ LLWearableSaveData* data = new LLWearableSaveData;
+ data->mType = mType;
+ gAssetStorage->storeAssetData(filename, mTransactionID, getAssetType(),
+ &LLWearable::onSaveNewAssetComplete,
+ (void*)data);
+ }
+}
+
+// static
+void LLWearable::onSaveNewAssetComplete(const LLUUID& new_asset_id, void* userdata, S32 status) // StoreAssetData callback (fixed)
+{
+ LLWearableSaveData* data = (LLWearableSaveData*)userdata;
+ const char* type_name = LLWearable::typeToTypeName(data->mType);
+ if(0 == status)
+ {
+ // Success
+ llinfos << "Saved wearable " << type_name << llendl;
+ }
+ else
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save %s to central asset store.",
+ type_name);
+ llwarns << buffer << " Status: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = type_name;
+ gViewerWindow->alertXml("CannotSaveToAssetStore", args);
+ }
+
+ // Delete temp file
+ char new_asset_id_string[UUID_STR_LENGTH];
+ new_asset_id.toString(new_asset_id_string);
+ char src_filename[LL_MAX_PATH];
+ sprintf(src_filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ LLFile::remove(src_filename);
+
+ // delete the context data
+ delete data;
+}
+
+BOOL LLWearable::isMatchedToInventoryItem( LLViewerInventoryItem* item )
+{
+ return
+ ( mName == item->getName() ) &&
+ ( mDescription == item->getDescription() ) &&
+ ( mPermissions == item->getPermissions() ) &&
+ ( mSaleInfo == item->getSaleInfo() );
+}
+
+void LLWearable::dump()
+{
+ llinfos << "wearable " << LLWearable::typeToTypeName( mType ) << llendl;
+ llinfos << " Name: " << mName << llendl;
+ llinfos << " Desc: " << mDescription << llendl;
+ //mPermissions
+ //mSaleInfo
+
+ llinfos << " Params:" << llendl;
+ for( F32* param_weightp = mVisualParamMap.getFirstData();
+ param_weightp;
+ param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << param_id << " " << *param_weightp << llendl;
+ }
+
+ llinfos << " Textures:" << llendl;
+ for( LLUUID* image_id = mTEMap.getFirstData();
+ image_id;
+ image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << te << " " << *image_id << llendl;
+ }
+}
+