446 lines
11 KiB
C++
446 lines
11 KiB
C++
/**
|
|
* @file llappappearanceutility.cpp
|
|
* @brief Implementation of LLAppAppearanceUtility class.
|
|
*
|
|
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2012, Linden Research, Inc.
|
|
*
|
|
* 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
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
// linden includes
|
|
#include "linden_common.h"
|
|
|
|
#include "llapr.h"
|
|
#include "llerrorcontrol.h"
|
|
#include "llsd.h"
|
|
#include "llsdserialize.h"
|
|
#include "llsdutil.h"
|
|
|
|
// appearance includes
|
|
#include "llavatarappearance.h"
|
|
#include "llwearabletype.h"
|
|
|
|
// project includes
|
|
#include "llappappearanceutility.h"
|
|
#include "llbakingprocess.h"
|
|
#include "llprocessparams.h"
|
|
|
|
const std::string NOTHING_EXTRA("");
|
|
|
|
////////////////////////////////////////////
|
|
// LLAppException
|
|
////////////////////////////////////////////
|
|
|
|
static const std::string MESSAGE_RV_UNKNOWN("Unknown error.");
|
|
static const std::string MESSAGE_RV_ARGUMENTS
|
|
("Invalid arguments: ");
|
|
static const std::string MESSAGE_RV_UNABLE_OPEN("Unable to open file: ");
|
|
static const std::string MESSAGE_RV_UNABLE_TO_PARSE("Unable to parse input LLSD.");
|
|
static const std::string MESSAGE_DUPLICATE_MODES = "Cannot specify more than one process mode.";
|
|
|
|
|
|
LLAppException::LLAppException(EResult status_code, const std::string& extra) :
|
|
mStatusCode(status_code)
|
|
{
|
|
switch(status_code)
|
|
{
|
|
case RV_UNKNOWN_ERROR:
|
|
printErrorLLSD("unknown", MESSAGE_RV_UNKNOWN);
|
|
case RV_BAD_ARGUMENTS:
|
|
printErrorLLSD("arguments", MESSAGE_RV_ARGUMENTS + extra);
|
|
break;
|
|
case RV_UNABLE_OPEN:
|
|
printErrorLLSD("file", MESSAGE_RV_UNABLE_OPEN + extra);
|
|
break;
|
|
case RV_UNABLE_TO_PARSE:
|
|
printErrorLLSD("input", MESSAGE_RV_UNABLE_TO_PARSE);
|
|
break;
|
|
default:
|
|
printErrorLLSD("arguments", "Unknown exception.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LLAppException::printErrorLLSD(const std::string& key, const std::string& message)
|
|
{
|
|
LLSD error_llsd;
|
|
error_llsd["success"] = false;
|
|
error_llsd["error"]["key"] = key;
|
|
error_llsd["error"]["message"] = message;
|
|
|
|
std::cerr << LLSDOStreamer<LLSDXMLFormatter>(error_llsd);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////
|
|
// LLAppAppearanceUtility
|
|
////////////////////////////////////////////
|
|
|
|
///////// Option Parsing /////////
|
|
|
|
// Simple usage command.
|
|
class LLProcessUsage : public LLBakingProcess
|
|
{
|
|
public:
|
|
LLProcessUsage(LLAppAppearanceUtility* app) :
|
|
LLBakingProcess(app) {}
|
|
/*virtual*/ void process(LLSD& input, std::ostream& output)
|
|
{
|
|
mApp->usage(output);
|
|
}
|
|
};
|
|
|
|
|
|
static const apr_getopt_option_t APPEARANCE_UTILITY_OPTIONS[] =
|
|
{
|
|
{"params", 'p', 0, "Generate appearance parameters for an agent."},
|
|
{"output", 'o', 1, "The output file to write to. Default is stdout"},
|
|
{"agent-id", 'a', 1, "The agent-id of the user."},
|
|
//{"grid", 'g', 1, "The grid."},
|
|
{"help", 'h', 0, "Print the help message."},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
void LLAppAppearanceUtility::usage(std::ostream& ostr)
|
|
{
|
|
ostr << "Utilities for processing agent appearance data."
|
|
<< std::endl << std::endl
|
|
<< "Usage:" << std::endl
|
|
<< "\t" << mAppName << " [options] filename" << std::endl << std::endl
|
|
<< "Will read from stdin if filename is set to '-'." << std::endl << std::endl
|
|
<< "Options:" << std::endl;
|
|
const apr_getopt_option_t* option = &APPEARANCE_UTILITY_OPTIONS[0];
|
|
while(option->name)
|
|
{
|
|
ostr << "\t--" << option->name << "\t\t"
|
|
<< option->description << std::endl;
|
|
++option;
|
|
}
|
|
ostr << std::endl << "Return Values:" << std::endl
|
|
<< "\t0\t\tSuccess." << std::endl
|
|
<< "\t1\t\tUnknown error." << std::endl
|
|
<< "\t2\t\tBad arguments." << std::endl
|
|
<< "\t3\t\tUnable to open file. Possibly wrong filename"
|
|
<< " or bad permissions." << std::endl
|
|
<< "\t4\t\tUnable to parse input LLSD." << std::endl
|
|
<< std::endl
|
|
<< "Output:" << std::endl
|
|
<< "If a non-zero status code is returned, additional error information"
|
|
<< " will be returned on stderr." << std::endl
|
|
<< "* This will be in the form of an LLSD document." << std::endl
|
|
<< "* Check ['error']['message'] to get a human readable message." << std::endl
|
|
<< "If a zero status code is returned, processed output will be written"
|
|
<< " to the file specified by --out (or stdout, if not specified)." << std::endl
|
|
<< std::endl
|
|
<< std::endl;
|
|
}
|
|
|
|
|
|
/////// LLApp Interface ////////
|
|
|
|
LLAppAppearanceUtility::LLAppAppearanceUtility(int argc, char** argv) :
|
|
LLApp(),
|
|
mArgc(argc),
|
|
mArgv(argv),
|
|
mProcess(NULL),
|
|
mInput(NULL),
|
|
mOutput(NULL),
|
|
mAppName(argv[0])
|
|
{
|
|
}
|
|
|
|
// virtual
|
|
LLAppAppearanceUtility::~LLAppAppearanceUtility()
|
|
{
|
|
}
|
|
|
|
void LLAppAppearanceUtility::verifyNoProcess()
|
|
{
|
|
if (mProcess)
|
|
{
|
|
std::cerr << "Invalid arguments. " << MESSAGE_DUPLICATE_MODES << std::endl;
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, MESSAGE_DUPLICATE_MODES);
|
|
}
|
|
}
|
|
|
|
void LLAppAppearanceUtility::parseArguments()
|
|
{
|
|
////// BEGIN OPTION PARSING //////
|
|
// Check for '-' as last option, since apr doesn't seem to like that.
|
|
if (std::string(mArgv[mArgc-1]) == "-")
|
|
{
|
|
mInputFilename.assign("-");
|
|
mArgc--;
|
|
}
|
|
|
|
apr_status_t apr_err;
|
|
const char* opt_arg = NULL;
|
|
int opt_id = 0;
|
|
apr_getopt_t* os = NULL;
|
|
if(APR_SUCCESS != apr_getopt_init(&os, gAPRPoolp, mArgc, mArgv))
|
|
{
|
|
std::cerr << "Unable to initialize apr" << std::endl;
|
|
throw LLAppException(RV_UNKNOWN_ERROR);
|
|
}
|
|
|
|
//std::string grid;
|
|
while(true)
|
|
{
|
|
apr_err = apr_getopt_long(os, APPEARANCE_UTILITY_OPTIONS, &opt_id, &opt_arg);
|
|
if(APR_STATUS_IS_EOF(apr_err)) break;
|
|
if(apr_err)
|
|
{
|
|
char buf[MAX_STRING]; /* Flawfinder: ignore */
|
|
std::cerr << "Error parsing options: "
|
|
<< apr_strerror(apr_err, buf, MAX_STRING) << std::endl;
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, buf);
|
|
}
|
|
switch (opt_id)
|
|
{
|
|
case 'h':
|
|
verifyNoProcess();
|
|
mProcess = new LLProcessUsage(this);
|
|
break;
|
|
case 'p':
|
|
verifyNoProcess();
|
|
mProcess = new LLProcessParams(this);
|
|
break;
|
|
case 'o':
|
|
mOutputFilename.assign(opt_arg);
|
|
break;
|
|
case 'a':
|
|
mAgentID.set(opt_arg);
|
|
if (mAgentID.isNull())
|
|
{
|
|
const char* INVALID_AGENT_ID="agent-id must be a valid uuid.";
|
|
std::cerr << "Invalid arguments. " << INVALID_AGENT_ID << std::endl;
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, INVALID_AGENT_ID);
|
|
}
|
|
break;
|
|
//case 'g':
|
|
// grid = opt_arg;
|
|
// break;
|
|
default:
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, "Unknown option.");
|
|
}
|
|
}
|
|
|
|
if ("-" != mInputFilename)
|
|
{
|
|
bool valid_input_filename = false;
|
|
// Try to grab the input filename.
|
|
if (os->argv && os->argv[os->ind])
|
|
{
|
|
mInputFilename.assign(os->argv[os->ind]);
|
|
if (! mInputFilename.empty() )
|
|
{
|
|
valid_input_filename = true;
|
|
}
|
|
}
|
|
if (!valid_input_filename)
|
|
{
|
|
const char* INVALID_FILENAME="Must specify input file.";
|
|
std::cerr << "Invalid arguments. " << INVALID_FILENAME << std::endl;
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, INVALID_FILENAME);
|
|
}
|
|
}
|
|
|
|
////// END OPTION PARSING //////
|
|
}
|
|
|
|
void LLAppAppearanceUtility::validateArguments()
|
|
{
|
|
///// BEGIN ARGUMENT VALIDATION /////
|
|
|
|
// Make sure we have a command specified.
|
|
if (!mProcess)
|
|
{
|
|
const char* INVALID_MODE="No process mode specified.";
|
|
std::cerr << "Invalid arguments. " << INVALID_MODE
|
|
<< std::endl;
|
|
usage(std::cerr);
|
|
throw LLAppException(RV_BAD_ARGUMENTS, INVALID_MODE);
|
|
}
|
|
|
|
///// END ARGUMENT VALIDATION /////
|
|
}
|
|
|
|
void LLAppAppearanceUtility::initializeIO()
|
|
{
|
|
///// BEGIN OPEN INPUT FILE ////
|
|
|
|
if ( "-" == mInputFilename )
|
|
{
|
|
// Read unformated data from stdin in to memory.
|
|
std::stringstream* data = new std::stringstream();
|
|
const S32 BUFFER_SIZE = BUFSIZ;
|
|
char buffer[BUFFER_SIZE];
|
|
while (true)
|
|
{
|
|
std::cin.read(buffer, BUFFER_SIZE);
|
|
// Check if anything out of the ordinary happened.
|
|
if (!std::cin)
|
|
{
|
|
// See if something 'really bad' happened, or if we just
|
|
// used up all of our buffer.
|
|
if (std::cin.bad())
|
|
{
|
|
std::cerr << "Problem reading standard input." << std::endl;
|
|
delete data;
|
|
throw (RV_UNKNOWN_ERROR);
|
|
}
|
|
else
|
|
{
|
|
// Output normally.
|
|
data->write(buffer, std::cin.gcount());
|
|
if (std::cin.eof()) break;
|
|
|
|
// Clear this problem. We have handled it.
|
|
std::cin.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data->write(buffer, std::cin.gcount());
|
|
if (std::cin.eof()) break;
|
|
}
|
|
}
|
|
mInput = data;
|
|
}
|
|
else
|
|
{
|
|
// Make sure we can open the input file.
|
|
std::ifstream* input_file = new std::ifstream();
|
|
input_file->open( mInputFilename.c_str(), std::fstream::in );
|
|
if ( input_file->fail())
|
|
{
|
|
std::cerr << "Couldn't open input file '" << mInputFilename << "'." << std::endl;
|
|
delete input_file;
|
|
throw LLAppException(RV_UNABLE_OPEN, mInputFilename);
|
|
}
|
|
mInput = input_file;
|
|
}
|
|
///// END OPEN INPUT FILE ////
|
|
|
|
///// BEGIN OPEN OUTPUT FILE ////
|
|
|
|
if ("" == mOutputFilename)
|
|
{
|
|
mOutput = &std::cout;
|
|
}
|
|
else
|
|
{
|
|
// Make sure we can open the output file.
|
|
std::fstream* output_file = new std::fstream();
|
|
output_file->open( mOutputFilename.c_str(), std::fstream::out );
|
|
if ( output_file->fail() )
|
|
{
|
|
std::cerr << "Couldn't open output file '" << mOutputFilename << "'." << std::endl;
|
|
delete output_file;
|
|
throw LLAppException(RV_UNABLE_OPEN, mOutputFilename);
|
|
}
|
|
mOutput = output_file;
|
|
}
|
|
///// END OPEN OUTPUT FILE ////
|
|
|
|
///// BEGIN INPUT PARSING ////
|
|
LLSDSerialize::fromXML( mInputData, *mInput );
|
|
if (mInputData.isUndefined())
|
|
{
|
|
throw LLAppException(RV_UNABLE_TO_PARSE);
|
|
}
|
|
///// END INPUT PARSING ////
|
|
}
|
|
|
|
class LLPassthroughTranslationBridge : public LLTranslationBridge
|
|
{
|
|
public:
|
|
virtual std::string getString(const std::string &xml_desc)
|
|
{
|
|
// Just pass back the input string.
|
|
return xml_desc;
|
|
}
|
|
};
|
|
|
|
|
|
bool LLAppAppearanceUtility::init()
|
|
{
|
|
parseArguments();
|
|
|
|
bool log_to_stderr = true;
|
|
LLError::initForApplication("", log_to_stderr);
|
|
// *TODO: Add debug mode(s). Skip this in debug mode.
|
|
LLError::setDefaultLevel(LLError::LEVEL_WARN);
|
|
|
|
validateArguments();
|
|
initializeIO();
|
|
|
|
// Initialize classes.
|
|
LLWearableType::initClass(new LLPassthroughTranslationBridge());
|
|
|
|
// *TODO: Create a texture bridge?
|
|
LLAvatarAppearance::initClass();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LLAppAppearanceUtility::cleanup()
|
|
{
|
|
LLAvatarAppearance::cleanupClass();
|
|
LLWearableType::cleanupClass();
|
|
|
|
if (mProcess)
|
|
{
|
|
delete mProcess;
|
|
mProcess = NULL;
|
|
}
|
|
if ("-" != mInputFilename && mInput)
|
|
{
|
|
static_cast<std::ifstream*>(mInput)->close();
|
|
}
|
|
if ("" != mOutputFilename && mOutput)
|
|
{
|
|
static_cast<std::ofstream*>(mOutput)->close();
|
|
delete mOutput;
|
|
mOutput = NULL;
|
|
}
|
|
delete mInput;
|
|
mInput = NULL;
|
|
return true;
|
|
}
|
|
|
|
bool LLAppAppearanceUtility::mainLoop()
|
|
{
|
|
// This isn't really a loop, for this application. We just execute the requested command.
|
|
mProcess->process(mInputData, *mOutput);
|
|
return true;
|
|
}
|
|
|