Wearable export improvements.

Add grid nick to default filename.
Add grid nick and date to exported XML.
Add inventory path, name and description of item to exported XML.
This commit is contained in:
Aleric Inglewood
2013-07-19 21:22:59 +02:00
parent 1fdb36cc40
commit 325ef60a8c
12 changed files with 189 additions and 55 deletions

View File

@@ -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<!-- wearable: %s -->\n", getTypeName().c_str());
apr_file_printf(fp, "\t\t<!-- Name : %s -->\n", XMLCommentEscape(mName).c_str());
apr_file_printf(fp, "\t\t<!-- Description: %s -->\n", XMLCommentEscape(mDescription).c_str());
apr_file_printf(fp, "\t\t<!-- date: %s -->\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<texture te=\"%i\" uuid=\"%s\"/>\n", te, image_id.asString().c_str());
}
apr_file_printf(fp, "\n");
}
BOOL LLWearable::exportFile(LLFILE* fp) const
{
llofstream ofs(fp);

View File

@@ -78,8 +78,6 @@ public:
virtual void writeToAvatar(LLAvatarAppearance* avatarp);
void archetypeExport(LLAPRFile& file) const;
enum EImportResult
{
FAILURE = 0,

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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, " <meta path=\"%s\" name=\"%s\" description=\"%s\"/>\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, " <texture te=\"%i\" uuid=\"%s\"/>\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

View File

@@ -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();

View File

@@ -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<param id=\"%d\" name=\"%s\" value=\"%.3f\" u8=\"%d\" type=\"%s\" wearable=\"%s\"/>\n",
apr_file_printf(file.getFileHandle(), " <param id=\"%d\" name=\"%s\" value=\"%.3f\" u8=\"%d\" type=\"%s\" wearable=\"%s\"/>\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 <linden_genepool>:
//
// <meta gridnick="secondlife" date="2013-07-16T16:49:00.40Z"/>
//
// Optionally, as child of <archetype>, the following node may appear:
//
// <meta path="clothing/jackets" name="Purple jacket" description="A jacket with mainly the color purple">
//
// Furthermore, metaversion 1.0 and higher allow the occurance of one or more <archetype> blocks.
// If this is used then it is strongly advised to use one <archetype> per wearable, so that
// the the <meta> node makes sense (it then refers to the wearable of that <archetype>).
//
// 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, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n");
apr_file_printf(fp, "<linden_genepool version=\"1.0\">\n");
apr_file_printf(fp, "\n\t<archetype name=\"???\">\n");
apr_file_printf(fp, "<linden_genepool version=\"1.0\" metaversion=\"1.0\">\n");
apr_file_printf(fp, " <meta gridnick=\"%s\" date=\"%s\"/>\n",
LLXMLNode::escapeXML(gHippoGridManager->getConnectedGrid()->getGridNick()).c_str(),
LLDate::now().asString().c_str());
apr_file_printf(fp, " <archetype name=\"%s\">\n", archetype_name.c_str());
}
//static
void LLVOAvatar::dumpArchetypeXML_footer(LLAPRFile& file)
{
apr_file_t* fp = file.getFileHandle();
apr_file_printf(fp, "\t</archetype>\n");
apr_file_printf(fp, "\n</linden_genepool>\n");
apr_file_printf(fp, " </archetype>\n");
apr_file_printf(fp, "</linden_genepool>\n");
}
void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_by_wearables)

View File

@@ -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);

View File

@@ -133,6 +133,56 @@
</form>
</template>
<notification
icon="alertmodal.tga"
name="AIXMLExportWriteError"
type="alertmodal">
Export Failure: could not open file &quot;[FILE]&quot; for writing.
</notification>
<notification
icon="alert.tga"
name="AIXMLImportGridMismatch"
type="alert">
Import Warning: could not apply textures: [FILE] was exported on grid &quot;[GRIDNAME]&quot; while the currentgrid is &quot;[CURGRID]&quot;.
</notification>
<notification
icon="alertmodal.tga"
name="AIXMLImportParseError"
type="alertmodal">
Import Failure: could not read or parse wearable import file &quot;[FILE]&quot;.
</notification>
<notification
icon="alertmodal.tga"
name="AIXMLImportRootTypeError"
type="alertmodal">
Import Failure: the file &quot;[FILE]&quot; is not a linden_genepool XML file.
</notification>
<notification
icon="alertmodal.tga"
name="AIXMLImportRootVersionError"
type="alertmodal">
Import Failure: the file &quot;[FILE]&quot; contains linden_genepool XML data of the wrong version. Version 1.0 is required.
</notification>
<notification
icon="alertmodal.tga"
name="AIXMLImportInvalidError"
type="alertmodal">
Import Failure: the file &quot;[FILE]&quot; contains invalid data.
</notification>
<notification
icon="alert.tga"
name="AIXMLImportWearableTypeMismatch"
type="alert">
Import Warning: the file &quot;[FILE]&quot; does not contain a wearable of type [TYPE].
Please select the correct type before importing.
</notification>
<notification
icon="alert.tga"
name="MediaAlert"