diff --git a/indra/llappearance/lldriverparam.h b/indra/llappearance/lldriverparam.h index 65dd3cdde..47d14d2a1 100644 --- a/indra/llappearance/lldriverparam.h +++ b/indra/llappearance/lldriverparam.h @@ -117,7 +117,8 @@ public: /*virtual*/ void stopAnimating(BOOL upload_bake); /*virtual*/ BOOL linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params); /*virtual*/ void resetDrivenParams(); - + /*virtual*/ char const* getTypeString(void) const { return "param_driver"; } + // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion(); /*virtual*/ const LLVector4a& getAvgDistortion(); diff --git a/indra/llappearance/llpolymorph.h b/indra/llappearance/llpolymorph.h index 03915d3da..f1ecef881 100644 --- a/indra/llappearance/llpolymorph.h +++ b/indra/llappearance/llpolymorph.h @@ -173,6 +173,7 @@ public: // LLVisualParam Virtual functions ///*virtual*/ BOOL parseData(LLXmlTreeNode* node); /*virtual*/ void apply( ESex sex ); + /*virtual*/ char const* getTypeString(void) const { return "param_morph"; } // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion(); diff --git a/indra/llappearance/llpolyskeletaldistortion.h b/indra/llappearance/llpolyskeletaldistortion.h index 774bc7dfa..a9b843af6 100644 --- a/indra/llappearance/llpolyskeletaldistortion.h +++ b/indra/llappearance/llpolyskeletaldistortion.h @@ -109,7 +109,8 @@ public: // LLVisualParam Virtual functions ///*virtual*/ BOOL parseData(LLXmlTreeNode* node); /*virtual*/ void apply( ESex sex ); - + /*virtual*/ char const* getTypeString(void) const { return "param_skeleton"; } + // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion() { return 0.1f; } /*virtual*/ const LLVector4a& getAvgDistortion() { return mDefaultVec; } diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h index b38d28d3e..64a465a59 100644 --- a/indra/llappearance/lltexlayerparams.h +++ b/indra/llappearance/lltexlayerparams.h @@ -86,6 +86,7 @@ public: /*virtual*/ void setWeight(F32 weight, BOOL upload_bake); /*virtual*/ void setAnimationTarget(F32 target_value, BOOL upload_bake); /*virtual*/ void animate(F32 delta, BOOL upload_bake); + /*virtual*/ char const* getTypeString(void) const { return "param_alpha"; } // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion() { return 1.f; } @@ -177,7 +178,7 @@ public: /*virtual*/ void setWeight(F32 weight, BOOL upload_bake); /*virtual*/ void setAnimationTarget(F32 target_value, BOOL upload_bake); /*virtual*/ void animate(F32 delta, BOOL upload_bake); - + /*virtual*/ char const* getTypeString(void) const { return "param_color"; } // LLViewerVisualParam Virtual functions /*virtual*/ F32 getTotalDistortion() { return 1.f; } diff --git a/indra/llappearance/llviewervisualparam.cpp b/indra/llappearance/llviewervisualparam.cpp index dd5331207..4a4f69c56 100644 --- a/indra/llappearance/llviewervisualparam.cpp +++ b/indra/llappearance/llviewervisualparam.cpp @@ -143,6 +143,12 @@ BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info) return TRUE; } +//virtual +std::string LLViewerVisualParam::getDumpWearableTypeName(void) const +{ + return LLWearableType::getTypeName(LLWearableType::EType(getInfo()->mWearableType)); +} + /* //============================================================================= // These virtual functions should always be overridden, diff --git a/indra/llappearance/llviewervisualparam.h b/indra/llappearance/llviewervisualparam.h index 64364c881..39d29fccb 100644 --- a/indra/llappearance/llviewervisualparam.h +++ b/indra/llappearance/llviewervisualparam.h @@ -82,6 +82,7 @@ public: // LLVisualParam Virtual functions ///*virtual*/ BOOL parseData(LLXmlTreeNode* node); + /*virtual*/ std::string getDumpWearableTypeName(void) const; // New Virtual functions virtual F32 getTotalDistortion() = 0; diff --git a/indra/llappearance/llwearable.h b/indra/llappearance/llwearable.h index b6049eb21..a944a16fd 100644 --- a/indra/llappearance/llwearable.h +++ b/indra/llappearance/llwearable.h @@ -41,6 +41,7 @@ class LLVisualParam; class LLTexGlobalColorInfo; class LLTexGlobalColor; class LLAvatarAppearance; +class AIArchetype; // Abstract class. class LLWearable diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h index a5864c15c..6cb1fcaa6 100644 --- a/indra/llcharacter/llvisualparam.h +++ b/indra/llcharacter/llvisualparam.h @@ -162,6 +162,10 @@ public: void setParamLocation(EParamLocation loc); EParamLocation getParamLocation() const { return mParamLocation; } + // Singu extensions. Used for dumping the archtype. + virtual char const* getTypeString(void) const = 0; + virtual std::string getDumpWearableTypeName(void) const = 0; + protected: F32 mCurWeight; // current weight F32 mLastWeight; // last weight diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 2b4c37be0..be045f100 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -17,6 +17,8 @@ include_directories( ) set(llcommon_SOURCE_FILES + aialert.cpp + aifile.cpp aiframetimer.cpp aithreadid.cpp imageids.cpp @@ -106,6 +108,8 @@ set(llcommon_SOURCE_FILES set(llcommon_HEADER_FILES CMakeLists.txt + aialert.h + aifile.h aiframetimer.h airecursive.h aithreadid.h diff --git a/indra/llcommon/aialert.cpp b/indra/llcommon/aialert.cpp new file mode 100644 index 000000000..f3d4f5af3 --- /dev/null +++ b/indra/llcommon/aialert.cpp @@ -0,0 +1,75 @@ +/** + * @file aialert.cpp + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 02/11/2013 + * - Initial version, written by Aleric Inglewood @ SL + * + * 05/11/2013 + * Moved everything in namespace AIAlert, except AIArgs. + */ + +#include "aialert.h" + +namespace AIAlert +{ + +Error::Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args) : mModal(type) +{ + if (prefix) mLines.push_back(Line(prefix)); + mLines.push_back(Line(xml_desc, args)); +} + +Error::Error(Prefix const& prefix, modal_nt type, + Error const& alert, + std::string const& xml_desc, AIArgs const& args) : mLines(alert.mLines), mModal(type) +{ + if (alert.mModal == modal) mModal = modal; + if (prefix) mLines.push_back(Line(prefix, !mLines.empty())); + mLines.push_back(Line(xml_desc, args)); +} + +Error::Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, + Error const& alert) : mLines(alert.mLines), mModal(type) +{ + if (alert.mModal == modal) mModal = modal; + if (!mLines.empty()) { mLines.front().set_newline(); } + mLines.push_front(Line(xml_desc)); + if (prefix) mLines.push_front(Line(prefix)); +} + +Error::Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args, + Error const& alert) : mLines(alert.mLines), mModal(type) +{ + if (alert.mModal == modal) mModal = modal; + if (!mLines.empty()) { mLines.front().set_newline(); } + mLines.push_front(Line(xml_desc, args)); + if (prefix) mLines.push_front(Line(prefix)); +} + +} // namespace AIAlert + diff --git a/indra/llcommon/aialert.h b/indra/llcommon/aialert.h new file mode 100644 index 000000000..83a0da300 --- /dev/null +++ b/indra/llcommon/aialert.h @@ -0,0 +1,293 @@ +/** + * @file aialert.h + * @brief Declaration of AIArgs and AIAlert classes. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 02/11/2013 + * Initial version, written by Aleric Inglewood @ SL + * + * 05/11/2013 + * Moved everything in namespace AIAlert, except AIArgs. + */ + +#ifndef AI_ALERT +#define AI_ALERT + +#include "llpreprocessor.h" +#include "llstring.h" +#include +#include + +//=================================================================================================================================== +// Facility to throw errors that can easily be converted to an informative pop-up floater for the user. + +// Throw arbitrary class. +#define THROW_ALERT_CLASS(Alert, ...) throw Alert(AIAlert::Prefix(), AIAlert::not_modal, __VA_ARGS__) +#define THROW_MALERT_CLASS(Alert, ...) throw Alert(AIAlert::Prefix(), AIAlert::modal, __VA_ARGS__) +#define THROW_FALERT_CLASS(Alert, ...) throw Alert(AIAlert::Prefix(__PRETTY_FUNCTION__, AIAlert::pretty_function_prefix), AIAlert::not_modal, __VA_ARGS__) +#define THROW_FMALERT_CLASS(Alert, ...) throw Alert(AIAlert::Prefix(__PRETTY_FUNCTION__, AIAlert::pretty_function_prefix), AIAlert::modal, __VA_ARGS__) + +// Shortcut to throw AIAlert::Error. +#define THROW_ALERT(...) THROW_ALERT_CLASS(AIAlert::Error, __VA_ARGS__) +#define THROW_MALERT(...) THROW_MALERT_CLASS(AIAlert::Error, __VA_ARGS__) +#define THROW_FALERT(...) THROW_FALERT_CLASS(AIAlert::Error, __VA_ARGS__) +#define THROW_FMALERT(...) THROW_FMALERT_CLASS(AIAlert::Error, __VA_ARGS__) + +// Shortcut to throw AIAlert::ErrorCode. +#define THROW_ALERTC(...) THROW_ALERT_CLASS(AIAlert::ErrorCode, __VA_ARGS__) +#define THROW_MALERTC(...) THROW_MALERT_CLASS(AIAlert::ErrorCode, __VA_ARGS__) +#define THROW_FALERTC(...) THROW_FALERT_CLASS(AIAlert::ErrorCode, __VA_ARGS__) +#define THROW_FMALERTC(...) THROW_FMALERT_CLASS(AIAlert::ErrorCode, __VA_ARGS__) + +// Shortcut to throw AIAlert::ErrorCode with errno as code. +#define THROW_ALERTE(...) do { int errn = errno; THROW_ALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__); } while(0) +#define THROW_MALERTE(...) do { int errn = errno; THROW_MALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__); } while(0) +#define THROW_FALERTE(...) do { int errn = errno; THROW_FALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__); } while(0) +#define THROW_FMALERTE(...) do { int errn = errno; THROW_FMALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__); } while(0) + +// Examples + +#ifdef EXAMPLE_CODE + + //---------------------------------------------------------- + // To show the alert box: + + catch (AIAlert::Error const& error) + { + AIAlert::add(error); // Optionally pass pretty_function_prefix as second parameter to *suppress* that output. + } + + // or, for example + + catch (AIAlert::ErrorCode const& error) + { + if (error.getCode() != EEXIST) + { + AIAlert::add(alert, AIAlert::pretty_function_prefix); + } + } + //---------------------------------------------------------- + // To throw alerts: + + THROW_ALERT("ExampleKey"); // A) Lookup "ExampleKey" in strings.xml and show translation. + THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)(...etc...)); // B) Same as A, but replace [FIRST] with first, [SECOND] with second, etc. + THROW_ALERT("ExampleKey", error); // C) As A, but followed by a colon and a newline, and then the text of 'error'. + THROW_ALERT(error, "ExampleKey"); // D) The text of 'error', followed by a colon and a newline and then as A. + THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second), error); // E) As B, but followed by a colon and a newline, and then the text of 'error'. + THROW_ALERT(error, "ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)); // F) The text of 'error', followed by a colon and a newline and then as B. + // where 'error' is a caught Error object (as above) in a rethrow. + // Prepend ALERT with M and/or F to make the alert box Modal and/or prepend the text with the current function name. + // For example, + THROW_MFALERT("ExampleKey", AIArgs("[FIRST]", first)); // Throw a Modal alert box that is prefixed with the current Function name. + // Append E after ALERT to throw an ErrorCode class that contains the current errno. + // For example, + THROW_FALERTE("ExampleKey", AIArgs("[FIRST]", first)); // Throw an alert box that is prefixed with the current Function name and pass errno to the catcher. + +#endif // EXAMPLE_CODE + +// +//=================================================================================================================================== + +// A wrapper around LLStringUtil::format_map_t to allow constructing a dictionary +// on one line by doing: +// +// AIArgs("[ARG1]", arg1)("[ARG2]", arg2)("[ARG3]", arg3)... + +class LL_COMMON_API AIArgs +{ + private: + LLStringUtil::format_map_t mArgs; // The underlying replacement map. + + public: + // Construct an empty map. + AIArgs(void) { } + // Construct a map with a single replacement. + AIArgs(char const* key, std::string const& replacement) { mArgs[key] = replacement; } + // Add another replacement. + AIArgs& operator()(char const* key, std::string const& replacement) { mArgs[key] = replacement; return *this; } + // The destructor may not throw. + ~AIArgs() throw() { } + + // Accessor. + LLStringUtil::format_map_t const& operator*() const { return mArgs; } +}; + +namespace AIAlert +{ + +enum modal_nt +{ + not_modal, + modal +}; + +enum alert_line_type_nt +{ + normal = 0, + empty_prefix = 1, + pretty_function_prefix = 2 + // These must exist of single bits (a mask). +}; + +// An Prefix currently comes only in two flavors: +// +// empty_prefix : An empty prefix. +// pretty_function_prefix : A function name prefix, this is the function from which the alert was thrown. + +class LL_COMMON_API Prefix +{ + public: + Prefix(void) : mType(empty_prefix) { } + Prefix(char const* str, alert_line_type_nt type) : mStr(str), mType(type) { } + + operator bool(void) const { return mType != empty_prefix; } + alert_line_type_nt type(void) const { return mType; } + std::string const& str(void) const { return mStr; } + + private: + std::string mStr; // Literal text. For example a C++ function name. + alert_line_type_nt mType; // The type of this prefix. +}; + +// A class that represents one line with its replacements. +// The string mXmlDesc shall be looked up in strings.xml. +// This is not done as part of this class because LLTrans::getString +// is not part of llcommon. + +class LL_COMMON_API Line +{ + private: + bool mNewline; // Prepend this line with a newline if set. + std::string mXmlDesc; // The keyword to look up in string.xml. + AIArgs mArgs; // Replacement map. + alert_line_type_nt mType; // The type of this line: normal for normal lines, other for prefixes. + + public: + Line(std::string const& xml_desc, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mType(normal) { } + Line(std::string const& xml_desc, AIArgs const& args, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mArgs(args), mType(normal) { } + Line(Prefix const& prefix, bool newline = false) : mNewline(newline), mXmlDesc("AIPrefix"), mArgs("[PREFIX]", prefix.str()), mType(prefix.type()) { } + // The destructor may not throw. + ~Line() throw() { } + + // Prepend a newline before this line. + void set_newline(void) { mNewline = true; } + + // These are to be used like: LLTrans::getString(line.getXmlDesc(), line.args()) and prepend with a \n if prepend_newline() returns true. + std::string getXmlDesc(void) const { return mXmlDesc; } + LLStringUtil::format_map_t const& args(void) const { return *mArgs; } + bool prepend_newline(void) const { return mNewline; } + + // Accessors. + bool suppressed(unsigned int suppress_mask) const { return (suppress_mask & mType) != 0; } + bool is_prefix(void) const { return mType != normal; } +}; + +// This class is used to throw an error that will cause +// an alert box to pop up for the user. +// +// An alert box only has text and an OK button. +// The alert box does not give feed back to the program; it is purely informational. + +// The class represents multiple lines, each line is to be translated and catenated, +// separated by newlines, and then written to an alert box. This is not done as part +// of this class because LLTrans::getString and LLNotification is not part of llcommon. +// Instead call LLNotificationUtil::add(Error const&). + +class LL_COMMON_API Error : public std::exception +{ + public: + typedef std::deque lines_type; + + // The destructor may not throw. + ~Error() throw() { } + + // Accessors. + lines_type const& lines(void) const { return mLines; } + bool is_modal(void) const { return mModal == modal; } + + // A string with zero or more replacements. + Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args = AIArgs()); + + // Same as above bit prepending the message with the text of another alert. + Error(Prefix const& prefix, modal_nt type, + Error const& alert, + std::string const& xml_desc, AIArgs const& args = AIArgs()); + + // Same as above but appending the message with the text of another alert. + // (no args) + Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, + Error const& alert); + // (with args) + Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args, + Error const& alert); + + private: + lines_type mLines; // The lines (or prefixes) of text to be displayed, each consisting on a keyword (to be looked up in strings.xml) and a replacement map. + modal_nt mModal; // If true, make the alert box a modal floater. +}; + +// Same as Error but allows to pass an additional error code. + +class LL_COMMON_API ErrorCode : public Error +{ + private: + int mCode; + + public: + // The destructor may not throw. + ~ErrorCode() throw() { } + + // Accessor. + int getCode(void) const { return mCode; } + + // A string with zero or more replacements. + ErrorCode(Prefix const& prefix, modal_nt type, int code, + std::string const& xml_desc, AIArgs const& args = AIArgs()) : + Error(prefix, modal, xml_desc, args) { } + + // Same as above bit prepending the message with the text of another alert. + ErrorCode(Prefix const& prefix, modal_nt type, int code, + Error const& alert, + std::string const& xml_desc, AIArgs const& args = AIArgs()) : + Error(prefix, modal, alert, xml_desc, args) { } + + // Same as above but appending the message with the text of another alert. + // (no args) + ErrorCode(Prefix const& prefix, modal_nt type, int code, + std::string const& xml_desc, + Error const& alert) : + Error(prefix, modal, xml_desc, alert) { } + // (with args) + ErrorCode(Prefix const& prefix, modal_nt type, int code, + std::string const& xml_desc, AIArgs const& args, + Error const& alert) : + Error(prefix, modal, xml_desc, args, alert) { } +}; + +} // namespace AIAlert + +#endif // AI_ALERT diff --git a/indra/llcommon/aifile.cpp b/indra/llcommon/aifile.cpp new file mode 100644 index 000000000..c963f5109 --- /dev/null +++ b/indra/llcommon/aifile.cpp @@ -0,0 +1,118 @@ +/** + * @file aifile.cpp + * @brief POSIX file operations that throw on error. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 03/11/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aifile.h" +#include "aialert.h" + +#if LL_WINDOWS +#include +#include // Windows errno +#else +#include +#endif + +AIFile::AIFile(std::string const& filename, char const* accessmode) +{ + mFp = AIFile::fopen(filename, accessmode); +} + +AIFile::~AIFile() +{ + AIFile::close(mFp); +} + +// Like THROW_MALERTE but appends "LLFile::strerr(errn) << " (" << errn << ')'" as argument to replace [ERROR]. +#define THROW_ERROR(...) \ + do { \ + int errn = errno; \ + std::ostringstream error; \ + error << LLFile::strerr(errn) << " (" << errn << ')'; \ + THROW_MALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__ ("[ERROR]", error.str())); \ + } while(0) + +//static +void AIFile::mkdir(std::string const& dirname, int perms) +{ + int rc = LLFile::mkdir(dirname, perms); + if (rc < 0 && rc != EEXIST) + { + THROW_ERROR("AIFile_mkdir_Failed_to_create_DIRNAME", AIArgs("[DIRNAME]", dirname)); + } +} + +//static +void AIFile::rmdir(std::string const& dirname) +{ + int rc = LLFile::rmdir(dirname); + if (rc < 0 && rc != ENOENT) + { + THROW_ERROR("AIFile_rmdir_Failed_to_remove_DIRNAME", AIArgs("[DIRNAME]", dirname)); + } +} + +//static +LLFILE* AIFile::fopen(std::string const& filename, const char* mode) +{ + LLFILE* fp = LLFile::fopen(filename, mode); + if (!fp) + { + THROW_ERROR("AIFile_fopen_Failed_to_open_FILENAME", AIArgs("[FILENAME]", filename)); + } + return fp; +} + +//static +void AIFile::close(LLFILE* file) +{ + if (LLFile::close(file) < 0) + { + THROW_ERROR("AIFile_close_Failed_to_close_file", AIArgs); + } +} + +//static +void AIFile::remove(std::string const& filename) +{ + if (LLFile::remove(filename) < 0) + { + THROW_ERROR("AIFile_remove_Failed_to_remove_FILENAME", AIArgs("[FILENAME]", filename)); + } +} + +//static +void AIFile::rename(std::string const& filename, std::string const& newname) +{ + if (LLFile::rename(filename, newname) < 0) + { + THROW_ERROR("AIFile_rename_Failed_to_rename_FILE_to_NEWFILE", AIArgs("[FILE]", filename)("[NEWFILE]", newname)); + } +} + diff --git a/indra/llcommon/aifile.h b/indra/llcommon/aifile.h new file mode 100644 index 000000000..1b110496a --- /dev/null +++ b/indra/llcommon/aifile.h @@ -0,0 +1,59 @@ +/** + * @file aifile.h + * @brief Declaration of AIFile. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 02/11/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIFILE_H +#define AIFILE_H + +#include "llfile.h" + +// As LLFile, but throws AIAlert instead of printing a warning. +class LL_COMMON_API AIFile +{ + private: + LLFILE* mFp; + + public: + // Scoped file (exception safe). Throws AIAlertCode with errno on failure. + AIFile(std::string const& filename, char const* accessmode); + ~AIFile(); + + operator LLFILE* () const { return mFp; } + + // All these functions take UTF8 path/filenames. + static LLFILE* fopen(std::string const& filename, char const* accessmode); + static void close(LLFILE* file); + + static void mkdir(std::string const& dirname, int perms = 0700); // Does NOT throw when dirname already exists. + static void rmdir(std::string const& dirname); // Does NOT throw when dirname does not exist. + static void remove(std::string const& filename); // Does NOT throw when filename does not exist. + static void rename(std::string const& filename, std::string const& newname); +}; + +#endif // AIFILE_H diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index e0e9d3a27..4a34ffaf9 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -49,7 +49,8 @@ static std::string empty; #if LL_WINDOWS // On Windows, use strerror_s(). -std::string strerr(int errn) +//static +std::string LLFile::strerr(int errn) { char buffer[256]; strerror_s(buffer, errn); // infers sizeof(buffer) -- love it! @@ -98,7 +99,8 @@ std::string message_from(int orig_errno, const char* buffer, size_t bufflen, << " (error " << stre_errno << ')'); } -std::string strerr(int errn) +//static +std::string LLFile::strerr(int errn) { char buffer[256]; // Select message_from() function matching the strerror_r() we have on hand. @@ -108,7 +110,8 @@ std::string strerr(int errn) #endif // ! LL_WINDOWS // On either system, shorthand call just infers global 'errno'. -std::string strerr() +//static +std::string LLFile::strerr() { return strerr(errno); } @@ -125,7 +128,7 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc if (errn != accept) { LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename - << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; + << "' (errno " << errn << "): " << LLFile::strerr(errn) << LL_ENDL; } #if 0 && LL_WINDOWS // turn on to debug file-locking problems // If the problem is "Permission denied," maybe it's because another diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 12eb04932..1f5514f3e 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -30,6 +30,9 @@ #ifndef LL_LLFILE_H #define LL_LLFILE_H +#include +#include + /** * This class provides a cross platform interface to the filesystem. * Attempts to mostly mirror the POSIX style IO functions. @@ -37,9 +40,6 @@ typedef FILE LLFILE; -#include -#include - #if LL_WINDOWS // windows version of stat function and stat data structure are called _stat typedef struct _stat llstat; @@ -82,6 +82,9 @@ public: std::ios::openmode mode); static const char * tmpdir(); + + static std::string strerr(int errn); + static std::string strerr(); }; /** diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp index 1409c55d1..46775313d 100644 --- a/indra/llcommon/llmd5.cpp +++ b/indra/llcommon/llmd5.cpp @@ -281,7 +281,12 @@ void LLMD5::raw_digest(unsigned char *s) const return; } - +//Singu extension: the inverse of LLMD5::raw_digest. +void LLMD5::clone(unsigned char const* s) +{ + memcpy(digest, s, 16); + finalized = 1; +} void LLMD5::hex_digest(char *s) const { @@ -305,12 +310,26 @@ void LLMD5::hex_digest(char *s) const return; } +//Singu extension: the inverse of LLMD5::hex_digest. +void LLMD5::clone(std::string const& hash_str) +{ + for (int i = 0; i < 16; ++i) + { + unsigned char byte = 0; + for (int j = 0; j < 2; ++j) + { + char c = hash_str[i * 2 + j]; + unsigned char nibble = (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10; + byte += nibble << ((1 - j) << 2); + } + digest[i] = byte; + } + finalized = 1; +} - - -std::ostream& operator<<(std::ostream &stream, LLMD5 context) +std::ostream& operator<<(std::ostream &stream, LLMD5 const& context) { char s[33]; /* Flawfinder: ignore */ context.hex_digest(s); @@ -318,23 +337,6 @@ std::ostream& operator<<(std::ostream &stream, LLMD5 context) return stream; } -bool operator==(const LLMD5& a, const LLMD5& b) -{ - unsigned char a_guts[16]; - unsigned char b_guts[16]; - a.raw_digest(a_guts); - b.raw_digest(b_guts); - if (memcmp(a_guts,b_guts,16)==0) - return true; - else - return false; -} - -bool operator!=(const LLMD5& a, const LLMD5& b) -{ - return !(a==b); -} - // PRIVATE METHODS: void LLMD5::init(){ diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index 8bf715f14..8909f04d4 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -32,6 +32,10 @@ #ifndef LL_LLMD5_H #define LL_LLMD5_H +#include "llpreprocessor.h" +#include +#include // memcmp + // LLMD5.CC - source code for the C++/object oriented translation and // modification of MD5. @@ -98,18 +102,27 @@ public: void update (const std::string& str); void finalize (); + bool isFinalized() const { return finalized; } + // constructors for special circumstances. All these constructors finalize // the MD5 context. LLMD5 (const unsigned char *string); // digest string, finalize LLMD5 (std::istream& stream); // digest stream, finalize LLMD5 (FILE *file); // digest file, close, finalize LLMD5 (const unsigned char *string, const unsigned int number); + + // Singu extension: set digest directly, finalize. + void clone(unsigned char const* digest); // Inverse of raw_digest. + void clone(std::string const& hash_str); // Inverse of hex_digest. // methods to acquire finalized result void raw_digest(unsigned char *array) const; // provide 16-byte array for binary data void hex_digest(char *string) const; // provide 33-byte array for ascii-hex string - friend LL_COMMON_API std::ostream& operator<< (std::ostream&, LLMD5 context); + friend LL_COMMON_API std::ostream& operator<< (std::ostream&, LLMD5 const& context); + friend LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b) { return std::memcmp(a.digest ,b.digest, 16) == 0; } + friend LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b) { return std::memcmp(a.digest ,b.digest, 16) != 0; } + friend LL_COMMON_API bool operator<(const LLMD5& a, const LLMD5& b) { return std::memcmp(a.digest ,b.digest, 16) < 0; } private: @@ -131,7 +144,4 @@ private: }; -LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b); -LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b); - #endif // LL_LLMD5_H diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index b7831e7fa..4df84028a 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -27,6 +27,9 @@ #define LLREFCOUNT_H #include +#include "llpreprocessor.h" // LL_COMMON_API +#include "stdtypes.h" // S32 +#include "llerror.h" // llassert #define LL_REF_COUNT_DEBUG 0 #if LL_REF_COUNT_DEBUG diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index 1fc9925df..1701800b1 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -37,6 +37,8 @@ #include #include +#include "llpreprocessor.h" +#include "stdtypes.h" class LLRunnable; diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 78b8f95be..a780a6a24 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,6 +27,7 @@ #include "llerror.h" // *TODO: eliminate this +#include #include #include diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 35d20d2ee..112a25e66 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -231,7 +231,9 @@ public: operator std::string() const { return mString; } bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; } std::size_t length() const { return mString.length(); } - + // The destructor may not throw. + ~LLFormatMapString() throw() { } + private: std::string mString; }; diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index e7b1f525e..ef99c13e7 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -28,6 +28,7 @@ #include #include +#include #include "stdtypes.h" #include "llpreprocessor.h" diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 41254b0bb..73dd99109 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -40,6 +40,7 @@ #include "lltrans.h" #include "llnotifications.h" +#include "aialert.h" #include "../newview/hippogridmanager.h" @@ -1479,6 +1480,32 @@ LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) return pNotif; } +LLNotificationPtr LLNotifications::add(AIAlert::Error const& error, int type, unsigned int suppress_mask) +{ + std::string alert_text; + bool suppress_newlines = false; + bool last_was_prefix = false; + for (AIAlert::Error::lines_type::const_iterator line = error.lines().begin(); line != error.lines().end(); ++line) + { + // Even if a line is suppressed, we print its leading newline if requested, but never more than one. + if (!suppress_newlines && line->prepend_newline()) + { + alert_text += '\n'; + suppress_newlines = true; + } + if (!line->suppressed(suppress_mask)) + { + if (last_was_prefix) alert_text += ' '; // The translation system strips off spaces... add them back. + alert_text += LLTrans::getString(line->getXmlDesc(), line->args()); + suppress_newlines = false; + last_was_prefix = line->is_prefix(); + } + } + LLSD substitutions = LLSD::emptyMap(); + substitutions["[PAYLOAD]"] = alert_text; + return add(LLNotification::Params((type == AIAlert::modal || error.is_modal()) ? "AIAlertModal" : "AIAlert").substitutions(substitutions)); +} + void LLNotifications::add(const LLNotificationPtr pNotif) { diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index fe9049ee2..bb769cae7 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -108,6 +108,8 @@ #include "llnotificationptr.h" #include "llnotificationcontext.h" +namespace AIAlert { class Error; } + typedef enum e_notification_priority { NOTIFICATION_PRIORITY_UNSPECIFIED, @@ -737,6 +739,7 @@ public: const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor); + LLNotificationPtr add(AIAlert::Error const& error, int type, unsigned int suppress_mask); LLNotificationPtr add(const LLNotification::Params& p); void forceResponse(const LLNotification::Params& params, S32 option); diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp index 4abc9ca69..134a5c267 100644 --- a/indra/llui/llnotificationsutil.cpp +++ b/indra/llui/llnotificationsutil.cpp @@ -30,6 +30,46 @@ #include "llsd.h" #include "llxmlnode.h" // apparently needed to call LLNotifications::instance() +namespace AIAlert +{ + +LLNotificationPtr add(Error const& error, modal_nt type, unsigned int suppress_mask) +{ + return LLNotifications::instance().add(error, type, suppress_mask); +} + +LLNotificationPtr add(std::string const& xml_desc, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, xml_desc, AIArgs()), type, 0); +} + +LLNotificationPtr add(std::string const& xml_desc, AIArgs const& args, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, xml_desc, args), type, 0); +} + +LLNotificationPtr add(Error const& error, std::string const& xml_desc, unsigned int suppress_mask, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, error, xml_desc, AIArgs()), type, suppress_mask); +} + +LLNotificationPtr add(Error const& error, std::string const& xml_desc, AIArgs const& args, unsigned int suppress_mask, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, error, xml_desc, args), type, suppress_mask); +} + +LLNotificationPtr add(std::string const& xml_desc, Error const& error, unsigned int suppress_mask, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, xml_desc, AIArgs(), error), type, suppress_mask); +} + +LLNotificationPtr add(std::string const& xml_desc, AIArgs const& args, Error const& error, unsigned int suppress_mask, modal_nt type) +{ + return LLNotifications::instance().add(Error(Prefix(), type, xml_desc, args, error), type, suppress_mask); +} + +} // namespace AIAlert + LLNotificationPtr LLNotificationsUtil::add(const std::string& name) { return LLNotifications::instance().add( diff --git a/indra/llui/llnotificationsutil.h b/indra/llui/llnotificationsutil.h index 4093324d0..fb0fb1ba4 100644 --- a/indra/llui/llnotificationsutil.h +++ b/indra/llui/llnotificationsutil.h @@ -30,11 +30,51 @@ // to avoid including the heavyweight llnotifications.h #include "llnotificationptr.h" +#include "aialert.h" #include class LLSD; +namespace AIAlert +{ + // Add an alert directly to LLNotifications. + + // Look up xml_desc in strings.xml. + LLNotificationPtr add(std::string const& xml_desc, + modal_nt type = not_modal); + // ... with replacement args. + LLNotificationPtr add(std::string const& xml_desc, AIArgs const& args, + modal_nt type = not_modal); + + // Append it to an existing alert error. + LLNotificationPtr add(Error const& error, + std::string const& xml_desc, + unsigned int suppress_mask = 0, modal_nt type = not_modal); + LLNotificationPtr add(Error const& error, + std::string const& xml_desc, AIArgs const& args, + unsigned int suppress_mask = 0, modal_nt type = not_modal); + // Prepend it to an existing alert error. + LLNotificationPtr add(std::string const& xml_desc, + Error const& error, + unsigned int suppress_mask = 0, modal_nt type = not_modal); + LLNotificationPtr add(std::string const& xml_desc, AIArgs const& args, + Error const& error, + unsigned int suppress_mask = 0, modal_nt type = not_modal); + + // Just show the caught alert error. + LLNotificationPtr add(Error const& error, + modal_nt type = not_modal, unsigned int suppress_mask = 0); + + // Short cuts for enforcing modal alerts. + inline LLNotificationPtr add_modal(std::string const& xml_desc) { return add(xml_desc, modal); } + inline LLNotificationPtr add_modal(std::string const& xml_desc, AIArgs const& args) { return add(xml_desc, args, modal); } + inline LLNotificationPtr add_modal(Error const& error, std::string const& xml_desc, unsigned int suppress_mask = 0) { return add(error, xml_desc, suppress_mask, modal); } + inline LLNotificationPtr add_modal(Error const& error, std::string const& xml_desc, AIArgs const& args, unsigned int suppress_mask = 0) { return add(error, xml_desc, args, suppress_mask, modal); } + inline LLNotificationPtr add_modal(std::string const& xml_desc, Error const& error, unsigned int suppress_mask = 0) { return add(xml_desc, error, suppress_mask, modal); } + inline LLNotificationPtr add_modal(std::string const& xml_desc, AIArgs const& args, Error const& error, unsigned int suppress_mask = 0) { return add(xml_desc, args, error, suppress_mask, modal); } +} + namespace LLNotificationsUtil { LLNotificationPtr add(const std::string& name); diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index 535cfc010..be0a510b4 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -13,6 +13,7 @@ include_directories( ) set(llxml_SOURCE_FILES + aixml.cpp llcontrol.cpp llxmlnode.cpp llxmlparser.cpp @@ -22,6 +23,7 @@ set(llxml_SOURCE_FILES set(llxml_HEADER_FILES CMakeLists.txt + aixml.h llcontrol.h llcontrolgroupreader.h llxmlnode.h diff --git a/indra/llxml/aixml.cpp b/indra/llxml/aixml.cpp new file mode 100644 index 000000000..db5bba7e3 --- /dev/null +++ b/indra/llxml/aixml.cpp @@ -0,0 +1,609 @@ +/** + * @file aixml.cpp + * @brief XML serialization support. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 30/07/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "sys.h" +#include "aixml.h" +#include "llmd5.h" +#include +#include "aifile.h" + +//============================================================================= +// Overview + +// The AIXML* classes provide an Object Oriented way to serialize objects +// to and from an XML file. +// +// The following classes are provided: +// +// AIXMLRootElement - Write an object to a file (including XML declaration at the top). +// AIXMLElement - Write an ojbect to an ostream (just one XML element). +// +// AIXMLParser - Read and deserialize an XML file written with AIXMLRootElement. +// AIXMLElementParser - Read and deserialize an XML stream written with AIXMLElement. +// +// Classes that need to be written to and from XML would typically +// supply two member functions. For example, + +#ifdef EXAMPLE_CODE // undefined + +class HelloWorld { + public: + // Write object to XML. + void toXML(std::ostream& os, int indentation) const; + // Read object from XML. + HelloWorld(AIXMLElementParser const& parser); + + private: + // Example member variables... + Attribute1 mAttribute1; + Attribute2 mAttribute2; + // etc. + Custom1 mCustom; + std::vector mContainer; + LLDate mDate; + LLMD5 mMd5; + LLUUID mUUID; +}; + +// Typical serialization member function. +void HelloWorld::toXML(std::ostream& os, int indentation) const +{ + AIXMLElement tag(os, "helloworld", indentation); + + // Zero or more attributes: + tag.attribute("attributename1", mAttribute1); // Uses operator<<(std::ostream&, Attribute1 const&) to write mAttribute1. + tag.attribute("attributename2", mAttribute2); // Uses operator<<(std::ostream&, Attribute2 const&) to write mAttribute2. + // etc. + + // Zero or more child elements: + tag.child("tagname", mChild1); + tag.child(mCustom); // Calls mCustom.toXML() to insert the object. + tag.child(mContainer.begin(), mContainer.end()); // Calls tag.child(element) for each element of the container. + // Special allowed cases: + tag.child(mDate); // Uses "date" as tag name. + tag.child(mMd5); // Uses "md5" as tag name. + tag.child(mUUID); // Uses "uuid" as tag name. +} + +// Typical deserialization member function. +HelloWorld::HelloWorld(AIXMLElementParser const& parser) +{ + // Zero or more attributes: + parser.attribute("attributename1", "foobar"); // Throws std::runtime_error is attributename1 is missing or does not have the value "foobar". + if (!parser.attribute("attributename2", mAttribute2)) // Reads value of attributename2 into mAttribute2 (throws if it could not be parsed). + { + throw std::runtime_error("..."); // Attribute was missing. + } + + // Zero or more child elements: + parser.child("tagname", mChild1); + parser.child("custom1", mCustom); + parser.insert_children("custom2", mContainer); + // Special allowed cases: + parser.child(mDate); + parser.child(mMd5); + parser.child(mUUID); +} + +// To actually write to an XML file one would do, for example: + + LLFILE* fp = fopen(...); + AIXMLRootElement tag(fp, "rootname"); + tag.attribute("version", "1.0"); + tag.child(LLDate::now()); + tag.child(mHelloWorld); + +// And to read it again, + + AIXMLParser helloworld(filename, "description of file used for error reporting", "rootname", 1); + helloworld.attribute("version", "1.0"); + helloworld.child("helloworld", mHelloWorld); + +// Of course, both would need to be in a try { } catch block. + +#endif // EXAMPLE_CODE + +// Do NOT change these - it would break old databases. +char const* const DEFAULT_LLUUID_NAME = "uuid"; +char const* const DEFAULT_MD5STR_NAME = "md5"; +char const* const DEFAULT_LLDATE_NAME = "date"; + +std::string const DEFAULT_MD5STR_ATTRIBUTE_NAME = DEFAULT_MD5STR_NAME; +std::string const DEFAULT_LLUUID_ATTRIBUTE_NAME = DEFAULT_LLUUID_NAME; +std::string const DEFAULT_LLDATE_ATTRIBUTE_NAME = DEFAULT_LLDATE_NAME; +std::string const DEFAULT_VERSION_ATTRIBUTE_NAME = "version"; + +struct xdigit { + bool isxdigit; + xdigit(void) : isxdigit(true) { } + void operator()(char c) { isxdigit = isxdigit && std::isxdigit(c); } + operator bool() const { return isxdigit; } +}; + +static bool is_valid_md5str(std::string const& str) +{ + return str.length() == MD5HEX_STR_BYTES && std::for_each(str.begin(), str.end(), xdigit()); +} + +// Conversion routine that is a lot more strict then LLStringUtil::convertToU32. +// This version does not allow leading or trailing spaces, nor does it allow a leading minus sign. +// Leading zeroes are not allowed except a 0 by itself. +bool convertToU32strict(std::string const& str, U32& value) +{ + bool first = true; + value = 0; + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + { + if (value == 0 && !first || !std::isdigit(*i)) // Reject leading zeroes and non-digits. + return false; + value = value * 10 + *i - '0'; + first = false; + } + return !first; // Reject empty string. +} + +typedef boost::tokenizer > boost_tokenizer; + +bool decode_version(std::string const& version, U32& major, U32& minor) +{ + boost_tokenizer tokens(version, boost::char_separator("", ".")); + boost_tokenizer::const_iterator itTok = tokens.begin(); + return itTok != tokens.end() && convertToU32strict(*itTok++, major) && + itTok != tokens.end() && *itTok++ == "." && + itTok != tokens.end() && convertToU32strict(*itTok, minor); +} + +bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out) +{ + static LLStdStringHandle const DEFAULT_MD5STR_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_MD5STR_ATTRIBUTE_NAME); + return node->getFastAttributeString(DEFAULT_MD5STR_ATTRIBUTE_NAME_HANDLE, md5str_out) && is_valid_md5str(md5str_out); +} + +bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out, std::string const& attribute_name) +{ + return node->getAttributeString(attribute_name, md5str_out) && is_valid_md5str(md5str_out); +} + +bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out) +{ + static LLStdStringHandle const DEFAULT_LLUUID_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_LLUUID_ATTRIBUTE_NAME); + return node->getFastAttributeUUID(DEFAULT_LLUUID_ATTRIBUTE_NAME_HANDLE, uuid_out); +} + +bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out, std::string const& attribute_name) +{ + return node->getAttributeUUID(attribute_name, uuid_out); +} + +bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out) +{ + static LLStdStringHandle const DEFAULT_LLDATE_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_LLDATE_ATTRIBUTE_NAME); + std::string date_s; + return node->getFastAttributeString(DEFAULT_LLDATE_ATTRIBUTE_NAME_HANDLE, date_s) && date_out.fromString(date_s); +} + +bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out, std::string const& attribute_name) +{ + std::string date_s; + return node->getAttributeString(attribute_name, date_s) && date_out.fromString(date_s); +} + +bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out) +{ + static LLStdStringHandle const DEFAULT_VERSION_ATTRIBUTE_NAME_HANDLE = LLXmlTree::addAttributeString(DEFAULT_VERSION_ATTRIBUTE_NAME); + major_out = minor_out = 0; + std::string version_s; + return node->getFastAttributeString(DEFAULT_VERSION_ATTRIBUTE_NAME_HANDLE, version_s) && decode_version(version_s, major_out, minor_out); +} + +bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out, std::string const& attribute_name) +{ + major_out = minor_out = 0; + std::string version_s; + return node->getAttributeString(attribute_name, version_s) && decode_version(version_s, major_out, minor_out); +} + +//----------------------------------------------------------------------------- +// AIXMLElement + +AIXMLElement::AIXMLElement(std::ostream& os, char const* name, int indentation) : + mOs(os), mName(name), mIndentation(indentation), mHasChildren(false) +{ + mOs << std::string(mIndentation, ' ') << '<' << mName; + if (!mOs.good()) + { + THROW_ALERT("AIXMLElement_Failed_to_write_DATA", AIArgs("[DATA]", "<" + mName)); + } +} + +int AIXMLElement::open_child(void) +{ + if (!mHasChildren) + { + mOs << ">\n"; + if (!mOs.good()) + { + THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", AIArgs("[DATA]", ">\\n")); + } + mHasChildren = true; + } + mIndentation += 2; + return mIndentation; +} + +void AIXMLElement::close_child(void) +{ + mIndentation -= 2; +} + +AIXMLElement::~AIXMLElement() +{ + if (mHasChildren) + { + mOs << std::string(mIndentation, ' ') << "\n"; + if (!mOs.good()) + { + THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", + AIArgs("[DATA]", "\\n" + std::string(mIndentation, ' ') + "\\n")); + } + } + else + { + mOs << " />\n"; + if (!mOs.good()) + { + THROW_ALERT("AIXMLElement_closing_child_Failed_to_write_DATA", AIArgs("[DATA]", " />\\n")); + } + } +} + +template<> +void AIXMLElement::child(LLUUID const& element) +{ + open_child(); + write_child(DEFAULT_LLUUID_NAME, element); + close_child(); +} + +template<> +void AIXMLElement::child(LLMD5 const& element) +{ + open_child(); + write_child(DEFAULT_MD5STR_NAME, element); + close_child(); +} + +template<> +void AIXMLElement::child(LLDate const& element) +{ + open_child(); + write_child(DEFAULT_LLDATE_NAME, element); + close_child(); +} + +//----------------------------------------------------------------------------- +// AIXMLStream + +AIXMLStream::AIXMLStream(LLFILE* fp, bool standalone) : mOfs(fp) +{ + char const* sp = standalone ? " standalone=\"yes\"" : ""; + int rc = fprintf(fp, "\n", sp); + if (rc < 0 || ferror(fp)) + { + // I don't think that errno is set to anything else but EBADF here, + // so there is not really any informative message to add here. + THROW_MALERT("AIXMLStream_fprintf_failed_to_write_xml_header"); + } +} + +AIXMLStream::~AIXMLStream() +{ + if (mOfs.is_open()) + { + mOfs.close(); + } +} + +//----------------------------------------------------------------------------- +// AIXMLParser + +AIXMLParser::AIXMLParser(std::string const& filename, char const* file_desc, std::string const& name, U32 major_version) : + AIXMLElementParser(mFilename, mFileDesc, major_version), + mFilename(filename), mFileDesc(file_desc) +{ + char const* error = NULL; + AIArgs args; + if (!mXmlTree.parseFile(filename, TRUE)) + { + AIFile dummy(filename, "rb"); // Check if the file can be opened at all (throws with a more descriptive error if not). + error = "AIXMLParser_Cannot_parse_FILEDESC_FILENAME"; + } + else + { + mNode = mXmlTree.getRoot(); + if (!mNode) + { + error = "AIXMLParser_No_root_node_found_in_FILEDESC_FILENAME"; + } + else if (!mNode->hasName(name)) + { + error = "AIXMLParser_Missing_header_NAME_invalid_FILEDESC_FILENAME"; + args("[NAME]", name); + } + else if (!versionFromXML(mNode, mVersionMajor, mVersionMinor)) + { + error = "AIXMLParser_Invalid_or_missing_NAME_version_attribute_in_FILEDESC_FILENAME"; + args("[NAME]", name); + } + else if (mVersionMajor != major_version) + { + error = "AIXMLParser_Incompatible_NAME_version_MAJOR_MINOR_in"; + args("[NAME]", name)("[MAJOR]", llformat("%u", mVersionMajor))("[MINOR]", llformat("%u", mVersionMinor)); + } + } + if (error) + { + THROW_MALERT(error, args("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } +} + +//----------------------------------------------------------------------------- +// AIXMLElementParser + +template<> +LLMD5 AIXMLElementParser::read_string(std::string const& value) const +{ + if (!is_valid_md5str(value)) + { + THROW_MALERT("AIXMLElementParser_read_string_Invalid_MD5_VALUE_in_FILEDESC_FILENAME", + AIArgs("[VALUE]", value)("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } + unsigned char digest[16]; + std::memset(digest, 0, sizeof(digest)); + for (int i = 0; i < 32; ++i) + { + int x = value[i]; + digest[i >> 1] += (x - (x & 0xf0) + (x >> 6) * 9) << ((~i & 1) << 2); + } + LLMD5 result; + result.clone(digest); + return result; +} + +template<> +LLDate AIXMLElementParser::read_string(std::string const& value) const +{ + LLDate result; + result.fromString(value); + return result; +} + +template +T AIXMLElementParser::read_integer(char const* type, std::string const& value) const +{ + long long int result; + sscanf(value.c_str(),"%lld", &result); + if (result < std::numeric_limits::min() || result > std::numeric_limits::max()) + { + THROW_MALERT("AIXMLElementParser_read_integer_Invalid_TYPE_VALUE_in_FILEDESC_FILENAME", + AIArgs("[TYPE]", type)("[VALUE]", value)("FILEDESC", mFileDesc)("[FILENAME]", mFilename)); + } + return result; +} + +template<> +U8 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("U8", value); +} + +template<> +S8 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("S8", value); +} + +template<> +U16 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("U16", value); +} + +template<> +S16 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("S16", value); +} + +template<> +U32 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("U32", value); +} + +template<> +S32 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_integer("S32", value); +} + +double read_float(std::string const& value) +{ + double result; + sscanf(value.c_str(),"%lf", &result); + return result; +} + +template<> +F32 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_float(value); +} + +template<> +F64 AIXMLElementParser::read_string(std::string const& value) const +{ + return read_float(value); +} + +template<> +bool AIXMLElementParser::read_string(std::string const& value) const +{ + if (value == "true") + { + return true; + } + else if (value != "false") + { + THROW_MALERT("AIXMLElementParser_read_string_Invalid_boolean_VALUE_in_FILEDESC_FILENAME", + AIArgs("[VALUE]", value)("FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } + return false; +} + +void AIXMLElementParser::attribute(char const* name, char const* required_value) const +{ + char const* error = NULL; + AIArgs args; + std::string value; + if (!mNode->getAttributeString(name, value)) + { + error = "AIXMLElementParser_attribute_Missing_NAME_attribute_in_NODENAME_of_FILEDESC_FILENAME"; + } + else if (value != required_value) + { + error = "AIXMLElementParser_attribute_Invalid_NAME_attribute_should_be_REQUIRED_in_NODENAME_of_FILEDESC_FILENAME"; + args("[REQUIRED]", required_value); + } + if (error) + { + THROW_MALERT(error, args("[NAME]", name)("[NODENAME]", node_name())("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } +} + +template<> +LLUUID AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + LLUUID result; + if (!LLUUID::parseUUID(node->getContents(), &result)) + { + THROW_MALERT("AIXMLElementParser_read_child_Invalid_uuid_in_FILEDESC_FILENAME", + AIArgs("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } + return result; +} + +template<> +LLMD5 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_string(node->getContents()); +} + +template<> +LLDate AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + LLDate result; + if (!result.fromString(node->getContents())) + { + THROW_MALERT("AIXMLElementParser_read_child_Invalid_date_DATE_in_FILEDESC_FILENAME", + AIArgs("[DATE]", node->getContents())("[FILEDESC]", mFileDesc)("[FILENAME]", mFilename)); + } + return result; +} + +template<> +S8 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("S8", node->getContents()); +} + +template<> +U8 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("U8", node->getContents()); +} + +template<> +S16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("S16", node->getContents()); +} + +template<> +U16 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("U16", node->getContents()); +} + +template<> +S32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("S32", node->getContents()); +} + +template<> +U32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_integer("U32", node->getContents()); +} + +template<> +F32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_string(node->getContents()); +} + +template<> +F64 AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_string(node->getContents()); +} + +template<> +bool AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return read_string(node->getContents()); +} + +bool AIXMLElementParser::child(LLUUID& uuid) const +{ + return child(DEFAULT_LLUUID_NAME, uuid); +} + +bool AIXMLElementParser::child(LLMD5& md5) const +{ + return child(DEFAULT_MD5STR_NAME, md5); +} + +bool AIXMLElementParser::child(LLDate& date) const +{ + return child(DEFAULT_LLDATE_NAME, date); +} + diff --git a/indra/llxml/aixml.h b/indra/llxml/aixml.h new file mode 100644 index 000000000..1aeec74e1 --- /dev/null +++ b/indra/llxml/aixml.h @@ -0,0 +1,374 @@ +/** + * @file aixml.h + * @brief XML serialization support. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 30/07/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIXML_H +#define AIXML_H + +#include "llxmltree.h" +#include "llxmlnode.h" +#include "llfile.h" +#include +#include "aialert.h" + +extern char const* const DEFAULT_LLUUID_NAME; +extern char const* const DEFAULT_MD5STR_NAME; +extern char const* const DEFAULT_LLDATE_NAME; + +class LLUUID; +class LLMD5; +class LLDate; + +bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out); +bool md5strFromXML(LLXmlTreeNode* node, std::string& md5str_out, std::string const& attribute_name); +bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out); +bool UUIDFromXML(LLXmlTreeNode* node, LLUUID& uuid_out, std::string const& attribute_name); +bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out); +bool dateFromXML(LLXmlTreeNode* node, LLDate& date_out, std::string const& attribute_name); +bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out); +bool versionFromXML(LLXmlTreeNode* node, U32& major_out, U32& minor_out, std::string const& attribute_name); + +class AIXMLElement +{ + private: + std::ostream& mOs; + std::string mName; + int mIndentation; + bool mHasChildren; + + public: + AIXMLElement(std::ostream& os, char const* name, int indentation); + ~AIXMLElement(); + + template + void attribute(char const* name, T const& attribute); + template + void child(T const& element); + template + void child(char const* name, T const& element); + template + void child(FWD_ITERATOR i1, FWD_ITERATOR const& i2); + + private: + template + void write_child(char const* name, T const& element); + + int open_child(void); + void close_child(void); +}; + +template +void AIXMLElement::attribute(char const* name, T const& attribute) +{ + std::ostringstream raw_attribute; + raw_attribute << attribute; + mOs << ' ' << name << "=\"" << LLXMLNode::escapeXML(raw_attribute.str()) << '"'; + if (!mOs.good()) + { + std::ostringstream ss; + ss << ' ' << name << "=\"" << LLXMLNode::escapeXML(raw_attribute.str()) << '"'; + THROW_FALERT("AIXMLElement_attribute_Failed_to_write_DATA", AIArgs("[DATA]", ss.str())); + } +} + +template +void AIXMLElement::child(T const& element) +{ + open_child(); + element.toXML(mOs, mIndentation); + if (!mOs.good()) // Normally toXML will already have thrown. + { + THROW_FALERT("AIXMLElement_child_Bad_ostream"); + } + close_child(); +} + +template<> +void AIXMLElement::child(LLUUID const& element); + +template<> +void AIXMLElement::child(LLMD5 const& element); + +template<> +void AIXMLElement::child(LLDate const& element); + +template +void AIXMLElement::write_child(char const* name, T const& element) +{ + mOs << std::string(mIndentation, ' ') << '<' << name << '>' << element << "\n"; + if (!mOs.good()) + { + std::ostringstream ss; + ss << std::string(mIndentation, ' ') << '<' << name << '>' << element << "\\n"; + THROW_FALERT("AIXMLElement_write_child_Failed_to_write_DATA", AIArgs("[DATA]", ss.str())); + } +} + +template +void AIXMLElement::child(char const* name, T const& element) +{ + open_child(); + write_child(name, element); + close_child(); +} + +template +void AIXMLElement::child(FWD_ITERATOR i1, FWD_ITERATOR const& i2) +{ + while (i1 != i2) + { + child(*i1++); + } +} + +// Helper class for AIXMLRootElement. +class AIXMLStream { + protected: + llofstream mOfs; + AIXMLStream(LLFILE* fp, bool standalone); + ~AIXMLStream(); +}; + +// Class to write XML files. +class AIXMLRootElement : public AIXMLStream, public AIXMLElement +{ + public: + AIXMLRootElement(LLFILE* fp, char const* name, bool standalone = true) : AIXMLStream(fp, standalone), AIXMLElement(mOfs, name, 0) { } +}; + +class AIXMLElementParser +{ + private: + U32 mVersion; + std::string const& mFilename; + std::string const& mFileDesc; + + protected: + LLXmlTreeNode* mNode; + + protected: + // Used by AIXMLParser, which initializes mNode directly. + AIXMLElementParser(std::string const& filename, std::string const& file_desc, U32 version) : mVersion(version), mFilename(filename), mFileDesc(file_desc) { } + + // Used for error reporting. + virtual std::string node_name(void) const { return "node '" + mNode->getName() + "'"; } + + // Parse the integer given as string 'value' and return it as type T (U8, S8, U16, S16, U32 or S32). + template + T read_integer(char const* type, std::string const& value) const; + + // Parse the string 'value' and return it as type T. + template + T read_string(std::string const& value) const; + + // Parse a child node and return it as type T. + template + T read_child(LLXmlTreeNode* node) const; + + public: + // Constructor for child member functions. + AIXMLElementParser(std::string const& filename, std::string const& file_desc, U32 version, LLXmlTreeNode* node) : mVersion(version), mFilename(filename), mFileDesc(file_desc), mNode(node) { } + + // Require the existence of some attribute with given value. + void attribute(char const* name, char const* required_value) const; + + // Read attribute. Returns true if attribute was found. + template + bool attribute(char const* name, T& attribute) const; + + // Read child element. Returns true if child was found. + template + bool child(char const* name, T& child) const; + // Read Linden types. Return true if the child was found. + bool child(LLUUID& uuid) const; + bool child(LLMD5& md5) const; + bool child(LLDate& date) const; + + // Append all elements with name 'name' to container. + template + void push_back_children(char const* name, CONTAINER& container) const; + + // Insert all elements with name 'name' into container. + template + void insert_children(char const* name, CONTAINER& container) const; + + // Set version of this particular element (if not set mVersion will be the version of the parent, all the way up to the xml header with a version of 1). + void setVersion(U32 version) { mVersion = version; } + + // Accessors. + std::string const& filename(void) const { return mFilename; } + std::string const& filedesc(void) const { return mFileDesc; } + U32 version(void) const { return mVersion; } +}; + +template +inline T AIXMLElementParser::read_string(std::string const& value) const +{ + // Construct from string. + return T(value); +} + +// Specializations. + +template<> +LLMD5 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +LLDate AIXMLElementParser::read_string(std::string const& value) const; + +template<> +U8 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +S8 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +U16 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +S16 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +U32 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +S32 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +F32 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +F64 AIXMLElementParser::read_string(std::string const& value) const; + +template<> +bool AIXMLElementParser::read_string(std::string const& value) const; + + +template +bool AIXMLElementParser::attribute(char const* name, T& attribute) const +{ + std::string value; + if (!mNode->getAttributeString(name, value)) + { + return false; + } + attribute = read_string(value); + return true; +} + +template +inline T AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return AIXMLElementParser(mFilename, mFileDesc, mVersion, node); +} + +// Specializations. + +template<> +inline std::string AIXMLElementParser::read_child(LLXmlTreeNode* node) const +{ + return node->getContents(); +} + +template<> +LLMD5 AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + +template<> +LLUUID AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + +template<> +LLDate AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + +template<> +S32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + +template<> +F32 AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + +template<> +bool AIXMLElementParser::read_child(LLXmlTreeNode* node) const; + + +template +bool AIXMLElementParser::child(char const* name, T& child) const +{ + LLXmlTreeNode* node = mNode->getChildByName(name); + if (!node) + { + return false; + } + child = read_child(node); + return true; +} + +template +void AIXMLElementParser::insert_children(char const* name, CONTAINER& container) const +{ + for (LLXmlTreeNode* node = mNode->getFirstChild(); node; node = mNode->getNextChild()) + { + if (!node->hasName(name)) + continue; + container.insert(read_child(node)); + } +} + +template +void AIXMLElementParser::push_back_children(char const* name, CONTAINER& container) const +{ + for (LLXmlTreeNode* node = mNode->getFirstChild(); node; node = mNode->getNextChild()) + { + if (!node->hasName(name)) + continue; + container.push_back(read_child(node)); + } +} + +// Class to read XML files. +class AIXMLParser : public AIXMLElementParser +{ + private: + std::string mFilename; + std::string mFileDesc; + + LLXmlTree mXmlTree; + U32 mVersionMajor; + U32 mVersionMinor; + + public: + AIXMLParser(std::string const& filename, char const* file_desc, std::string const& name, U32 major_version); + + U32 version_major(void) const { return mVersionMajor; } + U32 version_minor(void) const { return mVersionMinor; } + + protected: + /*virtual*/ std::string node_name(void) const { return "root node"; } +}; + +#endif // AIXML_H + diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 259fa091f..006bf3e71 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -555,6 +555,36 @@ void XMLCALL EndXMLNode(void *userData, node->setValue(value); } } + // Singu note: moved here from XMLData. + if (LLXMLNode::sStripEscapedStrings) + { + std::string value = node->getValue(); + int len = value.length(); + if (len > 1 && value[0] == '"' && value[len - 1] == '"') + { + // Special-case: Escaped string. + std::string unescaped_string; + for (S32 pos = 1; pos < len - 1; ++pos) + { + if (value[pos] == '\\' && value[pos + 1] == '\\') + { + unescaped_string += '\\'; + ++pos; + } + else if (value[pos] == '\\' && value[pos + 1] == '"') + { + unescaped_string += '"'; + ++pos; + } + else + { + unescaped_string += value[pos]; + } + } + value += unescaped_string; + node->setValue(value); + } + } } void XMLCALL XMLData(void *userData, @@ -563,6 +593,15 @@ void XMLCALL XMLData(void *userData, { LLXMLNode* current_node = (LLXMLNode *)userData; std::string value = current_node->getValue(); +#if 0 + // Apparently also Lindens who write XML parsers can't read documentation. + // "A single block of contiguous text free of markup may still result in a sequence + // of calls to this handler. In other words, if you're searching for a pattern in + // the text, it may be split across calls to this handler." + // (http://sepp.oetiker.ch/expat-1.95.6-rs.SEPP/expat-1.95.6/doc/reference.html#XML_SetCharacterDataHandler) + // + // In other words, this is not guaranteed to work at all -- Aleric. + if (LLXMLNode::sStripEscapedStrings) { if (s[0] == '\"' && s[len-1] == '\"') @@ -591,6 +630,7 @@ void XMLCALL XMLData(void *userData, return; } } +#endif value.append(std::string(s, len)); current_node->setValue(value); } @@ -928,12 +968,6 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root, return true; } -// static -void LLXMLNode::writeHeaderToFile(LLFILE *out_file) -{ - fprintf(out_file, "\n"); -} - void LLXMLNode::writeToFile(LLFILE *out_file, const std::string& indent, bool use_type_decorations) { if (isFullyDefault()) diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index 78572603d..5898375e9 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -157,11 +157,6 @@ public: static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector& paths); - - // Write standard XML file header: - // - static void writeHeaderToFile(LLFILE *out_file); - // Write XML to file with one attribute per line. // XML escapes values as they are written. void writeToFile(LLFILE *out_file, const std::string& indent = std::string(), bool use_type_decorations=true); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ab1bd0fa7..5d7091261 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -82,6 +82,7 @@ include_directories( set(viewer_SOURCE_FILES NACLantispam.cpp aihttpview.cpp + aixmllindengenepool.cpp aoremotectrl.cpp ascentfloatercontactgroups.cpp ascentkeyword.cpp @@ -599,6 +600,7 @@ set(viewer_HEADER_FILES NACLantispam.h aihttpview.h + aixmllindengenepool.h aoremotectrl.h ascentfloatercontactgroups.h ascentkeyword.h diff --git a/indra/newview/aixmllindengenepool.cpp b/indra/newview/aixmllindengenepool.cpp new file mode 100644 index 000000000..c2c9160f2 --- /dev/null +++ b/indra/newview/aixmllindengenepool.cpp @@ -0,0 +1,205 @@ +/** + * @file aixmllindengenepool.cpp + * @brief XML linden_genepool serialization support. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 01/11/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +// 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). + +#include "sys.h" +#include "aixmllindengenepool.h" +#include "hippogridmanager.h" +#include "llvisualparam.h" +#include "llviewerwearable.h" +#include "llquantize.h" + +extern void append_path_short(LLUUID const& id, std::string& path); + +void AIXMLLindenGenepool::MetaData::toXML(std::ostream& os, int indentation) const +{ + AIXMLElement tag(os, "meta", indentation); + tag.attribute("gridnick", mGridNick); + tag.attribute(DEFAULT_LLDATE_NAME, mDate); +} + +AIXMLLindenGenepool::MetaData::MetaData(AIXMLElementParser const& parser) +{ + parser.attribute("gridnick", mGridNick); + parser.attribute(DEFAULT_LLDATE_NAME, mDate); +} + +AIXMLLindenGenepool::AIXMLLindenGenepool(LLFILE* fp) : AIXMLRootElement(fp, "linden_genepool") +{ + attribute("version", "1.0"); + attribute("metaversion", "1.0"); + child(MetaData(gHippoGridManager->getConnectedGrid()->getGridNick(), LLDate::now())); +} + +void AIVisualParamIDValuePair::toXML(std::ostream& os, int indentation) const +{ + LLVisualParam const* visual_param = mVisualParam; + if (!visual_param && mWearable) + { + visual_param = mWearable->getVisualParam(mID); + } + if (visual_param) + { + AIXMLElement tag(os, "param", indentation); + tag.attribute("id", mID); + tag.attribute("name", visual_param->getName()); + tag.attribute("value", mValue); + tag.attribute("u8", (U32)F32_to_U8(mValue, visual_param->getMinWeight(), visual_param->getMaxWeight())); + tag.attribute("type", visual_param->getTypeString()); + tag.attribute("wearable", visual_param->getDumpWearableTypeName()); + } +} + +AIVisualParamIDValuePair::AIVisualParamIDValuePair(AIXMLElementParser const& parser) +{ + // Only id and value are relevant. Ignore all other attributes. + parser.attribute("id", mID); + parser.attribute("value", mValue); +} + +void AITextureIDUUIDPair::toXML(std::ostream& os, int indentation) const +{ + AIXMLElement tag(os, "texture", indentation); + tag.attribute("te", mID); + tag.attribute(DEFAULT_LLUUID_NAME, mUUID); +} + +AITextureIDUUIDPair::AITextureIDUUIDPair(AIXMLElementParser const& parser) +{ + parser.attribute("te", mID); + parser.attribute(DEFAULT_LLUUID_NAME, mUUID); +} + +void AIArchetype::MetaData::toXML(std::ostream& os, int indentation) const +{ + AIXMLElement tag(os, "meta", indentation); + tag.attribute("path", mPath); + tag.attribute("name", mName); + tag.attribute("description", mDescription); +} + +AIArchetype::MetaData::MetaData(AIXMLElementParser const& parser) +{ + char const* missing = NULL; + if (!parser.attribute("path", mPath)) + { + missing = "path"; + } + if (!parser.attribute("name", mName)) + { + missing = "name"; + } + if (!parser.attribute("description", mDescription)) + { + missing = "description"; + } + if (missing) + { + THROW_ALERT("AIArchetype_MetaData_archetype_meta_has_no_ATTRIBUTE", AIArgs("[ATTRIBUTE]", missing)); + } +} + +AIArchetype::MetaData::MetaData(LLViewerWearable const* wearable) : mName(wearable->getName()), mDescription(wearable->getDescription()) +{ + append_path_short(wearable->getItemID(), mPath); +} + +AIArchetype::AIArchetype(void) : mType(LLWearableType::WT_NONE) +{ +} + +AIArchetype::AIArchetype(LLWearableType::EType type) : mType(type) +{ +} + +AIArchetype::AIArchetype(LLViewerWearable const* wearable) : mType(wearable->getType()), mMetaData(wearable) +{ +} + +void AIArchetype::toXML(std::ostream& os, int indentation) const +{ + AIXMLElement tag(os, "archetype", indentation); + if (mType == LLWearableType::WT_NONE) + { + tag.attribute("name", "???"); + } + else + { + tag.attribute("name", LLWearableType::getTypeName(mType)); + } + if (!mMetaData.mPath.empty()) + { + tag.child(mMetaData); + } + tag.child(mParams.begin(), mParams.end()); + tag.child(mTextures.begin(), mTextures.end()); +} + +AIArchetype::AIArchetype(AIXMLElementParser const& parser) +{ + std::string name; + mType = LLWearableType::WT_NONE; + + if (!parser.attribute("name", name)) + { + llwarns << "The tag in file \"" << parser.filename() << "\" is missing the 'name' parameter." << llendl; + } + else if (name != "???") + { + mType = LLWearableType::typeNameToType(name); + } + if (parser.version() >= 1) + { + if (!parser.child("meta", mMetaData)) + { + THROW_ALERT("AIArchetype_archetype_has_no_meta"); + } + } + parser.push_back_children("param", mParams); + parser.push_back_children("texture", mTextures); +} + diff --git a/indra/newview/aixmllindengenepool.h b/indra/newview/aixmllindengenepool.h new file mode 100644 index 000000000..c3a5a65d6 --- /dev/null +++ b/indra/newview/aixmllindengenepool.h @@ -0,0 +1,150 @@ +/** + * @file aixmllindengenepool.h + * @brief XML linden_genepool serialization support. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 01/11/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIXMLLINDENGENEPOOL_H +#define AIXMLLINDENGENEPOOL_H + +#include "aixml.h" +#include "llwearabletype.h" +#include "llviewervisualparam.h" +#include + +class LLViewerWearable; + +class AIXMLLindenGenepool : public AIXMLRootElement +{ + public: + struct MetaData + { + std::string mGridNick; + LLDate mDate; + + MetaData(void) { } + MetaData(std::string const& grid_nick, LLDate const& date) : mGridNick(grid_nick), mDate(date) { } + + void toXML(std::ostream& os, int indentation) const; + MetaData(AIXMLElementParser const& parser); + }; + + AIXMLLindenGenepool(LLFILE* fp); +}; + +class AIVisualParamIDValuePair +{ + private: + // A wearable + ID define the LLVisualParam, but it also possible to specify the LLVisualParam directly. + LLVisualParam const* mVisualParam; // Specific LLVisualParam, given at construction, or ... + LLViewerWearable const* mWearable; // Underlaying wearable, if any. + + U32 mID; // The visual parameter id. + F32 mValue; // The value of the visual parameter. + + public: + AIVisualParamIDValuePair(LLVisualParam const* visual_param) : + mVisualParam(visual_param), mWearable(NULL), mID(visual_param->getID()), mValue(visual_param->getWeight()) { } + + AIVisualParamIDValuePair(LLVisualParam const* visual_param, F32 value) : + mVisualParam(visual_param), mWearable(NULL), mID(visual_param->getID()), mValue(value) { } + + AIVisualParamIDValuePair(LLViewerWearable const* wearable, U32 id, F32 value) : + mVisualParam(NULL), mWearable(wearable), mID(id), mValue(value) { } + + void toXML(std::ostream& os, int indentation) const; + AIVisualParamIDValuePair(AIXMLElementParser const& parser); + + // Accessors. + U32 getID(void) const { return mID; } + F32 getValue(void) const { return mValue; } +}; + +class AITextureIDUUIDPair +{ + private: + U32 mID; + LLUUID mUUID; + + public: + AITextureIDUUIDPair(U32 id, LLUUID const& uuid) : mID(id), mUUID(uuid) { } + + void toXML(std::ostream& os, int indentation) const; + AITextureIDUUIDPair(AIXMLElementParser const& parser); + + // Accessors. + U32 getID(void) const { return mID; } + LLUUID const& getUUID(void) const { return mUUID; } +}; + +class AIArchetype +{ + public: + struct MetaData + { + std::string mPath; // The wearable location in the inventory. + std::string mName; // The wearable name. + std::string mDescription; // The wearable description. + + MetaData(void) { } + MetaData(LLViewerWearable const* wearable); + + void toXML(std::ostream& os, int indentation) const; + MetaData(AIXMLElementParser const& parser); + }; + + typedef std::vector params_type; + typedef std::vector textures_type; + + private: + LLWearableType::EType mType; // The type of the wearable. + MetaData mMetaData; + params_type mParams; + textures_type mTextures; + + public: + // Accessors. + LLWearableType::EType getType(void) const { return mType; } + MetaData const& getMetaData(void) const { return mMetaData; } + params_type const& getParams(void) const { return mParams; } + textures_type const& getTextures(void) const { return mTextures; } + + public: + // An archtype without wearable has no (known) metadata. This is recognized because mPath will be empty. + // An archtype without type with get the attribute name="???". + AIArchetype(void); // + AIArchetype(LLWearableType::EType type); // + AIArchetype(LLViewerWearable const* wearable); // + + void add(AIVisualParamIDValuePair const& visual_param_id_value_pair) { mParams.push_back(visual_param_id_value_pair); } + void add(AITextureIDUUIDPair const& texture_id_uuid_pair) { mTextures.push_back(texture_id_uuid_pair); } + + void toXML(std::ostream& os, int indentation) const; + AIArchetype(AIXMLElementParser const& parser); +}; + +#endif // AIXMLLINDENGENEPOOL_H diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 3a9e9f397..0df0663b9 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -53,8 +53,9 @@ #include "llvoavatarself.h" #include "statemachine/aifilepicker.h" -#include "llxmltree.h" #include "hippogridmanager.h" +#include "aixmllindengenepool.h" +#include "aifile.h" using namespace LLAvatarAppearanceDefines; @@ -330,215 +331,112 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLPanelEditWearable* panel_edit_wearable = getCurrentWearablePanel(); LLViewerWearable* edit_wearable = panel_edit_wearable->getWearable(); + LLWearableType::EType panel_wearable_type = panel_edit_wearable->getType(); + std::string label = utf8str_tolower(panel_edit_wearable->getLabel()); + 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) - { - 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; - } + AIArgs args("[FILE]", gDirUtilp->getBaseFileName(filename)); - //------------------------------------------------------------------------- - // (root) - //------------------------------------------------------------------------- - 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"); - std::string version; - if (!root->getFastAttributeString(version_string, version) || (version != "1.0")) - { - llwarns << "Invalid or incompatible linden_genepool version: " << version << " in file: " << filename << llendl; - args["TAG"] = "version"; - args["VERSIONMAJOR"] = "1"; - LLNotificationsUtil::add("AIXMLImportRootVersionError", args); - return; - } - static LLStdStringHandle const metaversion_string = LLXmlTree::addAttributeString("metaversion"); - std::string metaversion; - U32 metaversion_major; - if (!root->getFastAttributeString(metaversion_string, metaversion)) - { - llwarns << "Invalid linden_genepool metaversion: " << metaversion << " in file: " << filename << llendl; - metaversion_major = 0; - } - else if (!LLStringUtil::convertToU32(metaversion, metaversion_major) || metaversion_major > 1) - { - llwarns << "Invalid or incompatible linden_genepool metaversion: " << metaversion << " in file: " << filename << llendl; - args["TAG"] = "metaversion"; - args["VERSIONMAJOR"] = "1"; - LLNotificationsUtil::add("AIXMLImportRootVersionError", args); - return; - } - - //------------------------------------------------------------------------- - // - //------------------------------------------------------------------------- - std::string gridnick; - LLDate date; - bool different_grid = false; // By default assume it was exported on the same grid as we're on now. - bool mixed_grids = false; // Set to true if two different grids (might) share UUIDs. Currently only "secondlife" and "secondlife_beta". - if (metaversion_major >= 1) - { - static LLStdStringHandle const gridnick_string = LLXmlTree::addAttributeString("gridnick"); - static LLStdStringHandle const date_string = LLXmlTree::addAttributeString("date"); - std::string date_s; - bool invalid = true; - LLXmlTreeNode* meta_node = root->getChildByName("meta"); - if (!meta_node) - { - llwarns << "No meta (1) in wearable import file: " << filename << llendl; - } - else if (!meta_node->getFastAttributeString(gridnick_string, gridnick)) - { - llwarns << "meta tag in file: " << filename << " is missing the 'gridnick' parameter." << llendl; - } - else if (!meta_node->getFastAttributeString(date_string, date_s) || !date.fromString(date_s)) - { - llwarns << "meta tag in file: " << filename << " is missing or invalid 'date' parameter." << llendl; - } - else - { - invalid = false; - std::string current_gridnick = gHippoGridManager->getConnectedGrid()->getGridNick(); - different_grid = gridnick != current_gridnick; - mixed_grids = (gridnick == "secondlife" && current_gridnick == "secondlife_beta") || - (gridnick == "secondlife_beta" && current_gridnick == "secondlife"); - } - if (invalid) - { - LLNotificationsUtil::add("AIXMLImportInvalidError", args); - return; - } - } - - static LLStdStringHandle const name_string = LLXmlTree::addAttributeString("name"); - - //------------------------------------------------------------------------- - // - //------------------------------------------------------------------------- - LLXmlTreeNode* archetype_node = root->getChildByName("archetype"); - if (!archetype_node) - { - llwarns << "No archetype in wearable import file: " << filename << llendl; - LLNotificationsUtil::add("AIXMLImportInvalidError", args); - return; - } - // Legacy that name="" exists. Using it as human (only) readable type label of contents. Don't use it for anything else because it might not be set. - std::string label = "???"; - if (metaversion_major >= 1) - { - if (!archetype_node->getFastAttributeString(name_string, label)) - { - llwarns << "archetype tag in file: " << filename << " is missing the 'name' parameter." << llendl; - } - } - - //------------------------------------------------------------------------- - // - //------------------------------------------------------------------------- - std::string path; - std::string wearable_name; - std::string wearable_description; - if (metaversion_major >= 1) - { - static LLStdStringHandle const path_string = LLXmlTree::addAttributeString("path"); - static LLStdStringHandle const description_string = LLXmlTree::addAttributeString("description"); - bool invalid = true; - LLXmlTreeNode* meta_node = archetype_node->getChildByName("meta"); - if (!meta_node) - { - llwarns << "No meta (2) in wearable import file: " << filename << llendl; - } - else if (!meta_node->getFastAttributeString(path_string, path)) - { - llwarns << "meta tag in file: " << filename << " is missing the 'path' parameter." << llendl; - } - else if (!meta_node->getFastAttributeString(name_string, wearable_name)) - { - llwarns << "meta tag in file: " << filename << " is missing the 'name' parameter." << llendl; - } - else if (!meta_node->getFastAttributeString(description_string, wearable_description)) - { - llwarns << "meta tag in file: " << filename << " is missing the 'description' parameter." << llendl; - } - else - { - invalid = false; - } - if (invalid) - { - LLNotificationsUtil::add("AIXMLImportInvalidError", args); - return; - } - } - - // Parse the XML content. - static LLStdStringHandle const id_string = LLXmlTree::addAttributeString("id"); - 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_param = false; bool found_texture = false; - for(LLXmlTreeNode* child = archetype_node->getFirstChild(); child; child = archetype_node->getNextChild()) + bool found_type = false; + + bool different_grid = false; // By default assume it was exported on the same grid as we're on now. + bool mixed_grids = false; // Set to true if two different grids (might) share UUIDs. Currently only "secondlife" and "secondlife_beta". + std::string gridnick; + std::string wearable_types; + + try { - if (child->hasName("param")) + //------------------------------------------------------------------------- + // (root) + //------------------------------------------------------------------------- + std::string metaversion; + U32 metaversion_major; + + AIXMLParser linden_genepool(filename, "wearable import file", "linden_genepool", 1); + linden_genepool.attribute("version", "1.0"); + linden_genepool.attribute("metaversion", metaversion); + + if (!LLStringUtil::convertToU32(metaversion, metaversion_major) || metaversion_major > 1) { - 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 node in " << filename << llendl; - continue; - } - LLVisualParam* visual_param = edit_wearable->getVisualParam(id); - if (visual_param) - { - found_param = true; - visual_param->setWeight(value, FALSE); - } + THROW_MALERT("AIXMLImportRootVersionError", args("[TAG]", "metaversion")("[VERSIONMAJOR]", "1")); } - else if (child->hasName("texture")) + + //------------------------------------------------------------------------- + // + //------------------------------------------------------------------------- + AIXMLLindenGenepool::MetaData meta_data; + + if (metaversion_major >= 1) { - std::string te_s; - S32 te; - std::string uuid_s; - LLUUID uuid; - if (!child->getFastAttributeString(te_string, te_s) || !LLStringUtil::convertToS32(te_s, te) || te < 0 || te >= TEX_NUM_INDICES || - !child->getFastAttributeString(uuid_string, uuid_s) || !uuid.set(uuid_s, TRUE)) + linden_genepool.child("meta", meta_data); + std::string current_gridnick = gHippoGridManager->getConnectedGrid()->getGridNick(); + gridnick = meta_data.mGridNick; + different_grid = gridnick != current_gridnick; + mixed_grids = (gridnick == "secondlife" && current_gridnick == "secondlife_beta") || + (gridnick == "secondlife_beta" && current_gridnick == "secondlife"); + } + + std::vector archetypes; + linden_genepool.setVersion(metaversion_major); + linden_genepool.push_back_children("archetype", archetypes); + + if (archetypes.empty()) + { + THROW_ALERT("AIXMLImportNoArchetypeError", AIArgs("[FILE]", filename)); + } + + for (std::vector::iterator archetype = archetypes.begin(); archetype != archetypes.end(); ++archetype) + { + LLWearableType::EType type = archetype->getType(); + if (type != LLWearableType::WT_NONE) { - llwarns << "Possible syntax error or corruption for node in " << filename << llendl; - continue; - } - ETextureIndex te_index = (ETextureIndex)te; - LLWearableType::EType te_wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(te_index); - if (te_wearable_type == edit_wearable->getType()) - { - found_texture = true; - if (!different_grid || mixed_grids) + if (!wearable_types.empty()) { - panel_edit_wearable->setNewImageID(te_index, uuid); + wearable_types += "/"; + } + wearable_types += LLWearableType::getTypeName(type); + if (panel_wearable_type == type) + { + found_type = true; + } + } + for (AIArchetype::params_type::const_iterator param = archetype->getParams().begin(); param != archetype->getParams().end(); ++param) + { + LLVisualParam* visual_param = edit_wearable->getVisualParam(param->getID()); + if (visual_param) + { + found_param = true; + visual_param->setWeight(param->getValue(), FALSE); + } + } + for (AIArchetype::textures_type::const_iterator texture = archetype->getTextures().begin(); texture != archetype->getTextures().end(); ++texture) + { + U32 te = texture->getID(); + if (te >= TEX_NUM_INDICES) + { + } + ETextureIndex te_index = (ETextureIndex)te; + LLWearableType::EType te_wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(te_index); + if (te_wearable_type == edit_wearable->getType()) + { + found_texture = true; + if (!different_grid || mixed_grids) + { + panel_edit_wearable->setNewImageID(te_index, texture->getUUID()); + } } } } } + catch (AIAlert::Error const& error) + { + AIAlert::add_modal("AIXMLImportError", AIArgs("[TYPE]", label), error); + return; + } + if (found_param || found_texture) { edit_wearable->writeToAvatar(gAgentAvatarp); @@ -546,23 +444,25 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) panel_edit_wearable->updateScrollingPanelUI(); if (found_texture && different_grid) { - args["EXPORTGRID"] = gridnick; - args["CURRENTGRID"] = gHippoGridManager->getConnectedGrid()->getGridNick(); + args("[EXPORTGRID]", gridnick); + args("[CURRENTGRID]", gHippoGridManager->getConnectedGrid()->getGridNick()); if (mixed_grids) { - LLNotificationsUtil::add("AIXMLImportMixedGrid", args); + AIAlert::add_modal("AIXMLImportMixedGrid", args); } else { - LLNotificationsUtil::add("AIXMLImportDifferentGrid", args); + AIAlert::add_modal("AIXMLImportDifferentGrid", args); } } } - else + else if (found_type) { - args["TYPE"] = panel_edit_wearable->LLPanel::getLabel(); - args["ARCHETYPENAME"] = label; - LLNotificationsUtil::add("AIXMLImportWearableTypeMismatch", args); + AIAlert::add("AIXMLImportEmptyArchetype", args("[TYPE]", label)); + } + else if (!wearable_types.empty()) + { + AIAlert::add("AIXMLImportWearableTypeMismatch", args("[TYPE]", label)("[ARCHETYPENAME]", wearable_types)); } } @@ -615,21 +515,26 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, } std::string filename = filepicker->getFilename(); - LLSD args(LLSD::emptyMap()); - args["FILE"] = filename; - LLAPRFile outfile; - outfile.open(filename, LL_APR_WB); - if (!outfile.getFileHandle()) + bool success = false; + try { - llwarns << "Could not open \"" << filename << "\" for writing." << llendl; - LLNotificationsUtil::add("AIXMLExportWriteError", args); - return; + AIFile outfile(filename, "wb"); + + AIXMLLindenGenepool linden_genepool(outfile); + linden_genepool.child(edit_wearable->getArchetype()); + + success = true; + } + catch (AIAlert::Error const& error) + { + AIAlert::add_modal("AIXMLExportWriteError", AIArgs("[FILE]", filename), error); } - LLVOAvatar::dumpArchetypeXML_header(outfile, edit_wearable->getTypeName()); - edit_wearable->archetypeExport(outfile); - LLVOAvatar::dumpArchetypeXML_footer(outfile); + if (success) + { + AIAlert::add_modal("AIXMLExportSuccess", AIArgs("[FILE]", filename)); + } } void LLFloaterCustomize::onBtnOk() diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 161ac8f91..0754d1d2e 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -297,6 +297,8 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mRegionFlags( REGION_FLAGS_DEFAULT ), mRegionProtocols( 0 ), mSimAccess( SIM_ACCESS_MIN ), + mLastSimAccess( 0 ), + mSimAccessString( "unknown" ), mBillableFactor(1.0), mMaxTasks(DEFAULT_MAX_REGION_WIDE_PRIM_COUNT), mCentralBakeVersion(0), @@ -599,9 +601,15 @@ BOOL LLViewerRegion::canManageEstate() const || gAgent.getID() == getOwner(); } -const std::string LLViewerRegion::getSimAccessString() const +std::string const& LLViewerRegion::getSimAccessString() { - return accessToString(mSimAccess); + // Singu: added a cache because this is called every frame. + if (mLastSimAccess != mSimAccess) + { + mSimAccessString = accessToString(mSimAccess); + mLastSimAccess = mSimAccess; + } + return mSimAccessString; } std::string LLViewerRegion::getLocalizedSimProductName() const @@ -2134,4 +2142,4 @@ U32 LLViewerRegion::getMaxMaterialsPerTransaction() const max_entries = mSimulatorFeatures[ "MaxMaterialsPerTransaction" ].asInteger(); } return max_entries; -} \ No newline at end of file +} diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index c2cc7fe39..f5d2d2eae 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -204,8 +204,8 @@ public: void setSimAccess(U8 sim_access) { mSimAccess = sim_access; } U8 getSimAccess() const { return mSimAccess; } - const std::string getSimAccessString() const; - + std::string const& getSimAccessString(); // Singu note: return reference to mSimAccessString. + // Homestead-related getters; there are no setters as nobody should be // setting them other than the individual message handler which is a member S32 getSimClassID() const { return mClassID; } @@ -430,6 +430,8 @@ private: U64 mRegionFlags; // includes damage flags U64 mRegionProtocols; // protocols supported by this region U8 mSimAccess; + U8 mLastSimAccess; // Singularity extension. + std::string mSimAccessString; // Singularity extension. F32 mBillableFactor; U32 mMaxTasks; // max prim count F32 mCameraDistanceSquared; // updated once per frame diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 30483c738..eca1c0615 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -40,6 +40,7 @@ #include "llviewerregion.h" #include "llinventoryobserver.h" #include "llinventoryfunctions.h" +#include "aixmllindengenepool.h" using namespace LLAvatarAppearanceDefines; @@ -134,32 +135,18 @@ 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 +AIArchetype LLViewerWearable::getArchetype(void) 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()); - + AIArchetype archetype(this); 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()); + archetype.add(AIVisualParamIDValuePair(iter->second)); } 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()); + archetype.add(AITextureIDUUIDPair(iter->first, iter->second->getID())); } - - apr_file_printf(fp, "\n"); + return archetype; } // Avatar parameter and texture definitions can change over time. diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index 660594d26..fbf4c87a0 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -29,6 +29,7 @@ #include "llwearable.h" #include "llavatarappearancedefines.h" +#include "aixmllindengenepool.h" class LLVOAvatar; class LLAPRFile; @@ -67,8 +68,9 @@ public: /*virtual*/ EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp ); - void archetypeExport(LLAPRFile& file) const; - + // Singu extension. + AIArchetype getArchetype(void) const; + void setParamsToDefaults(); void setTexturesToDefaults(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 6e766cd99..6916951c7 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -112,6 +112,8 @@ #include "llfloaterexploreanimations.h" #include "aihttptimeoutpolicy.h" +#include "aixmllindengenepool.h" +#include "aifile.h" #include "llavatarname.h" @@ -7672,29 +7674,10 @@ bool LLVOAvatar::visualParamWeightsAreDefault() void dump_visual_param(LLAPRFile& file, LLVisualParam const* viewer_param, F32 value) { - std::string type_string = "unknown"; - if (dynamic_cast(viewer_param)) - type_string = "param_alpha"; - if (dynamic_cast(viewer_param)) - type_string = "param_color"; - if (dynamic_cast(viewer_param)) - type_string = "param_driver"; - if (dynamic_cast(viewer_param)) - type_string = "param_morph"; - if (dynamic_cast(viewer_param)) - type_string = "param_skeleton"; - S32 wtype = -1; - LLViewerVisualParam const* vparam = dynamic_cast(viewer_param); - if (vparam) - { - wtype = vparam->getWearableType(); - } S32 u8_value = F32_to_U8(value,viewer_param->getMinWeight(),viewer_param->getMaxWeight()); 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() - ); + viewer_param->getID(), viewer_param->getName().c_str(), value, u8_value, viewer_param->getTypeString(), + viewer_param->getDumpWearableTypeName().c_str()); } void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix, @@ -8352,127 +8335,83 @@ 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, 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", - 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, " \n"); - apr_file_printf(fp, "\n"); -} - void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_by_wearables) { - LLAPRFile outfile; - outfile.open(fullpath, LL_APR_WB ); - apr_file_t* file = outfile.getFileHandle(); - if (!file) + try { - return; + AIFile outfile(fullpath, "wb"); + AIXMLLindenGenepool linden_genepool(outfile); + + if (group_by_wearables) + { + for (S32 type = LLWearableType::WT_SHAPE; type < LLWearableType::WT_COUNT; type++) + { + AIArchetype archetype((LLWearableType::EType)type); + + for (LLVisualParam* param = getFirstVisualParam(); param; param = getNextVisualParam()) + { + LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param; + if( (viewer_param->getWearableType() == type) && + (viewer_param->isTweakable() ) ) + { + archetype.add(AIVisualParamIDValuePair(param)); + } + } + + for (U8 te = 0; te < TEX_NUM_INDICES; te++) + { + if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)te) == type) + { + // MULTIPLE_WEARABLES: extend to multiple wearables? + LLViewerTexture* te_image = getImage((ETextureIndex)te, 0); + if( te_image ) + { + archetype.add(AITextureIDUUIDPair(te, te_image->getID())); + } + } + } + + linden_genepool.child(archetype); + } + } + else + { + // Just dump all params sequentially. + AIArchetype archetype; // Legacy: Type is set to WT_NONE and will result in . + + for (LLVisualParam* param = getFirstVisualParam(); param; param = getNextVisualParam()) + { + archetype.add(AIVisualParamIDValuePair(param)); + } + + for (U8 te = 0; te < TEX_NUM_INDICES; te++) + { + { + // MULTIPLE_WEARABLES: extend to multiple wearables? + LLViewerTexture* te_image = getImage((ETextureIndex)te, 0); + if( te_image ) + { + archetype.add(AITextureIDUUIDPair(te, te_image->getID())); + } + } + } + + linden_genepool.child(archetype); + } + +#if 0 // Wasn't used anyway. + bool ultra_verbose = false; + if (isSelf() && ultra_verbose) + { + // show the cloned params inside the wearables as well. + gAgentAvatarp->dumpWearableInfo(outfile); + } +#endif } - else + catch (AIAlert::Error const& error) { - llinfos << "xmlfile write handle obtained : " << fullpath << llendl; + AIAlert::add_modal("AIXMLdumpArchetypeXMLError", AIArgs("[FILE]", fullpath), error); } - - LLVOAvatar::dumpArchetypeXML_header(outfile); - - if (group_by_wearables) - { - for (S32 type = LLWearableType::WT_SHAPE; type < LLWearableType::WT_COUNT; type++) - { - const std::string& wearable_name = LLWearableType::getTypeName((LLWearableType::EType)type); - apr_file_printf( file, "\n\t\t\n", wearable_name.c_str() ); - - for (LLVisualParam* param = getFirstVisualParam(); param; param = getNextVisualParam()) - { - LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param; - if( (viewer_param->getWearableType() == type) && - (viewer_param->isTweakable() ) ) - { - dump_visual_param(outfile, viewer_param, viewer_param->getWeight()); - } - } - - for (U8 te = 0; te < TEX_NUM_INDICES; te++) - { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)te) == type) - { - // MULTIPLE_WEARABLES: extend to multiple wearables? - LLViewerTexture* te_image = getImage((ETextureIndex)te, 0); - if( te_image ) - { - std::string uuid_str; - te_image->getID().toString( uuid_str ); - apr_file_printf( file, "\t\t\n", te, uuid_str.c_str()); - } - } - } - } - } - else - { - // Just dump all params sequentially. - for (LLVisualParam* param = getFirstVisualParam(); param; param = getNextVisualParam()) - { - LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param; - dump_visual_param(outfile, viewer_param, viewer_param->getWeight()); - } - - for (U8 te = 0; te < TEX_NUM_INDICES; te++) - { - { - // MULTIPLE_WEARABLES: extend to multiple wearables? - LLViewerTexture* te_image = getImage((ETextureIndex)te, 0); - if( te_image ) - { - std::string uuid_str; - te_image->getID().toString( uuid_str ); - apr_file_printf( file, "\t\t\n", te, uuid_str.c_str()); - } - } - } - - } - - LLVOAvatar::dumpArchetypeXML_footer(outfile); - - bool ultra_verbose = false; - if (isSelf() && ultra_verbose) - { - // show the cloned params inside the wearables as well. - gAgentAvatarp->dumpWearableInfo(outfile); - } - // File will close when handle goes out of scope } void LLVOAvatar::setVisibilityRank(U32 rank) diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index d8587485f..6d6f2bba0 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -69,25 +69,6 @@ - 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 with the wrong [TAG]. Version "[VERSIONMAJOR].0" or less is required. - - Import Failure: the file "[FILE]" contains invalid data. - - Import was successful but note that the wearable was exported on grid [EXPORTGRID] (and the current grid is [CURRENTGRID]). Texture UUIDs have NOT been applied! - - Import was successful but note that the wearable was exported on grid [EXPORTGRID] (and the current grid is [CURRENTGRID]). Texture UUIDs have been applied but might not exist here! - - Import Warning: the file "[FILE]" does not contain a wearable of type [TYPE]. -It contains an archetype of type [ARCHETYPENAME]. Please select the correct type before importing. - Der Besitzer der Parzelle möchte Ihren Viewer anweisen die folgende [TYPE] URL zu laden:[URL]Sie können die korrespondierende Domäne oder In-Welt Objekt-Skript-Server erlauben oder ablehnen."Erlauben" und "Verweigern" gelten nur für die aktuelle Sitzung, während "Immer sperren" bzw. "Immer erlauben" dann immer gelten.