Add wearable exporting / importing in linden_genepool format.
As importing has been broken for a long time, I decided to hijack the existing (broken) buttons of the Appearance floater for the following new implementation: The currently selected wearable, if full perm and the agent is owner (of course) and creator, then that wearable can be exported to disk with AIFilePicker in the context "archetype". The used format is now XML, linden_genepool version 1.0, the same format that is used when saving from Advanced --> Character --> Character Tests --> Appearance to XML in any viewer. However, unlike that Advanced option (which normally only works when DebugAvatarAppearanceMessage is set to TRUE, ie in the official Linden Viewer; but that does a lot more, most likely unwanted things: it causes to dump files in this format to the LL_PATH_LOGS directory whenever appearance data comes by and other stuff for debugging purposes only), now ONLY the selected wearable is written to the file. In the case of multiwear with several layers, that means the selected layer, as well. When importing these XML files again, only the selected wearable/layer is overwritten (assuming it is modifiable) with the data in the file that corresponds to that wearable (if the file contains data of another wearable then nothing happens). Note that this file format can be read by blender-avastar: Using this feature to save the shape you created in SL will allow you to import that into blender. Likewise, a shape created or modified in blender can be imported into SL using this feature.
This commit is contained in:
@@ -81,6 +81,7 @@
|
||||
#include "llappearancemgr.h"
|
||||
|
||||
#include "statemachine/aifilepicker.h"
|
||||
#include "llxmltree.h"
|
||||
|
||||
using namespace LLAvatarAppearanceDefines;
|
||||
|
||||
@@ -297,7 +298,7 @@ void LLFloaterCustomize::onBtnImport()
|
||||
{
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open(FFLOAD_XML);
|
||||
filepicker->run(boost::bind(&LLFloaterCustomize::onBtnImport_continued, filepicker));
|
||||
filepicker->run(boost::bind(&LLFloaterCustomize::onBtnImport_continued, this, filepicker));
|
||||
}
|
||||
|
||||
void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker)
|
||||
@@ -308,59 +309,123 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker)
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string filename = filepicker->getFilename();
|
||||
// Find the editted wearable.
|
||||
LLPanelEditWearable* panel_edit_wearable = getCurrentWearablePanel();
|
||||
LLViewerWearable* edit_wearable = panel_edit_wearable->getWearable();
|
||||
|
||||
FILE* fp = LLFile::fopen(filename, "rb");
|
||||
std::string const filename = filepicker->getFilename();
|
||||
|
||||
//char text_buffer[2048]; /* Flawfinder: ignore */
|
||||
S32 c;
|
||||
S32 typ;
|
||||
S32 count;
|
||||
S32 param_id=0;
|
||||
F32 param_weight=0;
|
||||
S32 fields_read;
|
||||
|
||||
for( S32 i=0; i < LLWearableType::WT_COUNT; i++ )
|
||||
LLXmlTree xml;
|
||||
BOOL success = xml.parseFile(filename, FALSE);
|
||||
if (!success)
|
||||
{
|
||||
fields_read = fscanf( fp, "type %d\n", &typ);
|
||||
if( fields_read != 1 )
|
||||
{
|
||||
llwarns << "Bad asset type: early end of file" << llendl;
|
||||
return;
|
||||
}
|
||||
llwarns << "Could not read or parse wearable import file \"" << filename << "\"." << llendl;
|
||||
return;
|
||||
}
|
||||
LLXmlTreeNode* root = xml.getRoot();
|
||||
if (!root)
|
||||
{
|
||||
llwarns << "No root node found in wearable import file: " << filename << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
fields_read = fscanf( fp, "parameters %d\n", &count);
|
||||
if( fields_read != 1 )
|
||||
{
|
||||
llwarns << "Bad parameters : early end of file" << llendl;
|
||||
return;
|
||||
}
|
||||
for(c=0;c<count;c++)
|
||||
{
|
||||
fields_read = fscanf( fp, "%d %f\n", ¶m_id, ¶m_weight );
|
||||
if( fields_read != 2 )
|
||||
{
|
||||
llwarns << "Bad parameters list: early end of file" << llendl;
|
||||
return;
|
||||
}
|
||||
LLWearable* wearable = gAgentWearables.getTopWearable((LLWearableType::EType)typ);
|
||||
wearable->setVisualParamWeight(param_id, param_weight, TRUE);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// <linden_genepool version="1.0"> (root)
|
||||
//-------------------------------------------------------------------------
|
||||
if (!root->hasName("linden_genepool"))
|
||||
{
|
||||
llwarns << "Invalid wearable import file (missing linden_genepool header): " << filename << llendl;
|
||||
return;
|
||||
}
|
||||
static LLStdStringHandle const version_string = LLXmlTree::addAttributeString("version");
|
||||
std::string version;
|
||||
if (!root->getFastAttributeString(version_string, version) || (version != "1.0"))
|
||||
{
|
||||
llwarns << "Invalid linden_genepool version: " << version << " in file: " << filename << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
//-------------------------------------------------------------------------
|
||||
// <archetype name="???">
|
||||
//-------------------------------------------------------------------------
|
||||
LLXmlTreeNode* archetype_node = root->getChildByName("archetype");
|
||||
if (!archetype_node)
|
||||
{
|
||||
llwarns << "No archetype in wearable import file: " << filename << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the XML content.
|
||||
static LLStdStringHandle const id_string = LLXmlTree::addAttributeString("id");
|
||||
static LLStdStringHandle const value_string = LLXmlTree::addAttributeString("value");
|
||||
for(LLXmlTreeNode* child = archetype_node->getFirstChild(); child; child = archetype_node->getNextChild())
|
||||
{
|
||||
if (!child->hasName("param"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::string id_s;
|
||||
U32 id;
|
||||
std::string value_s;
|
||||
F32 value;
|
||||
if (!child->getFastAttributeString(id_string, id_s) || !LLStringUtil::convertToU32(id_s, id) ||
|
||||
!child->getFastAttributeString(value_string, value_s) || !LLStringUtil::convertToF32(value_s, value))
|
||||
{
|
||||
llwarns << "Possible syntax error or corruption for <param id=... value=... /> node in " << filename << llendl;
|
||||
continue;
|
||||
}
|
||||
LLVisualParam* visual_param = edit_wearable->getVisualParam(id);
|
||||
if (visual_param)
|
||||
{
|
||||
visual_param->setWeight(value, FALSE);
|
||||
}
|
||||
}
|
||||
edit_wearable->writeToAvatar(gAgentAvatarp);
|
||||
gAgentAvatarp->updateVisualParams();
|
||||
panel_edit_wearable->updateScrollingPanelUI();
|
||||
}
|
||||
|
||||
// reX: new function
|
||||
void LLFloaterCustomize::onBtnExport()
|
||||
{
|
||||
// Find the editted wearable.
|
||||
LLPanelEditWearable* panel_edit_wearable = getCurrentWearablePanel();
|
||||
LLViewerWearable* edit_wearable = panel_edit_wearable->getWearable();
|
||||
U32 edit_index = panel_edit_wearable->getIndex();
|
||||
std::string const& name = edit_wearable->getName();
|
||||
|
||||
// Determine if the currently selected wearable is modifiable.
|
||||
LLWearableType::EType edit_type = getCurrentWearableType();
|
||||
bool is_modifiable = false;
|
||||
LLViewerWearable* old_wearable = gAgentWearables.getViewerWearable(edit_type, edit_index);
|
||||
if (old_wearable)
|
||||
{
|
||||
LLViewerInventoryItem* item = gInventory.getItem(old_wearable->getItemID());
|
||||
if (item)
|
||||
{
|
||||
LLPermissions const& perm = item->getPermissions();
|
||||
// Modifiable means the user can see the sliders and type them over into a file anyway.
|
||||
is_modifiable = perm.allowModifyBy(gAgent.getID(), gAgent.getGroupID());
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_modifiable)
|
||||
{
|
||||
// We should never get here, because in that case the Export button is disabled.
|
||||
llwarns << "Cannot export current wearable \"" << name << "\" of type " << (int)edit_type << "because user lacks modify permissions." << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string file_name = edit_wearable->getName() + "_" + edit_wearable->getTypeName() + "?000.xml";
|
||||
std::string default_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
|
||||
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open("", FFSAVE_XML);
|
||||
filepicker->run(boost::bind(&LLFloaterCustomize::onBtnExport_continued, filepicker));
|
||||
filepicker->open(file_name, FFSAVE_XML, default_path, "archetype");
|
||||
filepicker->run(boost::bind(&LLFloaterCustomize::onBtnExport_continued, edit_wearable, filepicker));
|
||||
}
|
||||
|
||||
void LLFloaterCustomize::onBtnExport_continued(AIFilePicker* filepicker)
|
||||
//static
|
||||
void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, AIFilePicker* filepicker)
|
||||
{
|
||||
if (!filepicker->hasFilename())
|
||||
{
|
||||
@@ -368,62 +433,17 @@ void LLFloaterCustomize::onBtnExport_continued(AIFilePicker* filepicker)
|
||||
return;
|
||||
}
|
||||
|
||||
LLViewerInventoryItem* item;
|
||||
BOOL is_modifiable;
|
||||
|
||||
const std::string filename = filepicker->getFilename();
|
||||
|
||||
FILE* fp = LLFile::fopen(filename, "wb");
|
||||
|
||||
for( S32 i=0; i < LLWearableType::WT_COUNT; i++ )
|
||||
LLAPRFile outfile;
|
||||
outfile.open(filepicker->getFilename(), LL_APR_WB);
|
||||
if (!outfile.getFileHandle())
|
||||
{
|
||||
is_modifiable = FALSE;
|
||||
LLViewerWearable* old_wearable = gAgentWearables.getViewerWearable((LLWearableType::EType)i, 0); // TODO: MULTI-WEARABLE
|
||||
if( old_wearable )
|
||||
{
|
||||
item = gInventory.getItem(old_wearable->getItemID());
|
||||
if(item)
|
||||
{
|
||||
const LLPermissions& perm = item->getPermissions();
|
||||
is_modifiable = perm.allowModifyBy(gAgent.getID(), gAgent.getGroupID());
|
||||
}
|
||||
}
|
||||
if (is_modifiable)
|
||||
{
|
||||
old_wearable->FileExportParams(fp);
|
||||
}
|
||||
if (!is_modifiable)
|
||||
{
|
||||
fprintf( fp, "type %d\n",i);
|
||||
fprintf( fp, "parameters 0\n");
|
||||
}
|
||||
}
|
||||
llwarns << "Could not open \"" << filepicker->getFilename() << "\" for writing." << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
for( S32 i=0; i < LLWearableType::WT_COUNT; i++ )
|
||||
{
|
||||
is_modifiable = FALSE;
|
||||
LLViewerWearable* old_wearable = gAgentWearables.getViewerWearable((LLWearableType::EType)i, 0); // TODO: MULTI-WEARABLE
|
||||
if( old_wearable )
|
||||
{
|
||||
item = gInventory.getItem(old_wearable->getItemID());
|
||||
if(item)
|
||||
{
|
||||
const LLPermissions& perm = item->getPermissions();
|
||||
is_modifiable = perm.allowModifyBy(gAgent.getID(), gAgent.getGroupID());
|
||||
}
|
||||
}
|
||||
if (is_modifiable)
|
||||
{
|
||||
old_wearable->FileExportTextures(fp);
|
||||
}
|
||||
if (!is_modifiable)
|
||||
{
|
||||
fprintf( fp, "type %d\n",i);
|
||||
fprintf( fp, "textures 0\n");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
LLVOAvatar::dumpArchetypeXML_header(outfile);
|
||||
edit_wearable->archetypeExport(outfile);
|
||||
LLVOAvatar::dumpArchetypeXML_footer(outfile);
|
||||
}
|
||||
|
||||
void LLFloaterCustomize::onBtnOk()
|
||||
|
||||
Reference in New Issue
Block a user