diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp index 379465eb2..6f163dd16 100644 --- a/indra/llappearance/llwearable.cpp +++ b/indra/llappearance/llwearable.cpp @@ -69,44 +69,6 @@ LLAssetType::EType LLWearable::getAssetType() const return LLWearableType::getAssetType(mType); } -extern void dump_visual_param(LLAPRFile& file, LLVisualParam const* viewer_param, F32 value); - -// Replace '--' with '- -', see http://en.wikipedia.org/wiki/XML#Comments -std::string XMLCommentEscape(std::string const& comment) -{ - std::string result = comment; - std::string::size_type off = std::string::npos; - while ((off = result.rfind("--", off)) != std::string::npos) - { - result.replace(off, 2, "- -"); - } - return result; -} - -void LLWearable::archetypeExport(LLAPRFile& file) const -{ - apr_file_t* fp = file.getFileHandle(); - - apr_file_printf(fp, "\n\t\t\n", getTypeName().c_str()); - apr_file_printf(fp, "\t\t\n", XMLCommentEscape(mName).c_str()); - apr_file_printf(fp, "\t\t\n", XMLCommentEscape(mDescription).c_str()); - apr_file_printf(fp, "\t\t\n", LLDate::now().asString().c_str()); - - for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); ++iter) - { - LLVisualParam const* param = iter->second; - dump_visual_param(file, param, param->getWeight()); - } - for (te_map_t::const_iterator iter = mTEMap.begin(); iter != mTEMap.end(); ++iter) - { - S32 te = iter->first; - LLUUID const& image_id = iter->second->getID(); - apr_file_printf(fp, "\t\t\n", te, image_id.asString().c_str()); - } - - apr_file_printf(fp, "\n"); -} - BOOL LLWearable::exportFile(LLFILE* fp) const { llofstream ofs(fp); diff --git a/indra/llappearance/llwearable.h b/indra/llappearance/llwearable.h index f381835be..b6049eb21 100644 --- a/indra/llappearance/llwearable.h +++ b/indra/llappearance/llwearable.h @@ -78,8 +78,6 @@ public: virtual void writeToAvatar(LLAvatarAppearance* avatarp); - void archetypeExport(LLAPRFile& file) const; - enum EImportResult { FAILURE = 0, diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 9a1e4b3b9..259fa091f 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -2569,6 +2569,19 @@ std::string LLXMLNode::escapeXML(const std::string& xml) return out; } +// Replace '--' with '- -', see http://en.wikipedia.org/wiki/XML#Comments +// static +std::string LLXMLNode::commentEscape(std::string const& comment) +{ + std::string result = comment; + std::string::size_type off = std::string::npos; + while ((off = result.rfind("--", off)) != std::string::npos) + { + result.replace(off, 2, "- -"); + } + return result; +} + void LLXMLNode::setStringValue(U32 length, const std::string *strings) { if (length == 0) return; diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index 6b7e3bfa0..78572603d 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -279,6 +279,8 @@ public: // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt) static std::string escapeXML(const std::string& xml); + // Escapes -- (double-hyphen) + static std::string commentEscape(std::string const& comment); // Set the default node corresponding to this default node void setDefault(LLXMLNode *default_node); diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 2f6214e5b..c55ff49d4 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -82,6 +82,7 @@ #include "statemachine/aifilepicker.h" #include "llxmltree.h" +#include "hippogridmanager.h" using namespace LLAvatarAppearanceDefines; @@ -314,18 +315,21 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLViewerWearable* edit_wearable = panel_edit_wearable->getWearable(); std::string const filename = filepicker->getFilename(); + LLSD args(LLSD::emptyMap()); + args["FILE"] = gDirUtilp->getBaseFileName(filename); LLXmlTree xml; BOOL success = xml.parseFile(filename, FALSE); if (!success) { - llwarns << "Could not read or parse wearable import file \"" << filename << "\"." << llendl; + LLNotificationsUtil::add("AIXMLImportParseError", args); return; } LLXmlTreeNode* root = xml.getRoot(); if (!root) { llwarns << "No root node found in wearable import file: " << filename << llendl; + LLNotificationsUtil::add("AIXMLImportParseError", args); return; } @@ -335,6 +339,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) if (!root->hasName("linden_genepool")) { llwarns << "Invalid wearable import file (missing linden_genepool header): " << filename << llendl; + LLNotificationsUtil::add("AIXMLImportRootTypeError", args); return; } static LLStdStringHandle const version_string = LLXmlTree::addAttributeString("version"); @@ -342,6 +347,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) if (!root->getFastAttributeString(version_string, version) || (version != "1.0")) { llwarns << "Invalid linden_genepool version: " << version << " in file: " << filename << llendl; + LLNotificationsUtil::add("AIXMLImportRootVersionError", args); return; } @@ -352,6 +358,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) if (!archetype_node) { llwarns << "No archetype in wearable import file: " << filename << llendl; + LLNotificationsUtil::add("AIXMLImportInvalidError", args); return; } @@ -360,6 +367,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) static LLStdStringHandle const value_string = LLXmlTree::addAttributeString("value"); static LLStdStringHandle const te_string = LLXmlTree::addAttributeString("te"); static LLStdStringHandle const uuid_string = LLXmlTree::addAttributeString("uuid"); + bool found = false; for(LLXmlTreeNode* child = archetype_node->getFirstChild(); child; child = archetype_node->getNextChild()) { if (child->hasName("param")) @@ -377,6 +385,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLVisualParam* visual_param = edit_wearable->getVisualParam(id); if (visual_param) { + found = true; visual_param->setWeight(value, FALSE); } } @@ -396,13 +405,22 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLWearableType::EType te_wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(te_index); if (te_wearable_type == edit_wearable->getType()) { + found = true; panel_edit_wearable->setNewImageID(te_index, uuid); } } } - edit_wearable->writeToAvatar(gAgentAvatarp); - gAgentAvatarp->updateVisualParams(); - panel_edit_wearable->updateScrollingPanelUI(); + if (found) + { + edit_wearable->writeToAvatar(gAgentAvatarp); + gAgentAvatarp->updateVisualParams(); + panel_edit_wearable->updateScrollingPanelUI(); + } + else + { + args["TYPE"] = panel_edit_wearable->LLPanel::getLabel(); + LLNotificationsUtil::add("AIXMLImportWearableTypeMismatch", args); + } } // reX: new function @@ -436,7 +454,7 @@ void LLFloaterCustomize::onBtnExport() return; } - std::string file_name = edit_wearable->getName() + "_" + edit_wearable->getTypeName() + "?000.xml"; + std::string file_name = edit_wearable->getName() + "_" + gHippoGridManager->getConnectedGrid()->getGridNick() + "_" + edit_wearable->getTypeName() + "?000.xml"; std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); AIFilePicker* filepicker = AIFilePicker::create(); @@ -453,15 +471,20 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, return; } + std::string filename = filepicker->getFilename(); + LLSD args(LLSD::emptyMap()); + args["FILE"] = filename; + LLAPRFile outfile; - outfile.open(filepicker->getFilename(), LL_APR_WB); + outfile.open(filename, LL_APR_WB); if (!outfile.getFileHandle()) { - llwarns << "Could not open \"" << filepicker->getFilename() << "\" for writing." << llendl; + llwarns << "Could not open \"" << filename << "\" for writing." << llendl; + LLNotificationsUtil::add("AIXMLExportWriteError", args); return; } - LLVOAvatar::dumpArchetypeXML_header(outfile); + LLVOAvatar::dumpArchetypeXML_header(outfile, edit_wearable->getTypeName()); edit_wearable->archetypeExport(outfile); LLVOAvatar::dumpArchetypeXML_footer(outfile); } diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 1099c165d..fe77aa6c3 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -111,6 +111,37 @@ void append_path(const LLUUID& id, std::string& path) path.append(temp); } +// Path should already end on '/' if it is not empty. +void append_path_short(LLUUID const& id, std::string& path) +{ + LLInventoryObject const* obj = gInventory.getObject(id); + if (!obj) return; + + LLUUID const root_id = gInventory.getRootFolderID(); + std::string const forward_slash("/"); + + std::string result; + while(1) + { + LLUUID parent_id = obj->getParentUUID(); + if (parent_id == root_id || + !(obj = gInventory.getCategory(parent_id))) + { + break; + } + std::string temp; + temp.swap(result); + result = obj->getName(); + if (!temp.empty()) + { + result.append(forward_slash); + result.append(temp); + } + } + + path.append(result); +} + void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) { LLViewerInventoryCategory* cat; diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index d1020d388..f42543e52 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -62,6 +62,9 @@ void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); +// Same as append_path but omits the root prefix "/My Inventory/". +void append_path_short(const LLUUID& id, std::string& path); + void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id); void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id); diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 2ad5fc490..bdd03c0eb 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -39,6 +39,7 @@ #include "llviewercontrol.h" #include "llviewerregion.h" #include "llinventoryobserver.h" +#include "llinventoryfunctions.h" using namespace LLAvatarAppearanceDefines; @@ -133,6 +134,33 @@ LLWearable::EImportResult LLViewerWearable::importStream( std::istream& input_st return result; } +extern void dump_visual_param(LLAPRFile& file, LLVisualParam const* viewer_param, F32 value); + +void LLViewerWearable::archetypeExport(LLAPRFile& file) const +{ + apr_file_t* fp = file.getFileHandle(); + + std::string path; + append_path_short(mItemID, path); + apr_file_printf(fp, " \n", + LLXMLNode::escapeXML(path).c_str(), + LLXMLNode::escapeXML(mName).c_str(), + LLXMLNode::escapeXML(mDescription).c_str()); + + for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); ++iter) + { + LLVisualParam const* param = iter->second; + dump_visual_param(file, param, param->getWeight()); + } + for (te_map_t::const_iterator iter = mTEMap.begin(); iter != mTEMap.end(); ++iter) + { + S32 te = iter->first; + LLUUID const& image_id = iter->second->getID(); + apr_file_printf(fp, " \n", te, image_id.asString().c_str()); + } + + apr_file_printf(fp, "\n"); +} // Avatar parameter and texture definitions can change over time. // This function returns true if parameters or textures have been added or removed diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index cd5a2dfff..660594d26 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -31,6 +31,7 @@ #include "llavatarappearancedefines.h" class LLVOAvatar; +class LLAPRFile; class LLViewerWearable : public LLWearable { @@ -65,6 +66,8 @@ public: static void removeFromAvatar( LLWearableType::EType type, BOOL upload_bake ); /*virtual*/ EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp ); + + void archetypeExport(LLAPRFile& file) const; void setParamsToDefaults(); void setTexturesToDefaults(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 0164c722c..25e60e9bc 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -7638,7 +7638,7 @@ void dump_visual_param(LLAPRFile& file, LLVisualParam const* viewer_param, F32 v wtype = vparam->getWearableType(); } S32 u8_value = F32_to_U8(value,viewer_param->getMinWeight(),viewer_param->getMaxWeight()); - apr_file_printf(file.getFileHandle(), "\t\t\n", + apr_file_printf(file.getFileHandle(), " \n", viewer_param->getID(), viewer_param->getName().c_str(), value, u8_value, type_string.c_str(), LLWearableType::getTypeName(LLWearableType::EType(wtype)).c_str() // param_location_name(vparam->getParamLocation()).c_str() @@ -8300,21 +8300,42 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara dumpArchetypeXML_cont(fullpath, group_by_wearables); } +// metaversion 1.0 +// =============== +// +// Added as child of : +// +// +// +// Optionally, as child of , the following node may appear: +// +// +// +// Furthermore, metaversion 1.0 and higher allow the occurance of one or more blocks. +// If this is used then it is strongly advised to use one per wearable, so that +// the the node makes sense (it then refers to the wearable of that ). +// +// The reason for this clumsy way to link wearable to extra meta data is to stay +// compatible with the older format (no metaversion). +// //static -void LLVOAvatar::dumpArchetypeXML_header(LLAPRFile& file) +void LLVOAvatar::dumpArchetypeXML_header(LLAPRFile& file, std::string const& archetype_name) { apr_file_t* fp = file.getFileHandle(); apr_file_printf(fp, "\n"); - apr_file_printf(fp, "\n"); - apr_file_printf(fp, "\n\t\n"); + apr_file_printf(fp, "\n"); + apr_file_printf(fp, " \n", + LLXMLNode::escapeXML(gHippoGridManager->getConnectedGrid()->getGridNick()).c_str(), + LLDate::now().asString().c_str()); + apr_file_printf(fp, " \n", archetype_name.c_str()); } //static void LLVOAvatar::dumpArchetypeXML_footer(LLAPRFile& file) { apr_file_t* fp = file.getFileHandle(); - apr_file_printf(fp, "\t\n"); - apr_file_printf(fp, "\n\n"); + apr_file_printf(fp, " \n"); + apr_file_printf(fp, "\n"); } void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_by_wearables) diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 23964a77e..11b5c1be6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -987,7 +987,7 @@ private: // General //-------------------------------------------------------------------- public: - static void dumpArchetypeXML_header(LLAPRFile& file); + static void dumpArchetypeXML_header(LLAPRFile& file, std::string const& archetype_name = "???"); static void dumpArchetypeXML_footer(LLAPRFile& file); void dumpArchetypeXML(const std::string& prefix, bool group_by_wearables = false); void dumpArchetypeXML_cont(std::string const& fullpath, bool group_by_wearables); diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 0849f9b4d..6cc8735e9 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -133,6 +133,56 @@ + +Export Failure: could not open file "[FILE]" for writing. + + + +Import Warning: could not apply textures: [FILE] was exported on grid "[GRIDNAME]" while the currentgrid is "[CURGRID]". + + + +Import Failure: could not read or parse wearable import file "[FILE]". + + + +Import Failure: the file "[FILE]" is not a linden_genepool XML file. + + + +Import Failure: the file "[FILE]" contains linden_genepool XML data of the wrong version. Version 1.0 is required. + + + +Import Failure: the file "[FILE]" contains invalid data. + + + +Import Warning: the file "[FILE]" does not contain a wearable of type [TYPE]. +Please select the correct type before importing. + +