/** * @file fsfloaterimport.cpp * @brief Floater to import objects from a file. * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ * Phoenix Firestorm Viewer Source Code * Copyright (c) 2013 Techwolf Lupindo * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA * http://www.firestormviewer.org * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "fsfloaterimport.h" #include "fscommon.h" #include "llagent.h" #include "llappviewer.h" #include "llbuycurrencyhtml.h" #include "llcallbacklist.h" #include "llcheckboxctrl.h" #include "lldatapacker.h" #include "lldir.h" #include "lleconomy.h" #include "llfloaterperms.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" #include "llmultigesture.h" #include "llnotificationsutil.h" #include "llparcel.h" #include "llprimlinkinfo.h" #include "llsdserialize.h" #include "llsdutil_math.h" #include "llsdutil.h" #include "llstatusbar.h" #include "lltooldraganddrop.h" #include "lltrans.h" #include "lltransactiontypes.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerstats.h" #include "llviewerregion.h" #include "llvfile.h" #include "llvfs.h" #include "llvolumemessage.h" #include "lltrace.h" #include "fsexportperms.h" #include #include #include "llcoros.h" #include "llcoproceduremanager.h" #include "llsdutil.h" struct FSResourceData { LLUUID uuid; bool temporary; LLAssetType::EType asset_type; LLUUID inventory_item; LLWearableType::EType wearable_type; bool post_asset_upload; LLUUID post_asset_upload_id; FSFloaterImport *mFloater; }; void resourceDeleter( LLResourceData *aData ) { FSResourceData *data = (FSResourceData*)aData->mUserData; delete data; delete aData; } void uploadCoroutine( LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &a_httpAdapter, std::string aURL, LLSD aBody, LLAssetID aAssetId, LLAssetType::EType aAssetType, boost::shared_ptr< LLResourceData > aResourceData ); FSFloaterImport::FSFloaterImport(const LLSD& filename) : LLFloater(filename), mCreatingActive(false), mLinkset(0), mObject(0), mPrim(0), mImportState(IDLE), mFileFullName(filename), mObjectCreatedCallback(), m_AssetsUploading( 0 ) { mInstance = this; mCommitCallbackRegistrar.add("Import.UploadAsset",boost::bind(&FSFloaterImport::onClickCheckBoxUploadAsset,this)); mCommitCallbackRegistrar.add("Import.TempAsset",boost::bind(&FSFloaterImport::onClickCheckBoxTempAsset,this)); gIdleCallbacks.addFunction(onIdle, this); mSavedSettingShowNewInventory = gSavedSettings.getBOOL("ShowNewInventory"); } FSFloaterImport::~FSFloaterImport() { if (!gIdleCallbacks.deleteFunction(onIdle, this)) { LL_WARNS("import") << "FSFloaterImport::~FSFloaterImport() failed to delete idle callback" << LL_ENDL; } if (mObjectCreatedCallback.connected()) { mObjectCreatedCallback.disconnect(); } gSavedSettings.setBOOL("ShowNewInventory", mSavedSettingShowNewInventory); } BOOL FSFloaterImport::postBuild() { if (LLGlobalEconomy::getInstance()->getPriceUpload() == 0 || gAgent.getRegion()->getCentralBakeVersion() > 0) { getChild("temp_asset")->setVisible(FALSE); getChild("temp_asset")->set(FALSE); } getChild("import_btn")->setCommitCallback(boost::bind(&FSFloaterImport::onClickBtnImport, this)); loadFile(); populateBackupInfo(); return TRUE; } // static void FSFloaterImport::onIdle(void* user_data) { FSFloaterImport* self = (FSFloaterImport*)user_data; self->onIdle(); } void FSFloaterImport::onIdle() { if( getUploads() < 1 ) popNextAsset(); switch(mImportState) { case IDLE: break; case INVENTORY_TRANSFER: { if (mInventoryQueue.empty()) { mImportState = IDLE; mWaitTimer.stop(); if ((mObject + 1) >= mObjectSize) { if (mObjectSize > 1) { // should be allready selected LL_DEBUGS("import") << "Linking " << mObjectSize << " objects. " << LLSelectMgr::getInstance()->getSelection()->getObjectCount() << " prims are selected" << LL_ENDL; LLSelectMgr::getInstance()->sendLink(); mImportState = LINKING; } else { postLink(); } } else { mObject++; createPrim(); } return; } if (mWaitTimer.getElapsedTimeF32() < mThrottleTime) { return; } FSInventoryQueue item_queue = mInventoryQueue.back(); LL_DEBUGS("import") << "Dropping " << item_queue.item->getName() << " " << item_queue.item->getUUID() << " into " << item_queue.prim_name << " " << item_queue.object->getID() << LL_ENDL; if (item_queue.item->getType() == LLAssetType::AT_LSL_TEXT) { LLToolDragAndDrop::dropScript(item_queue.object, item_queue.item, TRUE, LLToolDragAndDrop::SOURCE_AGENT, gAgentID); } else { LLToolDragAndDrop::dropInventory(item_queue.object, item_queue.item, LLToolDragAndDrop::SOURCE_AGENT, gAgentID); } mInventoryQueue.pop_back(); mWaitTimer.start(); } break; case LINKING: if (LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() < 2) { mImportState = IDLE; postLink(); } break; default: break; } } void FSFloaterImport::loadFile() { mFilePath = gDirUtilp->getDirName(mFilename); mFilename = gDirUtilp->getBaseFileName(mFileFullName); mManifest.clear(); mTextureQueue.clear(); mAnimQueue.clear(); mSoundQueue.clear(); mLinksetSize = 0; mTexturesTotal = 0; mAnimsTotal = 0; mSoundsTotal = 0; bool file_loaded = false; llifstream filestream(mFileFullName.c_str(), std::ios_base::in | std::ios_base::binary); if(filestream.is_open()) { filestream.seekg(0, std::ios::end); S32 file_size = (S32)filestream.tellg(); filestream.seekg(0, std::ios::beg); if (LLUZipHelper::unzip_llsd(mManifest, filestream, file_size) == LLUZipHelper::ZR_OK) { file_loaded = true; } else { LL_WARNS("import") << "Failed to deserialize " << mFileFullName << LL_ENDL; } } else { LL_WARNS("import") << "Unable to open file: " << mFileFullName << LL_ENDL; } filestream.close(); if (file_loaded) { if (mManifest.has("format_version") && mManifest["format_version"].asInteger() <= OXP_FORMAT_VERSION) { LLSD& linksetsd = mManifest["linkset"]; U32 prims = 0; for (LLSD::array_iterator linkset_iter = linksetsd.beginArray(); linkset_iter != linksetsd.endArray(); ++linkset_iter) { LLSD& objectsd = mManifest["linkset"][mLinksetSize]; for (LLSD::array_iterator prim_iter = objectsd.beginArray(); prim_iter != objectsd.endArray(); ++prim_iter) { processPrim(mManifest["prim"][(*prim_iter).asString()]); prims++; } mLinksetSize++; } LLUIString stats = getString("file_status"); stats.setArg("[LINKSETS]", llformat("%u", mLinksetSize)); stats.setArg("[PRIMS]", llformat("%u", prims)); stats.setArg("[TEXTURES]", llformat("%u", mTexturesTotal)); stats.setArg("[SOUNDS]", llformat("%u", mSoundsTotal)); stats.setArg("[ANIMATIONS]", llformat("%u", mAnimsTotal)); stats.setArg("[ASSETS]", llformat("%u", mAssetsTotal)); getChild("file_status_text")->setText(stats.getString()); } else { getChild("file_status_text")->setText(getString("file_version_error")); } } else { getChild("file_status_text")->setText(getString("file_status_error")); } LL_DEBUGS("import") << "Linkset size is " << mLinksetSize << LL_ENDL; if (mLinksetSize != 0) { getChild("import_btn")->setEnabled(TRUE); getChild("do_not_attach")->setEnabled(TRUE); getChild("region_position")->setEnabled(TRUE); getChild("upload_asset")->setEnabled(TRUE); } else { getChild("import_btn")->setEnabled(FALSE); getChild("do_not_attach")->setEnabled(FALSE); getChild("region_position")->setEnabled(FALSE); getChild("upload_asset")->setEnabled(FALSE); } } void FSFloaterImport::populateBackupInfo() { childSetTextArg("filename_text", "[FILENAME]", mFilename); childSetTextArg("client_text", "[VERSION]", mManifest["format_version"].asString()); childSetTextArg("client_text", "[CLIENT]", (mManifest.has("client") ? mManifest["client"].asString() : LLTrans::getString("Unknown"))); childSetTextArg("author_text", "[AUTHOR]", (mManifest.has("author") ? mManifest["author"].asString() : LLTrans::getString("Unknown"))); childSetTextArg("author_text", "[GRID]", (mManifest.has("grid") ? "@ " + mManifest["grid"].asString() : LLTrans::getString("Unknown"))); childSetTextArg("creation_date_text", "[DATE_STRING]", (mManifest.has("creation_date") ? mManifest["creation_date"].asString(): LLTrans::getString("Unknown"))); LLUIString title = getString("floater_title"); title.setArg("[FILENAME]", mFilename); setTitle(title); } void FSFloaterImport::processPrim(LLSD& prim) { if (prim.has("texture")) { LLSD& textures = prim["texture"]; for (LLSD::array_iterator texture_iter = textures.beginArray(); texture_iter != textures.endArray(); ++texture_iter) { addAsset((*texture_iter)["imageid"].asUUID(), LLAssetType::AT_TEXTURE); } } if (prim.has("sculpt")) { addAsset(prim["sculpt"]["texture"].asUUID(), LLAssetType::AT_TEXTURE); } if (!prim.has("content")) { return; } LLSD& contentsd = prim["content"]; for (LLSD::array_iterator content_iter = contentsd.beginArray(); content_iter != contentsd.endArray(); ++content_iter) { if (!mManifest["inventory"].has((*content_iter).asString())) { continue; } LLAssetType::EType asset_type = LLAssetType::lookup(mManifest["inventory"][(*content_iter).asString()]["type"].asString().c_str()); LLUUID asset_id = mManifest["inventory"][(*content_iter).asString()]["asset_id"].asUUID(); if (!mManifest["asset"].has(asset_id.asString())) { continue; } addAsset(asset_id, asset_type); std::vector buffer = mManifest["asset"][asset_id.asString()]["data"].asBinary(); switch(asset_type) { case LLAssetType::AT_CLOTHING: case LLAssetType::AT_BODYPART: { std::string asset(buffer.begin(), buffer.end()); S32 position = asset.rfind("textures"); boost::regex pattern("[[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}"); boost::sregex_iterator m1(asset.begin() + position, asset.end(), pattern); boost::sregex_iterator m2; for( ; m1 != m2; ++m1) { LL_DEBUGS("export") << "Found wearable texture " << m1->str() << LL_ENDL; if(LLUUID::validate(m1->str())) { addAsset(LLUUID(m1->str()), LLAssetType::AT_TEXTURE); } else { LL_DEBUGS("export") << "Invalied uuid: " << m1->str() << LL_ENDL; } } } break; case LLAssetType::AT_GESTURE: { buffer.push_back('\0'); LLMultiGesture* gesture = new LLMultiGesture(); LLDataPackerAsciiBuffer dp((char*)&buffer[0], (S32)buffer.size()); if (!gesture->deserialize(dp)) { LL_WARNS("export") << "Unable to load gesture " << asset_id << LL_ENDL; delete gesture; gesture = NULL; break; } S32 i; S32 count = gesture->mSteps.size(); for (i = 0; i < count; ++i) { LLGestureStep* step = gesture->mSteps[i]; switch(step->getType()) { case STEP_ANIMATION: { LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; addAsset(anim_step->mAnimAssetID, LLAssetType::AT_ANIMATION); } break; case STEP_SOUND: { LLGestureStepSound* sound_step = (LLGestureStepSound*)step; addAsset(sound_step->mSoundAssetID, LLAssetType::AT_SOUND); } break; default: break; } } delete gesture; } break; default: break; } } } void FSFloaterImport::addAsset(LLUUID asset_id, LLAssetType::EType asset_type) { if (!mManifest["asset"].has(asset_id.asString())) { LL_DEBUGS("import") << "Missing "<< asset_id.asString() << " asset data." << LL_ENDL; return; } switch(asset_type) { case LLAssetType::AT_TEXTURE: { if (std::find(mTextureQueue.begin(), mTextureQueue.end(), asset_id) == mTextureQueue.end()) { mTextureQueue.push_back(asset_id); mTexturesTotal++; } } break; case LLAssetType::AT_SOUND: { if (std::find(mSoundQueue.begin(), mSoundQueue.end(), asset_id) == mSoundQueue.end()) { mSoundQueue.push_back(asset_id); mSoundsTotal++; } } break; case LLAssetType::AT_ANIMATION: { if (std::find(mAnimQueue.begin(), mAnimQueue.end(), asset_id) == mAnimQueue.end()) { mAnimQueue.push_back(asset_id); mAnimsTotal++; } } break; default: { if (std::find(mAssetQueue.begin(), mAssetQueue.end(), asset_id) == mAssetQueue.end()) { mAssetQueue.push_back(asset_id); mAssetsTotal++; } } break; } } void FSFloaterImport::onClickBtnImport() { mPrimObjectMap.clear(); mLinkset = 0; mObject = 0; mPrim = 0; mObjectSelection = LLSelectMgr::getInstance()->getEditSelection(); LLSelectMgr::getInstance()->deselectAll(); mStartPosition = gAgent.getPositionAgent(); LL_DEBUGS("import") << "gAgent position is " << mStartPosition << LL_ENDL; LLVector3 offset; offset.set(gSavedSettings.getVector3("FSImportBuildOffset")); mStartPosition = mStartPosition + offset * gAgent.getQuat(); LL_DEBUGS("import") << "mStartPosition is " << mStartPosition << LL_ENDL; // don't allow change during a long upload/import getChild("import_btn")->setEnabled(FALSE); getChild("do_not_attach")->setEnabled(FALSE); getChild("region_position")->setEnabled(FALSE); getChild("upload_asset")->setEnabled(FALSE); getChild("temp_asset")->setEnabled(FALSE); if (((mTexturesTotal + mSoundsTotal + mAnimsTotal + mAssetsTotal) != 0) && getChild("upload_asset")->get()) { // do not pop up preview floaters when creating new inventory items. gSavedSettings.setBOOL("ShowNewInventory", false); if (!getChild("temp_asset")->get()) { U32 expected_upload_cost = mTexturesTotal * (U32)LLGlobalEconomy::getInstance()->getPriceUpload(); if(!(can_afford_transaction(expected_upload_cost))) { LLStringUtil::format_map_t args; args["AMOUNT"] = llformat("%d", expected_upload_cost); LLBuyCurrencyHTML::openCurrencyFloater(LLTrans::getString("UploadingCosts", args), expected_upload_cost); // re-enable the controls getChild("import_btn")->setEnabled(TRUE); getChild("do_not_attach")->setEnabled(TRUE); getChild("region_position")->setEnabled(TRUE); getChild("upload_asset")->setEnabled(TRUE); getChild("temp_asset")->setEnabled(getChild("upload_asset")->get()); return; } } if (!mTextureQueue.empty()) { LLUIString status = getString("texture_uploading"); status.setArg("[TEXTURE]", llformat("%u", mTexturesTotal - (U32)mTextureQueue.size() + 1)); status.setArg("[TEXTURETOTAL]", llformat("%u", mTexturesTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mTextureQueue.front()); return; } if (!mSoundQueue.empty()) { LLUIString status = getString("sound_uploading"); status.setArg("[SOUND]", llformat("%u", mSoundsTotal - (U32)mSoundQueue.size() + 1)); status.setArg("[SOUNDTOTAL]", llformat("%u", mSoundsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mSoundQueue.front()); return; } if (!mAnimQueue.empty()) { LLUIString status = getString("animation_uploading"); status.setArg("[ANIMATION]", llformat("%u", mAnimsTotal - (U32)mAnimQueue.size() + 1)); status.setArg("[ANIMATIONTOTAL]", llformat("%u", mAnimsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mAnimQueue.front()); return; } if (!mAssetQueue.empty()) { LLUIString status = getString("asset_uploading"); status.setArg("[ASSET]", llformat("%u", mAssetsTotal - (U32)mAssetQueue.size() + 1)); status.setArg("[ASSETTOTAL]", llformat("%u", mAssetsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mAssetQueue.front()); return; } LL_WARNS("import") << "Nothing in queue, proceding to prim import." << LL_ENDL; // restore setting to allow preview popups. gSavedSettings.setBOOL("ShowNewInventory", mSavedSettingShowNewInventory); importPrims(); } else { importPrims(); } } void FSFloaterImport::onClickCheckBoxUploadAsset() { if (getChild("upload_asset")->get()) { getChild("temp_asset")->setEnabled(TRUE); LLUIString stats = getString("upload_cost"); stats.setArg("[COST]", llformat("%u", (mTexturesTotal + mSoundsTotal + mAnimsTotal) * (U32)LLGlobalEconomy::getInstance()->getPriceUpload())); getChild("file_status_text")->setText(stats.getString()); } else { getChild("temp_asset")->set(FALSE); getChild("temp_asset")->setEnabled(FALSE); std::string text; getChild("file_status_text")->setText(text); } } void FSFloaterImport::onClickCheckBoxTempAsset() { if (getChild("temp_asset")->get()) { LLUIString stats = getString("upload_cost"); stats.setArg("[COST]", llformat("%u", 0)); getChild("file_status_text")->setText(stats.getString()); } else { LLUIString stats = getString("upload_cost"); stats.setArg("[COST]", llformat("%u", (mTexturesTotal + mSoundsTotal + mAnimsTotal) * (U32)LLGlobalEconomy::getInstance()->getPriceUpload())); getChild("file_status_text")->setText(stats.getString()); } } void FSFloaterImport::importPrims() { mObjectSize = 0; LLSD& objectsd = mManifest["linkset"][mLinkset]; for (LLSD::array_iterator iter = objectsd.beginArray(); iter != objectsd.endArray(); ++iter) { mObjectSize++; } LL_DEBUGS("import") << "Object size is " << mObjectSize << LL_ENDL; if (mObjectSize > MAX_PRIMS_PER_OBJECT) { LL_WARNS("import") << "Object size is to large to link. " << mObjectSize << " is greater then max linking size of " << MAX_PRIMS_PER_OBJECT << LL_ENDL; } mRootPosition = mStartPosition; LLUUID linkset_root_prim_uuid = mManifest["linkset"][0][0].asUUID(); LLSD& linkset_root_prim = mManifest["prim"][linkset_root_prim_uuid.asString()]; mLinksetPosition.setValue(linkset_root_prim["position"]); mCreatingActive = true; createPrim(); } void FSFloaterImport::createPrim() { LLUIString status = getString("file_status_running"); status.setArg("[LINKSET]", llformat("%u", mLinkset + 1)); status.setArg("[LINKSETS]", llformat("%u", mLinksetSize)); status.setArg("[PRIM]", llformat("%u", mObject + 1)); status.setArg("[PRIMS]", llformat("%u", mObjectSize)); getChild("file_status_text")->setText(status.getString()); LLUUID prim_uuid = mManifest["linkset"][mLinkset][mObject].asUUID(); LL_DEBUGS("import") << "Creating prim from " << prim_uuid.asString() << LL_ENDL; LLSD& prim = mManifest["prim"][prim_uuid.asString()]; gMessageSystem->newMessageFast(_PREHASH_ObjectAdd); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID); gMessageSystem->addUUIDFast(_PREHASH_GroupID, FSCommon::getGroupForRezzing()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU8Fast(_PREHASH_Material, (U8)prim["material"].asInteger()); U32 flags = ll_U32_from_sd(prim["flags"]); flags |= FLAGS_CREATE_SELECTED; gMessageSystem->addU32Fast(_PREHASH_AddFlags, flags); LLVolumeParams volume_params; volume_params.getPathParams().fromLLSD(prim["volume"]["path"]); volume_params.getProfileParams().fromLLSD(prim["volume"]["profile"]); LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem); gMessageSystem->addU8Fast(_PREHASH_PCode, LL_PCODE_VOLUME); LLVector3 scale; scale.setValue(prim["scale"]); LL_DEBUGS("import") << "Creating prim scale of " << scale << LL_ENDL; gMessageSystem->addVector3Fast(_PREHASH_Scale, scale ); LLQuaternion rotation = ll_quaternion_from_sd(prim["rotation"]); if (mObject != 0) { rotation = rotation * mRootRotation; } else { mRootRotation = rotation; } gMessageSystem->addQuatFast(_PREHASH_Rotation, rotation); LLVector3 position; LLVector3 prim_position; prim_position.setValue(prim["position"]); if (mObject != 0) { position = (prim_position * mRootRotation) + mRootPosition; } else { position = mRootPosition; } if (mObjectCreatedCallback.connected()) { mObjectCreatedCallback.disconnect(); } mObjectCreatedCallback = gObjectList.setNewObjectCallback(boost::bind(&FSFloaterImport::processPrimCreated, this, _1)); LL_DEBUGS("import") << "Creating prim at position " << position << LL_ENDL; gMessageSystem->addVector3Fast(_PREHASH_RayStart, position); gMessageSystem->addVector3Fast(_PREHASH_RayEnd, position); gMessageSystem->addU8Fast(_PREHASH_BypassRaycast, (U8)TRUE); gMessageSystem->addU8Fast(_PREHASH_RayEndIsIntersection, (U8)FALSE); gMessageSystem->addU8Fast(_PREHASH_State, (U8)0); gMessageSystem->addUUIDFast(_PREHASH_RayTargetID, LLUUID::null); gMessageSystem->sendReliable(gAgent.getRegion()->getHost()); LLTrace::add(LLStatViewer::OBJECT_CREATE,1); } bool FSFloaterImport::processPrimCreated(LLViewerObject* object) { if (!(mInstance && mCreatingActive)) { return false; } LLSelectMgr::getInstance()->selectObjectAndFamily(object, TRUE); LLUUID prim_uuid = mManifest["linkset"][mLinkset][mObject].asUUID(); LLSD& prim = mManifest["prim"][prim_uuid.asString()]; LL_DEBUGS("import") << "Processing prim " << prim_uuid.asString() << " for object " << object->getID().asString() << LL_ENDL; mPrimObjectMap[prim_uuid] = object->getID(); U32 object_local_id = object->getLocalID(); // due to raycasting prim placement errors, send prim position update that is more accurite. LLVector3 position; LLVector3 prim_position(prim["position"]); if (mObject != 0) { position = (prim_position * mRootRotation) + mRootPosition; } else { position = mRootPosition; } LL_DEBUGS("import") << "Setting prim at position " << position << LL_ENDL; setPrimPosition(UPD_POSITION, object, position); #if (0) // reserved for possiable future bugfix should rot and scale not be accurite from prim creation. LLQuaternion rotation = ll_quaternion_from_sd(prim["rotation"]); if (mObject != 0) { rotation = rotation * mRootRotation; } LLVector3 scale(prim["scale"]); LL_DEBUGS("import") << "Setting prim scale of " << scale << LL_ENDL; setPrimPosition(UPD_POSITION|UPD_ROTATION|UPD_SCALE, object, position, rotation, scale); #endif S32 texture_count = (S32)object->getNumTEs(); for(S32 face = 0; face < texture_count; face++) { LLTextureEntry texture_entry; if (texture_entry.fromLLSD(prim["texture"][face])) { LL_DEBUGS("import") << "Setting face " << face << " with texture " << prim["texture"][face]["imageid"].asUUID().asString() << LL_ENDL; if (mAssetMap[texture_entry.getID()].notNull()) { texture_entry.setID(mAssetMap[texture_entry.getID()]); LL_DEBUGS("import") << "Replaced " << prim["texture"][face]["imageid"].asUUID().asString() << " with " << texture_entry.getID().asString() << LL_ENDL; } object->setTE((U8)face, texture_entry); } } object->sendTEUpdate(); // [FS:CR] Materials if(prim.has("materials")) { U8 te = 0; LLSD materials = prim["materials"]; for (LLSD::array_iterator m_itr = materials.beginArray() ; m_itr != materials.endArray() ; ++m_itr) { LL_DEBUGS("import") << "Setting materials" << LL_ENDL; LLMaterial* mat = new LLMaterial(); mat->fromLLSD(*m_itr); object->setTEMaterialParams(te, mat); ++te; } } if (prim.has("sculpt")) { LL_DEBUGS("import") << "Found sculpt for " << prim_uuid.asString() << LL_ENDL; LLSculptParams sculpt_params; sculpt_params.fromLLSD(prim["sculpt"]); LL_DEBUGS("import") << "Setting sculpt to " << prim["sculpt"]["texture"].asUUID().asString() << LL_ENDL; if (mAssetMap[sculpt_params.getSculptTexture()].notNull()) { sculpt_params.setSculptTexture(mAssetMap[sculpt_params.getSculptTexture()], sculpt_params.getSculptType()); LL_DEBUGS("import") << "Replaced " << prim["sculpt"]["texture"].asUUID().asString() << " with " << sculpt_params.getSculptTexture().asString() << LL_ENDL; } object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, true); // sets locally and fires off an update to the region. } if (prim.has("flexible")) { LL_DEBUGS("import") << "Found flexiable for " << prim_uuid.asString() << LL_ENDL; LLFlexibleObjectData attributes; attributes.fromLLSD(prim["flexible"]); object->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, attributes, true); } if (prim.has("light")) { LL_DEBUGS("import") << "Found light for " << prim_uuid.asString() << LL_ENDL; LLLightParams light_param_block; light_param_block.fromLLSD(prim["light"]); object->setParameterEntry(LLNetworkData::PARAMS_LIGHT, light_param_block, true); } if (prim.has("light_texture")) { LL_DEBUGS("import") << "Found light_texture for " << prim_uuid.asString() << LL_ENDL; LLLightImageParams new_light_image_param_block; new_light_image_param_block.fromLLSD(prim["light_texture"]); object->setParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE, new_light_image_param_block, true); } if (prim.has("clickaction")) { LL_DEBUGS("import") << "Setting clickaction on " << prim_uuid.asString() << " to " << prim["clickaction"].asInteger() << LL_ENDL; gMessageSystem->newMessageFast(_PREHASH_ObjectClickAction); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); gMessageSystem->addU8("ClickAction", (U8)prim["clickaction"].asInteger()); gMessageSystem->sendReliable(object->getRegion()->getHost()); } std::string prim_name; if (prim.has("name")) { prim_name = prim["name"].asString(); LL_DEBUGS("import") << "Setting name on " << prim_uuid.asString() << " to " << prim_name << LL_ENDL; gMessageSystem->newMessageFast(_PREHASH_ObjectName); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_LocalID, object_local_id); gMessageSystem->addStringFast(_PREHASH_Name, prim_name); gMessageSystem->sendReliable(object->getRegion()->getHost()); } if (prim.has("description")) { LL_DEBUGS("import") << "Setting description on " << prim_uuid.asString() << " to " << prim["description"].asString() << LL_ENDL; gMessageSystem->newMessageFast(_PREHASH_ObjectDescription); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_LocalID, object_local_id); gMessageSystem->addStringFast(_PREHASH_Description, prim["description"].asString()); gMessageSystem->sendReliable(object->getRegion()->getHost()); } if (prim.has("group_mask") || prim.has("everyone_mask") || prim.has("next_owner_mask")) { // can only set one permission bit at a time. LL_DEBUGS("import") << "Setting permissions on " << prim_uuid.asString() << LL_ENDL; gMessageSystem->newMessageFast(_PREHASH_ObjectPermissions); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_HeaderData); gMessageSystem->addBOOLFast(_PREHASH_Override, (BOOL)FALSE); if (prim.has("group_mask")) { U32 group_mask = ll_U32_from_sd(prim["group_mask"]); LL_DEBUGS("import") << "Setting group mask to " << group_mask << LL_ENDL; gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_GROUP); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(group_mask & PERM_MODIFY) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_MODIFY | PERM_MOVE | PERM_COPY); } if (prim.has("everyone_mask")) { U32 everyone_mask = ll_U32_from_sd(prim["everyone_mask"]); LL_DEBUGS("import") << "Setting everyone mask to " << everyone_mask << LL_ENDL; gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_EVERYONE); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(everyone_mask & PERM_MOVE) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_MOVE); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_EVERYONE); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(everyone_mask & PERM_COPY) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_COPY); } if (prim.has("next_owner_mask")) { U32 next_owner_mask = ll_U32_from_sd(prim["next_owner_mask"]); LL_DEBUGS("import") << "Setting next owner mask to " << next_owner_mask << LL_ENDL; gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_NEXT_OWNER); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(next_owner_mask & PERM_MODIFY) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_MODIFY); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_NEXT_OWNER); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(next_owner_mask & PERM_COPY) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_COPY); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object_local_id); gMessageSystem->addU8Fast(_PREHASH_Field, PERM_NEXT_OWNER); gMessageSystem->addBOOLFast(_PREHASH_Set, (BOOL)(next_owner_mask & PERM_TRANSFER) ? TRUE : FALSE); gMessageSystem->addU32Fast(_PREHASH_Mask, PERM_TRANSFER); } gMessageSystem->sendReliable(object->getRegion()->getHost()); } if (prim.has("sale_info")) { LLSaleInfo sale_info; BOOL has_perm_mask; U32 perm_mask; sale_info.fromLLSD(prim["sale_info"], has_perm_mask, perm_mask); if (sale_info.isForSale()) { LL_DEBUGS("import") << "Setting sale info on " << prim_uuid.asString() << LL_ENDL; gMessageSystem->newMessageFast(_PREHASH_ObjectSaleInfo); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_LocalID, object->getLocalID()); sale_info.packMessage(gMessageSystem); gMessageSystem->sendReliable(object->getRegion()->getHost()); } } if (prim.has("ExtraPhysics")) { LLSD sim_features; object->getRegion()->getSimulatorFeatures(sim_features); if (sim_features.has("PhysicsShapeTypes")) { LL_DEBUGS("import") << "Setting physics on " << prim_uuid.asString() << LL_ENDL; LLSD& extra_physics = prim["ExtraPhysics"]; U8 type = (U8)extra_physics["PhysicsShapeType"].asInteger(); F32 density = (F32)extra_physics["Density"].asReal(); F32 friction = (F32)extra_physics["Friction"].asReal(); F32 restitution = (F32)extra_physics["Restitution"].asReal(); F32 gravity = (F32)extra_physics["GravityMultiplier"].asReal(); object->setPhysicsShapeType(type); object->setPhysicsGravity(gravity); object->setPhysicsFriction(friction); object->setPhysicsDensity(density); object->setPhysicsRestitution(restitution); object->updateFlags(TRUE); } } mInventoryQueue.clear(); if (prim.has("content")) { LLSD& contentsd = prim["content"]; for (LLSD::array_iterator content_iter = contentsd.beginArray(); content_iter != contentsd.endArray(); ++content_iter) { if (!mManifest["inventory"].has((*content_iter).asString())) { LL_WARNS("import") << "Inventory content " << (*content_iter) << " was not found in import file." << LL_ENDL; continue; } LLSD& item_sd = mManifest["inventory"][(*content_iter).asString()]; LLUUID asset_id = item_sd["asset_id"].asUUID(); if (asset_id.isNull()) { LL_WARNS("import") << "Item " << item_sd["desc"].asString() << " " << (*content_iter) << " had NULL asset ID." << LL_ENDL; continue; } if (mAssetMap[asset_id].isNull()) { // asset was not uploaded, search inventory using asset_id searchInventory(asset_id, object, prim_name); continue; } LLUUID new_asset_id = mAssetMap[asset_id]; if (mAssetItemMap[new_asset_id].isNull()) { // no item was created for asset, search inventory using new_asset_id searchInventory(new_asset_id, object, prim_name); continue; } LLUUID item_id = mAssetItemMap[new_asset_id]; LLViewerInventoryItem* item = gInventory.getItem(item_id); if (!item) { // else item was not found, search inventory using new_asset id searchInventory(new_asset_id, object, prim_name); continue; } FSInventoryQueue item_queue; item_queue.item = item; item_queue.object = object; item_queue.prim_name = prim_name; mInventoryQueue.push_back(item_queue); } } if (mInventoryQueue.size() < 20) { mThrottleTime = 0.25f; } else { mThrottleTime = 0.5f; } mImportState = INVENTORY_TRANSFER; mWaitTimer.start(); return true; } void FSFloaterImport::searchInventory(LLUUID asset_id, LLViewerObject* object, std::string prim_name) { LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; LLAssetIDMatches asset_id_matches(asset_id); gInventory.collectDescendentsIf(LLUUID::null, cats, items, LLInventoryModel::INCLUDE_TRASH, asset_id_matches); if (items.size()) { LLViewerInventoryItem* item = items.at(0); FSInventoryQueue item_queue; item_queue.item = item; item_queue.object = object; item_queue.prim_name = prim_name; mInventoryQueue.push_back(item_queue); } else { LL_WARNS("import") << "Asset " << asset_id << " was not found in inventory. Asset not added to object." << LL_ENDL; } } void FSFloaterImport::postLink() { if (!mCreatingActive) { LL_WARNS("import") << "Post link called without being active." << LL_ENDL; return; } LLUUID root_prim_uuid = mManifest["linkset"][mLinkset][0].asUUID(); LLSD& root_prim = mManifest["prim"][root_prim_uuid.asString()]; if (root_prim.has("attachment_point") && !getChild("do_not_attach")->get()) { LL_DEBUGS("import") << "Attaching to " << root_prim["attachment_point"].asInteger() << LL_ENDL; LLSelectMgr::getInstance()->sendAttach((U8)root_prim["attachment_point"].asInteger(), false); LLViewerObject* root_object = gObjectList.findObject(mPrimObjectMap[root_prim_uuid]); setPrimPosition(UPD_POSITION|UPD_LINKED_SETS, root_object, LLVector3(root_prim["position"])); } if (getChild("region_position")->get()) { if (root_prim.has("attachment_point")) { LL_DEBUGS("import") << "Object is an attachment, can not retore to region position." << LL_ENDL; } else { LLViewerObject* root_object = gObjectList.findObject(mPrimObjectMap[root_prim_uuid]); setPrimPosition(UPD_POSITION|UPD_LINKED_SETS, root_object, LLVector3(root_prim["position"])); } } LLSelectMgr::getInstance()->deselectAll(); if ((mLinkset + 1) >= mLinksetSize) { // all done mCreatingActive = false; LL_DEBUGS("import") << "Finished with " << mLinkset << " linksets and " << mObject << " prims in last linkset" << LL_ENDL; mObjectSelection = NULL; getChild("file_status_text")->setText(getString("file_status_done")); } else { mObject = 0; mLinkset++; mObjectSize = 0; LLSD& objectsd = mManifest["linkset"][mLinkset]; for ( LLSD::array_iterator iter = objectsd.beginArray(); iter != objectsd.endArray(); ++iter) { mObjectSize++; } LL_DEBUGS("import") << "Next linkset Object size is " << mObjectSize << LL_ENDL; LLUUID root_prim_uuid = mManifest["linkset"][mLinkset][0].asUUID(); LLSD& root_prim = mManifest["prim"][root_prim_uuid.asString()]; LLVector3 root_prim_location(root_prim["position"]); mRootPosition = mStartPosition + (root_prim_location - mLinksetPosition); createPrim(); } } void FSFloaterImport::setPrimPosition(U8 type, LLViewerObject* object, LLVector3 position, LLQuaternion rotation, LLVector3 scale) { gMessageSystem->newMessage(_PREHASH_MultipleObjectUpdate); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); gMessageSystem->addU8Fast(_PREHASH_Type, type); U8 data[256]; S32 offset = 0; if (type & UPD_POSITION) { htonmemcpy(&data[offset], &(position.mV), MVT_LLVector3, 12); offset += 12; } if (type & UPD_ROTATION) { LLVector3 vec = rotation.packToVector3(); htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12); offset += 12; } if (type & UPD_SCALE) { htonmemcpy(&data[offset], &(scale.mV), MVT_LLVector3, 12); offset += 12; } gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset); gMessageSystem->sendReliable(object->getRegion()->getHost()); } void FSFloaterImport::uploadAsset(LLUUID asset_id, LLUUID inventory_item) { bool temporary = false; std::vector asset_data = mManifest["asset"][asset_id.asString()]["data"].asBinary(); std::string name = mManifest["asset"][asset_id.asString()]["name"].asString(); std::string description = mManifest["asset"][asset_id.asString()]["description"].asString(); LLAssetType::EType asset_type = LLAssetType::lookup(mManifest["asset"][asset_id.asString()]["type"].asString().c_str()); std::string url; LLSD body = LLSD::emptyMap(); LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); LLUUID folder_id = gInventory.findCategoryUUIDForType(folder_type); LLInventoryType::EType inventory_type = LLInventoryType::defaultForAssetType(asset_type); bool new_file_agent_inventory = false; LLWearableType::EType wearable_type = NOT_WEARABLE; std::string perms_prefix = ""; if (name.empty()) { name = llformat("Uploaded %s %s", LLAssetType::lookupHumanReadable(asset_type), asset_id.asString().c_str()); } switch(asset_type) { case LLAssetType::AT_TEXTURE: { perms_prefix = "Uploads"; temporary = getChild("temp_asset")->get(); if (temporary) { url = gAgent.getRegionCapability("UploadBakedTexture"); } else { url = gAgent.getRegionCapability("NewFileAgentInventory"); new_file_agent_inventory = true; } LLTrace::add(LLStatViewer::UPLOAD_TEXTURE,1); } break; case LLAssetType::AT_SOUND: { perms_prefix = "Uploads"; temporary = getChild("temp_asset")->get(); if (temporary) { // skip upload due to no temp support for sound pushNextAsset(LLUUID::null, asset_id, asset_type); return; } else { url = gAgent.getRegionCapability("NewFileAgentInventory"); new_file_agent_inventory = true; LLTrace::add(LLStatViewer::UPLOAD_SOUND,1); } } break; case LLAssetType::AT_CLOTHING: case LLAssetType::AT_BODYPART: { perms_prefix = "Wearables"; std::string asset(asset_data.begin(), asset_data.end()); S32 position = asset.rfind("type"); S32 end = asset.find("\n", position); wearable_type = (LLWearableType::EType)boost::lexical_cast(asset.substr(position + 5, (end - (position + 5)))); if (getChild("temp_asset")->get()) { // wearables don't support using temp textures. break; } position = asset.rfind("textures"); boost::regex pattern("[[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}"); boost::sregex_iterator m1(asset.begin() + position, asset.end(), pattern); boost::sregex_iterator m2; bool replace = false; for( ; m1 != m2; ++m1) { LL_DEBUGS("export") << "Found wearable texture " << m1->str() << LL_ENDL; if(LLUUID::validate(m1->str())) { LLUUID texture_id = LLUUID(m1->str()); if (mAssetMap[texture_id].notNull()) { asset.replace(m1->position(), m1->length(), mAssetMap[texture_id].asString()); replace = true; LL_DEBUGS("export") << "Replaced wearable texture " << m1->str() << " with " << mAssetMap[texture_id].asString() << LL_ENDL; } } else { LL_DEBUGS("export") << "Invalied uuid: " << m1->str() << LL_ENDL; } } if (replace) { std::copy(asset.begin(), asset.end(), asset_data.begin()); } } break; case LLAssetType::AT_NOTECARD: { perms_prefix = "Notecards"; if (inventory_item.isNull()) { // create inventory item first FSResourceData* ci_data = new FSResourceData; ci_data->uuid = asset_id; ci_data->mFloater = this; ci_data->asset_type = asset_type; ci_data->post_asset_upload = false; LLPointer cb = new FSCreateItemCallback(ci_data); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, LLTransactionID::tnull, name, description, asset_type, inventory_type, NOT_WEARABLE, PERM_ALL, cb); return; } else { url = gAgent.getRegionCapability("UpdateNotecardAgentInventory"); body["item_id"] = inventory_item; } } break; case LLAssetType::AT_LSL_TEXT: { perms_prefix = "Scripts"; if (inventory_item.isNull()) { // create inventory item first FSResourceData* ci_data = new FSResourceData; ci_data->uuid = asset_id; ci_data->mFloater = this; ci_data->asset_type = asset_type; ci_data->post_asset_upload = false; LLPointer cb = new FSCreateItemCallback(ci_data); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, LLTransactionID::tnull, name, description, asset_type, inventory_type, NOT_WEARABLE, PERM_MOVE | PERM_TRANSFER, cb); return; } else { url = gAgent.getRegionCapability("UpdateScriptAgent"); body["item_id"] = inventory_item; if (gSavedSettings.getBOOL("FSSaveInventoryScriptsAsMono")) { body["target"] = "mono"; } else { body["target"] = "lsl2"; } } } break; case LLAssetType::AT_ANIMATION: { perms_prefix = "Uploads"; temporary = getChild("temp_asset")->get(); if (temporary) { // no temp support, skip pushNextAsset(LLUUID::null, asset_id, asset_type); return; } else { url = gAgent.getRegionCapability("NewFileAgentInventory"); new_file_agent_inventory = true; LLTrace::add(LLStatViewer::ANIMATION_UPLOADS,1); } } break; case LLAssetType::AT_GESTURE: { perms_prefix = "Gestures"; if (inventory_item.isNull()) { // create inventory item first FSResourceData* ci_data = new FSResourceData; ci_data->uuid = asset_id; ci_data->mFloater = this; ci_data->asset_type = asset_type; ci_data->post_asset_upload = false; LLPointer cb = new FSCreateItemCallback(ci_data); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, LLTransactionID::tnull, name, description, asset_type, inventory_type, NOT_WEARABLE, PERM_ALL, cb); return; } else { url = gAgent.getRegionCapability("UpdateGestureAgentInventory"); body["item_id"] = inventory_item; asset_data.push_back('\0'); LLMultiGesture* gesture = new LLMultiGesture(); LLDataPackerAsciiBuffer dp((char*)&asset_data[0], (S32)asset_data.size()); if (!gesture->deserialize(dp)) { LL_WARNS("export") << "Unable to load gesture " << asset_id << LL_ENDL; delete gesture; break; } S32 i; S32 count = gesture->mSteps.size(); bool replace = false; for (i = 0; i < count; ++i) { LLGestureStep* step = gesture->mSteps[i]; switch(step->getType()) { case STEP_ANIMATION: { LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; if (mAssetMap[anim_step->mAnimAssetID].notNull()) { LL_DEBUGS("export") << "Replaced animation " << anim_step->mAnimAssetID.asString() << " with " << mAssetMap[anim_step->mAnimAssetID].asString() << LL_ENDL; anim_step->mAnimAssetID = mAssetMap[anim_step->mAnimAssetID]; replace = true; } } break; case STEP_SOUND: { LLGestureStepSound* sound_step = (LLGestureStepSound*)step; if (mAssetMap[sound_step->mSoundAssetID].notNull()) { LL_DEBUGS("export") << "Replaced sound " << sound_step->mSoundAssetID.asString() << " with " << mAssetMap[sound_step->mSoundAssetID].asString() << LL_ENDL; sound_step->mSoundAssetID = mAssetMap[sound_step->mSoundAssetID]; replace = true; } } break; default: break; } } if (replace) { S32 max_size = gesture->getMaxSerialSize(); char* buffer = new char[max_size]; LLDataPackerAsciiBuffer dp(buffer, max_size); if (!gesture->serialize(dp)) { LL_WARNS("export") << "Unable to serialize gesture " << asset_id << LL_ENDL; delete[] buffer; delete gesture; break; } S32 size = dp.getCurrentSize(); asset_data.assign(buffer, buffer + size); delete[] buffer; } delete gesture; } } break; default: { url = gAgent.getRegionCapability("NewFileAgentInventory"); new_file_agent_inventory = true; } break; } LLTransactionID tid; tid.generate(); LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); LLVFile::writeFile((U8*)&asset_data[0], (S32)asset_data.size(), gVFS, new_asset_id, asset_type); LLResourceData* data( new LLResourceData ); data->mAssetInfo.mTransactionID = tid; data->mAssetInfo.mUuid = new_asset_id; data->mAssetInfo.mType = asset_type; data->mAssetInfo.mCreatorID = gAgentID; data->mInventoryType = inventory_type; data->mNextOwnerPerm = LLFloaterPerms::getNextOwnerPerms(perms_prefix); data->mExpectedUploadCost = LLGlobalEconomy::getInstance()->getPriceUpload(); FSResourceData* fs_data = new FSResourceData; fs_data->uuid = asset_id; fs_data->mFloater = this; fs_data->temporary = temporary; fs_data->inventory_item = inventory_item; fs_data->wearable_type = wearable_type; fs_data->asset_type = asset_type; data->mUserData = fs_data; data->mAssetInfo.setName(name); data->mAssetInfo.setDescription(description); data->mPreferredLocation = folder_type; if(!url.empty()) { if (new_file_agent_inventory) { body["folder_id"] = folder_id; body["asset_type"] = LLAssetType::lookup(asset_type); body["inventory_type"] = LLInventoryType::lookup(inventory_type); body["name"] = name; body["description"] = description; body["next_owner_mask"] = LLSD::Integer(LLFloaterPerms::getNextOwnerPerms(perms_prefix)); body["group_mask"] = LLSD::Integer(LLFloaterPerms::getGroupPerms(perms_prefix)); body["everyone_mask"] = LLSD::Integer(LLFloaterPerms::getEveryonePerms(perms_prefix)); } boost::shared_ptr< LLResourceData> pData( data, resourceDeleter ); LLCoprocedureManager::instance().enqueueCoprocedure( "FSImporter", "Upload asset", boost::bind( uploadCoroutine, _1, url, body, new_asset_id, asset_type, pData ) ); LL_DEBUGS("import") << "Asset upload via capability of " << new_asset_id.asString() << " to " << url << " of " << asset_id.asString() << LL_ENDL; } else { gAssetStorage->storeAssetData(tid,asset_type,FSFloaterImport::onAssetUploadComplete, data, temporary, temporary, temporary); LL_DEBUGS("import") << "Asset upload via AssetStorage of " << new_asset_id.asString() << " of " << asset_id.asString() << LL_ENDL; } } // static void FSFloaterImport::onAssetUploadComplete(const LLUUID& uuid, void* userdata, S32 result, LLExtStat ext_status) { LLResourceData* data = (LLResourceData*)userdata; if (!data) { LL_WARNS("import") << "Got Asset upload complete without data info" << LL_ENDL; return; } FSResourceData* fs_data = (FSResourceData*)data->mUserData; if (!fs_data) { LL_WARNS("import") << "Missing FSResourceData, can not continue." << LL_ENDL; return; } FSFloaterImport* self = fs_data->mFloater; LLUUID asset_id = uuid; if (result >= 0) { const LLUUID folder_id = gInventory.findCategoryUUIDForType(data->mPreferredLocation); if (fs_data->inventory_item.isNull()) { if (fs_data->temporary) { LLUUID item_id; item_id.generate(); LLPermissions perm; perm.init(gAgentID, gAgentID, gAgentID, gAgentID); perm.setMaskBase(PERM_ALL); perm.setMaskOwner(PERM_ALL); perm.setMaskEveryone(PERM_ALL); perm.setMaskGroup(PERM_ALL); LLPointer item = new LLViewerInventoryItem(item_id, folder_id, perm, data->mAssetInfo.mTransactionID.makeAssetID(gAgent.getSecureSessionID()), data->mAssetInfo.mType, data->mInventoryType, data->mAssetInfo.getName(), "Temporary asset", LLSaleInfo::DEFAULT, LLInventoryItemFlags::II_FLAGS_NONE, time_corrected()); gInventory.updateItem(item); gInventory.notifyObservers(); } else { if (LLAssetType::AT_SOUND == data->mAssetInfo.mType || LLAssetType::AT_TEXTURE == data->mAssetInfo.mType || LLAssetType::AT_ANIMATION == data->mAssetInfo.mType) { // Charge the user for the upload. S32 expected_upload_cost = data->mExpectedUploadCost; LLViewerRegion* region = gAgent.getRegion(); if(region) { // Charge user for upload gStatusBar->debitBalance(expected_upload_cost); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoneyTransferRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID()); msg->addUUIDFast(_PREHASH_DestID, LLUUID::null); msg->addU8("Flags", 0); // we tell the sim how much we were expecting to pay so it // can respond to any discrepancy msg->addS32Fast(_PREHASH_Amount, expected_upload_cost); msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY); msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY); msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE); msg->addStringFast(_PREHASH_Description, NULL); msg->sendReliable(region->getHost()); } } // Actually add the upload to inventory LL_DEBUGS("import") << "Adding " << asset_id << " to inventory." << LL_ENDL; if(folder_id.notNull()) { U32 next_owner_perms = data->mNextOwnerPerm; if(PERM_NONE == next_owner_perms) { next_owner_perms = PERM_MOVE | PERM_TRANSFER; } fs_data->post_asset_upload = true; fs_data->post_asset_upload_id = asset_id; LLPointer cb = new FSCreateItemCallback(fs_data); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(), data->mAssetInfo.getDescription(), data->mAssetInfo.mType, data->mInventoryType, fs_data->wearable_type, next_owner_perms, cb); // delete data; // can this be done without deleting fs_data that still is in use? return; } else { LL_WARNS("import") << "Can't find a folder to put asset in" << LL_ENDL; } } } else { // Saving into user inventory LLViewerInventoryItem* item; item = (LLViewerInventoryItem*)gInventory.getItem(fs_data->inventory_item); if(item) { LLPointer new_item = new LLViewerInventoryItem(item); new_item->setDescription(data->mAssetInfo.getDescription()); new_item->setTransactionID(data->mAssetInfo.mTransactionID); new_item->setAssetUUID(asset_id); new_item->updateServer(FALSE); gInventory.updateItem(new_item); gInventory.notifyObservers(); LL_DEBUGS("import") << "Asset " << asset_id << " saved into " << "inventory item " << item->getName() << LL_ENDL; self->mAssetItemMap[asset_id] = fs_data->inventory_item; } else { LL_WARNS("import") << "Inventory item for asset " << asset_id << " is no longer in agent inventory. Skipping to next asset upload." << LL_ENDL; asset_id = LLUUID::null; } } } else { LLSD args; args["FILE"] = LLInventoryType::lookupHumanReadable(data->mInventoryType); args["REASON"] = std::string(LLAssetStorage::getErrorString(result)); LLNotificationsUtil::add("CannotUploadReason", args); } if (self) { self->pushNextAsset(asset_id, fs_data->uuid, data->mAssetInfo.mType); } else { LL_WARNS("import") << "Unable to upload next asset due to missing self." << LL_ENDL; } delete fs_data; delete data; } void FSFloaterImport::pushNextAsset( LLUUID new_uuid, LLUUID asset_id, LLAssetType::EType asset_type ) { NextAsset oAsset; oAsset.new_uuid = new_uuid; oAsset.asset_id = asset_id; oAsset.asset_type = asset_type; m_NextAssets.push_back( oAsset ); } void FSFloaterImport::popNextAsset() { if( m_NextAssets.empty() ) return; NextAsset oAsset = m_NextAssets.front(); m_NextAssets.pop_front(); LLUUID new_uuid = oAsset.new_uuid; LLUUID asset_id = oAsset.asset_id; LLAssetType::EType asset_type = oAsset.asset_type; if (gDisconnected) { // on logout, all assest uploads callbacks are fired. // this fixes a crash on logout if there are still pending uploads. // Crash is due to ~distructor is called for *this before the callbacks are fired. return; } switch(asset_type) { case LLAssetType::AT_TEXTURE: { uuid_vec_t::iterator iter = std::find(mTextureQueue.begin(), mTextureQueue.end(), asset_id); if ( iter != mTextureQueue.end()) { if (new_uuid.notNull()) { mAssetMap[asset_id] = new_uuid; } mTextureQueue.erase(iter); } else { LL_WARNS("import") << "Error with texture upload, got callback without texture in mTextureQueue." << LL_ENDL; return; } } break; case LLAssetType::AT_SOUND: { uuid_vec_t::iterator iter = std::find(mSoundQueue.begin(), mSoundQueue.end(), asset_id); if ( iter != mSoundQueue.end()) { if (new_uuid.notNull()) { mAssetMap[asset_id] = new_uuid; } mSoundQueue.erase(iter); } else { LL_WARNS("import") << "Error with sound upload, got callback without sound in mSoundQueue." << LL_ENDL; return; } } break; case LLAssetType::AT_ANIMATION: { uuid_vec_t::iterator iter = std::find(mAnimQueue.begin(), mAnimQueue.end(), asset_id); if ( iter != mAnimQueue.end()) { if (new_uuid.notNull()) { mAssetMap[asset_id] = new_uuid; } mAnimQueue.erase(iter); } else { LL_WARNS("import") << "Error with animation upload, got callback without animation in mAnimQueue." << LL_ENDL; return; } } break; default: { uuid_vec_t::iterator iter = std::find(mAssetQueue.begin(), mAssetQueue.end(), asset_id); if ( iter != mAssetQueue.end()) { if (new_uuid.notNull()) { mAssetMap[asset_id] = new_uuid; } mAssetQueue.erase(iter); } else { LL_WARNS("import") << "Error with asset upload, got callback without asset in mAssetQueue." << LL_ENDL; return; } } break; } if (!mTextureQueue.empty()) { LLUIString status = getString("texture_uploading"); status.setArg("[TEXTURE]", llformat("%u", mTexturesTotal - (U32)mTextureQueue.size() + 1)); status.setArg("[TEXTURETOTAL]", llformat("%u", mTexturesTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mTextureQueue.front()); return; } if (!mSoundQueue.empty()) { LLUIString status = getString("sound_uploading"); status.setArg("[SOUND]", llformat("%u", mSoundsTotal - (U32)mSoundQueue.size() + 1)); status.setArg("[SOUNDTOTAL]", llformat("%u", mSoundsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mSoundQueue.front()); return; } if (!mAnimQueue.empty()) { LLUIString status = getString("animation_uploading"); status.setArg("[ANIMATION]", llformat("%u", mAnimsTotal - (U32)mAnimQueue.size() + 1)); status.setArg("[ANIMATIONTOTAL]", llformat("%u", mAnimsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mAnimQueue.front()); return; } if (!mAssetQueue.empty()) { LLUIString status = getString("asset_uploading"); status.setArg("[ASSET]", llformat("%u", mAssetsTotal - (U32)mAssetQueue.size() + 1)); status.setArg("[ASSETTOTAL]", llformat("%u", mAssetsTotal)); getChild("file_status_text")->setText(status.getString()); uploadAsset(mAssetQueue.front()); return; } LL_DEBUGS("import") << "Nothing left in upload queue, proceding to prim import." << LL_ENDL; // restore setting to allow preview popups. gSavedSettings.setBOOL("ShowNewInventory", mSavedSettingShowNewInventory); importPrims(); } FSCreateItemCallback::FSCreateItemCallback(FSResourceData* data) : mData(data) { } void FSCreateItemCallback::fire(const LLUUID& inv_item) { FSFloaterImport* self = mData->mFloater; if (inv_item.isNull()) { LL_WARNS( "import" ) << "Item creation for asset failed, skipping " << mData->uuid.asString() << LL_ENDL; if (self) { self->pushNextAsset( LLUUID::null, mData->uuid, mData->asset_type ); } else { LL_WARNS("import") << "Unable to upload next asset due to missing self." << LL_ENDL; } return; } if( mData->post_asset_upload ) { self->mAssetItemMap[ mData->post_asset_upload_id ] = inv_item; self->pushNextAsset( mData->post_asset_upload_id, mData->uuid, mData->asset_type ); } else { self->uploadAsset( mData->uuid, inv_item ); } } class UploadFlag { FSFloaterImport *mFloater; public: UploadFlag( FSFloaterImport *aFloater ) : mFloater( aFloater ) { if( mFloater ) mFloater->startUpload(); } ~UploadFlag() { if( mFloater ) mFloater->uploadDone(); } }; void uploadCoroutine( LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &a_httpAdapter, std::string aURL, LLSD aBody, LLAssetID aAssetId, LLAssetType::EType aAssetType, boost::shared_ptr< LLResourceData > aResourceData ) { FSResourceData* fs_data = (FSResourceData*)aResourceData->mUserData; FSFloaterImport* self = fs_data->mFloater; UploadFlag uploadDone( self ); LLCore::HttpRequest::ptr_t pRequest( new LLCore::HttpRequest() ); LLSD postResult = a_httpAdapter->postAndSuspend( pRequest, aURL, aBody ); LLSD httpResults = postResult[ LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS ]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if( !status ) { LL_WARNS( "import" ) << "Error " << status.toULong() << " reason: " << status.toString() << LL_ENDL; LL_WARNS( "import" ) << "Skipping " << fs_data->uuid.asString() << " due to upload error." << LL_ENDL; if( self ) { self->pushNextAsset( LLUUID::null, fs_data->uuid, aResourceData->mAssetInfo.mType ); } else { LL_WARNS( "import" ) << "Unable to upload next asset due to missing self." << LL_ENDL; } } std::string uploader = postResult[ "uploader" ].asString(); if( uploader.empty() ) { LL_WARNS( "import" ) << "No upload url provided. Nothing uploaded." << LL_ENDL; self->pushNextAsset( LLUUID::null, fs_data->uuid, aResourceData->mAssetInfo.mType ); return; } if (!gVFS->getExists(aAssetId, aAssetType)) { LL_WARNS() << "Asset doesn't exist in VFS anymore. Nothing uploaded." << LL_ENDL; self->pushNextAsset( LLUUID::null, fs_data->uuid, aResourceData->mAssetInfo.mType ); return; } LLSD postContentResult; { pRequest.reset( new LLCore::HttpRequest() ); postContentResult = a_httpAdapter->postFileAndSuspend( pRequest, uploader, aAssetId, aAssetType ); LLSD httpResults = postContentResult[ LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS ]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD( httpResults ); std::string ulstate = postContentResult[ "state" ].asString(); if( (!status) || (ulstate != "complete") ) { self->pushNextAsset( LLUUID::null, fs_data->uuid, aResourceData->mAssetInfo.mType ); LL_WARNS( "import" ) << "Asset upload failed." << ll_pretty_print_sd( postContentResult) << LL_ENDL; return; } } LLUUID item_id = aBody[ "item_id" ]; std::string responseResult = postContentResult[ "state" ]; LLUUID new_id = postContentResult[ "new_asset" ]; LL_DEBUGS( "import" ) << "result: " << responseResult << " new_id: " << new_id << LL_ENDL; // rename the file in the VFS to the actual asset id gVFS->renameFile(aAssetId, aAssetType, new_id, aAssetType); if( item_id.isNull() ) { if( fs_data->temporary ) { if( responseResult == "complete" ) { LLUUID folder_id( gInventory.findCategoryUUIDForType( aResourceData->mPreferredLocation ) ); LLUUID temp_item_id; temp_item_id.generate(); LLPermissions perm; perm.init( gAgentID, gAgentID, gAgentID, gAgentID ); perm.setMaskBase( PERM_ALL ); perm.setMaskOwner( PERM_ALL ); perm.setMaskEveryone( PERM_ALL ); perm.setMaskGroup( PERM_ALL ); LLPointer item = new LLViewerInventoryItem( temp_item_id, folder_id, perm, new_id, aResourceData->mAssetInfo.mType, aResourceData->mInventoryType, aResourceData->mAssetInfo.getName(), "Temporary asset", LLSaleInfo::DEFAULT, LLInventoryItemFlags::II_FLAGS_NONE, time_corrected() ); gInventory.updateItem( item ); gInventory.notifyObservers(); } else { LL_WARNS( "import" ) << "Unable to add texture, got bad result." << LL_ENDL; new_id = LLUUID::null; } } else { LLAssetType::EType asset_type = LLAssetType::lookup( aBody[ "asset_type" ].asString() ); LLInventoryType::EType inventory_type = LLInventoryType::lookup( aBody[ "inventory_type" ].asString() ); S32 upload_price = LLGlobalEconomy::getInstance()->getPriceUpload(); const std::string inventory_type_string = aBody[ "asset_type" ].asString(); const LLUUID& item_folder_id = aBody[ "folder_id" ].asUUID(); const std::string& item_name = aBody[ "name" ]; const std::string& item_description = aBody[ "description" ]; if( upload_price > 0 ) { // this upload costed us L$, update our balance // and display something saying that it cost L$ LLStatusBar::sendMoneyBalanceRequest(); // FIRE-10628 - Option to supress upload cost notification if( gSavedSettings.getBOOL( "FSShowUploadPaymentToast" ) ) { LLSD args; args[ "AMOUNT" ] = llformat( "%d", upload_price ); LLNotificationsUtil::add( "UploadPayment", args ); } // } if( item_folder_id.notNull() ) { U32 everyone_perms = PERM_NONE; U32 group_perms = PERM_NONE; U32 next_owner_perms = PERM_ALL; if( postContentResult.has( "new_next_owner_mask" ) ) { // The server provided creation perms so use them. // Do not assume we got the perms we asked for in // since the server may not have granted them all. everyone_perms = postContentResult[ "new_everyone_mask" ].asInteger(); group_perms = postContentResult[ "new_group_mask" ].asInteger(); next_owner_perms = postContentResult[ "new_next_owner_mask" ].asInteger(); } else { // The server doesn't provide creation perms // so use old assumption-based perms. if( inventory_type_string != "snapshot" ) { next_owner_perms = PERM_MOVE | PERM_TRANSFER; } } LLPermissions new_perms; new_perms.init( gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null ); new_perms.initMasks( PERM_ALL, PERM_ALL, everyone_perms, group_perms, next_owner_perms ); U32 inventory_item_flags = 0; if( postContentResult.has( "inventory_flags" ) ) { inventory_item_flags = (U32)postContentResult[ "inventory_flags" ].asInteger(); if( inventory_item_flags != 0 ) { LL_INFOS() << "inventory_item_flags " << inventory_item_flags << LL_ENDL; } } S32 creation_date_now = time_corrected(); LLPointer item = new LLViewerInventoryItem( postContentResult[ "new_inventory_item" ].asUUID(), item_folder_id, new_perms, postContentResult[ "new_asset" ].asUUID(), asset_type, inventory_type, item_name, item_description, LLSaleInfo::DEFAULT, inventory_item_flags, creation_date_now ); gInventory.updateItem( item ); gInventory.notifyObservers(); } else { LL_WARNS( "import" ) << "Can't find a folder to put the texture in" << LL_ENDL; } } } else { LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem( item_id ); if( !item ) { LL_WARNS( "import" ) << "Inventory item for " << aAssetId << " is no longer in agent inventory. Skipping to next asset upload." << LL_ENDL; new_id = LLUUID::null; } else { // Update viewer inventory item LLPointer new_item = new LLViewerInventoryItem( item ); new_item->setAssetUUID( postContentResult[ "new_asset" ].asUUID() ); gInventory.updateItem( new_item ); gInventory.notifyObservers(); LL_DEBUGS( "import" ) << "Asset " << postContentResult[ "new_asset" ].asString() << " saved into " << "inventory item " << item->getName() << LL_ENDL; self->mAssetItemMap[ postContentResult[ "new_asset" ].asUUID() ] = item_id; } } if( self ) { self->pushNextAsset( new_id, fs_data->uuid, aResourceData->mAssetInfo.mType ); } else { LL_WARNS( "import" ) << "Unable to upload next asset due to missing self." << LL_ENDL; } }