From 66a43ea537854745d9e3181f036f364c0982f8b0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 1 Nov 2013 00:47:47 +0100 Subject: [PATCH 01/14] Make global strerr() functions in llfile.cpp static member functions of LLFile. This is the cleanest way to make them available to the rest of the viewer as exported functions. This change is needed for / use by AIMultiGrid. --- indra/llcommon/llfile.cpp | 11 +++++++---- indra/llcommon/llfile.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) 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..63b9380fb 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -82,6 +82,9 @@ public: std::ios::openmode mode); static const char * tmpdir(); + + static std::string strerr(int errn); + static std::string strerr(); }; /** From 54b43f1ad7c7649ebba9e10767cf1e597c539a9e Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 02:19:57 +0100 Subject: [PATCH 02/14] Cache the lookup of the sim access level. Being part of the state line, it is called every frame, causing a translation lookup every frame. --- indra/newview/llviewerregion.cpp | 10 ++++++++-- indra/newview/llviewerregion.h | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 9402996b4..4146aa40b 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -599,9 +599,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 diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 6b0e73b49..8209a4dbe 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; } @@ -425,6 +425,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 From b424d0232bd3f8192497e9d707bd7b58895efb52 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 1 Nov 2013 01:14:30 +0100 Subject: [PATCH 03/14] Make #includes in llcommon header files more complete. These includes are needed. The current code includes them in .cpp files *before* including these include files, but that is not very clean, and not useful. Actually including what a header file needs makes it more clear what it drags in, it doesn't drag in more then before. It also allows to include these headers without having to includes needed for the included header file in a specific order, in every .cpp file. --- indra/llcommon/llfile.h | 6 +++--- indra/llcommon/llmd5.h | 4 ++++ indra/llcommon/llrefcount.h | 3 +++ indra/llcommon/llrun.h | 2 ++ indra/llcommon/llsingleton.h | 1 + indra/llcommon/lluuid.h | 1 + 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 63b9380fb..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; diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index 8bf715f14..f03aac83d 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. 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/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" From 2f53f7232bb7afffc87a1b3b5058dac05500d0fd Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 1 Nov 2013 02:04:52 +0100 Subject: [PATCH 04/14] Minor LLMD5 extension and cleanup. * Add LLMD5::clone(unsigned char const*), the inverse of LLMD5::raw_digest. * Add LLMD5::clone(std::string const&), the inverse of LLMD5::hex_digest. * Add LLMD5::isFinalized(), returns true if the object is finalized. * Turn all binary operators into inline friends in the class. * Fix operator<< to take a LLMD5 const& instead of a LLMD5&. These changes are needed for / used by AIMultiGrid. --- indra/llcommon/llmd5.cpp | 44 +++++++++++++++++++++------------------- indra/llcommon/llmd5.h | 14 +++++++++---- 2 files changed, 33 insertions(+), 25 deletions(-) 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 f03aac83d..8909f04d4 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -102,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: @@ -135,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 From b7d2683b76ddb7b436f8213f9935c13673408d67 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 1 Nov 2013 02:34:05 +0100 Subject: [PATCH 05/14] Removed LLXMLNode::writeHeaderToFile Isn't used. This header is written else where already, and will be written from now on (also) by AIXML*, which will be committed next. --- indra/llxml/llxmlnode.cpp | 6 ------ indra/llxml/llxmlnode.h | 5 ----- 2 files changed, 11 deletions(-) diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 259fa091f..db66282c4 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -928,12 +928,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); From 193010e9471685c0efd95d64d225f22dae7197e1 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 3 Nov 2013 21:18:06 +0100 Subject: [PATCH 06/14] Add THROW_[MF]ALERT[EC] (AIArgs, AIAlert, AIAlertCode, AIAlertPrefix, AIAlertLine) A system to throw errors that allow for easy error reporting to the user by showing a translated pop-up alert box with the error message. The messages use strings.xml for translation and allow the usual replacement args (ie [FILE] is replaced with a filename). The exceptions can be cascaded, by adding more (translated) text when caught and then re-throwing the result. Macros are being used to support adding a function name prefix to a message of the current function that the exception is thrown from. The syntax is: (); to show 'line' (, ); to append 'line' to a caught alert. (, ); to prepend 'line' to a caught alert. where is one of: THROW_ALERT, THROW_MALERT, THROW_FALERT, THROW_FMALERT, THROW_ALERTE, THROW_MALERTE, THROW_FALERTE, THROW_FMALERTE, where M = modal, F = Function name. and where is one of: , AIArgs where is a string literal that will be looked up in strings.xml, and is: (, )[] There are more variations of the macros to throw an arbitrary class (append _CLASS), include an int code (append C) or to store the current errno as code (append E). For example, THROW_MALERTC(code, ...), or THROW_FALERT_CLASS(Foobar, ...), where the ... is the same as for the macros above. Documentation and example usage has been added to aialert.h. --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/aialert.cpp | 67 +++++ indra/llcommon/aialert.h | 281 ++++++++++++++++++ indra/llcommon/llstring.h | 4 +- indra/llui/llnotifications.cpp | 27 ++ indra/llui/llnotifications.h | 3 + indra/llui/llnotificationsutil.cpp | 5 + indra/llui/llnotificationsutil.h | 3 + .../skins/default/xui/en-us/notifications.xml | 14 + .../skins/default/xui/en-us/strings.xml | 3 + 10 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 indra/llcommon/aialert.cpp create mode 100644 indra/llcommon/aialert.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 2b4c37be0..f2ad6229c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories( ) set(llcommon_SOURCE_FILES + aialert.cpp aiframetimer.cpp aithreadid.cpp imageids.cpp @@ -106,6 +107,7 @@ set(llcommon_SOURCE_FILES set(llcommon_HEADER_FILES CMakeLists.txt + aialert.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..44d2c3230 --- /dev/null +++ b/indra/llcommon/aialert.cpp @@ -0,0 +1,67 @@ +/** + * @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 + */ + +#include "aialert.h" + +AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args) : mModal(type) +{ + if (prefix) mLines.push_back(AIAlertLine(prefix)); + mLines.push_back(AIAlertLine(xml_desc, args)); +} + +AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, + AIAlert 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(AIAlertLine(prefix, !mLines.empty())); + mLines.push_back(AIAlertLine(xml_desc, args)); +} + +AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, + std::string const& xml_desc, + AIAlert const& alert) : mLines(alert.mLines), mModal(type) +{ + if (alert.mModal == modal) mModal = modal; + if (!mLines.empty()) { mLines.front().set_newline(); } + mLines.push_front(AIAlertLine(xml_desc)); + if (prefix) mLines.push_front(AIAlertLine(prefix)); +} + +AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, + std::string const& xml_desc, AIArgs const& args, + AIAlert const& alert) : mLines(alert.mLines), mModal(type) +{ + if (alert.mModal == modal) mModal = modal; + if (!mLines.empty()) { mLines.front().set_newline(); } + mLines.push_front(AIAlertLine(xml_desc, args)); + if (prefix) mLines.push_front(AIAlertLine(prefix)); +} + diff --git a/indra/llcommon/aialert.h b/indra/llcommon/aialert.h new file mode 100644 index 000000000..655a4890d --- /dev/null +++ b/indra/llcommon/aialert.h @@ -0,0 +1,281 @@ +/** + * @file aialert.h + * @brief Declaration of AIArgs, AIAlertPrefix, AIAlertLine, AIAlert and AIAlertCode + * + * 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 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(AIAlertPrefix(), AIAlert::not_modal, __VA_ARGS__) +#define THROW_MALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(), AIAlert::modal, __VA_ARGS__) +#define THROW_FALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::not_modal, __VA_ARGS__) +#define THROW_FMALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::modal, __VA_ARGS__) + +// Shortcut to throw AIAlert. +#define THROW_ALERT(...) THROW_ALERT_CLASS(AIAlert, __VA_ARGS__) +#define THROW_MALERT(...) THROW_MALERT_CLASS(AIAlert, __VA_ARGS__) +#define THROW_FALERT(...) THROW_FALERT_CLASS(AIAlert, __VA_ARGS__) +#define THROW_FMALERT(...) THROW_FMALERT_CLASS(AIAlert, __VA_ARGS__) + +// Shortcut to throw AIAlertCode. +#define THROW_ALERTC(...) THROW_ALERT_CLASS(AIAlertCode, __VA_ARGS__) +#define THROW_MALERTC(...) THROW_MALERT_CLASS(AIAlertCode, __VA_ARGS__) +#define THROW_FALERTC(...) THROW_FALERT_CLASS(AIAlertCode, __VA_ARGS__) +#define THROW_FMALERTC(...) THROW_FMALERT_CLASS(AIAlertCode, __VA_ARGS__) + +// Shortcut to throw AIAlertCode with errno as code. +#define THROW_ALERTE(...) do { int errn = errno; THROW_ALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) +#define THROW_MALERTE(...) do { int errn = errno; THROW_MALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) +#define THROW_FALERTE(...) do { int errn = errno; THROW_FALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) +#define THROW_FMALERTE(...) do { int errn = errno; THROW_FMALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) + +// Examples + +#ifdef EXAMPLE_CODE + + //---------------------------------------------------------- + // To show the alert box: + + catch (AIAlert const& alert) + { + LLNotificationsUtil::add(alert); // Optionally pass alert_line_pretty_function_prefix as second parameter to *suppress* that output. + } + + // or, for example + + catch (AIAlertCode const& alert) + { + if (alert.getCode() != EEXIST) + { + LLNotificationsUtil::add(alert, alert_line_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", alert); // C) As A, but followed by a colon and a newline, and then the text of 'alert'. + THROW_ALERT(alert, "ExampleKey"); // D) The text of 'alert', followed by a colon and a newline and then as A. + THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second), alert); // E) As B, but followed by a colon and a newline, and then the text of 'alert'. + THROW_ALERT(alert, "ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)); // F) The text of 'alert', followed by a colon and a newline and then as B. + // where 'alert' is a caught AIAlert 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 AIAlertCode 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 + +// +//=================================================================================================================================== + +enum alert_line_type_nt +{ + alert_line_normal = 0, + alert_line_empty_prefix = 1, + alert_line_pretty_function_prefix = 2 + // These must exist of single bits (a mask). +}; + +// An AIAlertPrefix currently comes only in two flavors: +// +// alert_line_empty_prefix : An empty prefix. +// alert_line_pretty_function_prefix : A function name prefix, this is the function from which the alert was thrown. + +class LL_COMMON_API AIAlertPrefix +{ + public: + AIAlertPrefix(void) : mType(alert_line_empty_prefix) { } + AIAlertPrefix(char const* str, alert_line_type_nt type) : mStr(str), mType(type) { } + + operator bool(void) const { return mType != alert_line_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 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; } +}; + +// 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 AIAlertLine +{ + 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: alert_line_normal for normal lines, other for prefixes. + + public: + AIAlertLine(std::string const& xml_desc, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mType(alert_line_normal) { } + AIAlertLine(std::string const& xml_desc, AIArgs const& args, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mArgs(args), mType(alert_line_normal) { } + AIAlertLine(AIAlertPrefix const& prefix, bool newline = false) : mNewline(newline), mXmlDesc("AIPrefix"), mArgs("[PREFIX]", prefix.str()), mType(prefix.type()) { } + // The destructor may not throw. + ~AIAlertLine() 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 != alert_line_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(AIAlert const&). + +class LL_COMMON_API AIAlert : public std::exception +{ + public: + typedef std::deque lines_type; + enum modal_nt { not_modal, modal }; + + // The destructor may not throw. + ~AIAlert() 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. + AIAlert(AIAlertPrefix const& prefix, modal_nt modal, + std::string const& xml_desc, AIArgs const& args = AIArgs()); + + // Same as above bit prepending the message with the text of another alert. + AIAlert(AIAlertPrefix const& prefix, modal_nt modal, + AIAlert 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) + AIAlert(AIAlertPrefix const& prefix, modal_nt modal, + std::string const& xml_desc, + AIAlert const& alert); + // (with args) + AIAlert(AIAlertPrefix const& prefix, modal_nt modal, + std::string const& xml_desc, AIArgs const& args, + AIAlert 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 AIAlert but allows to pass an additional error code. + +class LL_COMMON_API AIAlertCode : public AIAlert +{ + private: + int mCode; + + public: + // The destructor may not throw. + ~AIAlertCode() throw() { } + + // Accessor. + int getCode(void) const { return mCode; } + + // A string with zero or more replacements. + AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, + std::string const& xml_desc, AIArgs const& args = AIArgs()) : + AIAlert(prefix, modal, xml_desc, args) { } + + // Same as above bit prepending the message with the text of another alert. + AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, + AIAlert const& alert, + std::string const& xml_desc, AIArgs const& args = AIArgs()) : + AIAlert(prefix, modal, alert, xml_desc, args) { } + + // Same as above but appending the message with the text of another alert. + // (no args) + AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, + std::string const& xml_desc, + AIAlert const& alert) : + AIAlert(prefix, modal, xml_desc, alert) { } + // (with args) + AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, + std::string const& xml_desc, AIArgs const& args, + AIAlert const& alert) : + AIAlert(prefix, modal, xml_desc, args, alert) { } + +}; + +#endif // AI_ALERT 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/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 41254b0bb..2f60b99fa 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 const& alert, unsigned int suppress_mask) +{ + std::string alert_text; + bool suppress_newlines = false; + bool last_was_prefix = false; + for (AIAlert::lines_type::const_iterator line = alert.lines().begin(); line != alert.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(alert.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..4adfabbcb 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -108,6 +108,8 @@ #include "llnotificationptr.h" #include "llnotificationcontext.h" +class AIAlert; + 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 const& alert, 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..13e2b897d 100644 --- a/indra/llui/llnotificationsutil.cpp +++ b/indra/llui/llnotificationsutil.cpp @@ -30,6 +30,11 @@ #include "llsd.h" #include "llxmlnode.h" // apparently needed to call LLNotifications::instance() +LLNotificationPtr LLNotificationsUtil::add(AIAlert const& alert, unsigned int suppress_mask) +{ + return LLNotifications::instance().add(alert, suppress_mask); +} + 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..586b0d0bc 100644 --- a/indra/llui/llnotificationsutil.h +++ b/indra/llui/llnotificationsutil.h @@ -34,9 +34,12 @@ #include class LLSD; +class AIAlert; namespace LLNotificationsUtil { + LLNotificationPtr add(AIAlert const& alert, unsigned int suppress_mask = 0); // Singu extension. + LLNotificationPtr add(const std::string& name); LLNotificationPtr add(const std::string& name, diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 1bcef0294..919e2595a 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -133,6 +133,20 @@ + +[PAYLOAD] + + + +[PAYLOAD] + + Teleport completed from + + "[PREFIX]: " + From 78673e4a94bd25c2e2313281915be12b3ffc085f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 4 Nov 2013 01:41:33 +0100 Subject: [PATCH 07/14] Add AIFile - LLFile but throws AIAlertCode exceptions on error. The code is the current errno. --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/aifile.cpp | 118 ++++++++++++++++++ indra/llcommon/aifile.h | 59 +++++++++ .../skins/default/xui/en-us/strings.xml | 8 ++ 4 files changed, 187 insertions(+) create mode 100644 indra/llcommon/aifile.cpp create mode 100644 indra/llcommon/aifile.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index f2ad6229c..be045f100 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -18,6 +18,7 @@ include_directories( set(llcommon_SOURCE_FILES aialert.cpp + aifile.cpp aiframetimer.cpp aithreadid.cpp imageids.cpp @@ -108,6 +109,7 @@ set(llcommon_HEADER_FILES CMakeLists.txt aialert.h + aifile.h aiframetimer.h airecursive.h aithreadid.h diff --git a/indra/llcommon/aifile.cpp b/indra/llcommon/aifile.cpp new file mode 100644 index 000000000..4013f3cf7 --- /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(AIAlertCode, 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/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index b65877527..980bbcaa1 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -4367,4 +4367,12 @@ Try enclosing path to the editor with double quotes. "[PREFIX]: " + + Failed to create folder [DIRNAME]: [ERROR] + Failed to remove folder [DIRNAME]: [ERROR] + Failed to open file "[FILENAME]": [ERROR] + Failed to close file: [ERROR] + Failed to remove file [FILENAME]: [ERROR] + Failed to rename file [FILE] to [NEWFILE]: [ERROR] + From d549e549d821823aa82711b3758729df54bc5a7e Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 02:12:04 +0100 Subject: [PATCH 08/14] Fix xml parser. This fixes quoted strings, like "Hello \"World\"", which otherwise might not be processed correctly because the XML_CharacterDataHandler isn't guaranteed to be called exactly once per element. It certainly isn't when a string contains entities, ie: "'Hello \"World\"'" certainly fails to be unescaped with the old code. Moreover, the old unescape code things that a singly quote is actually a quoted string (not noting that begin and end quote are the same) and destructs all " entities; also fixed with this commit. --- indra/llxml/llxmlnode.cpp | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index db66282c4..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); } From d174b7fcf8f0a9c7597af369a4f1a55f6fe65095 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 1 Nov 2013 02:48:36 +0100 Subject: [PATCH 09/14] Add AIXMLRootElement, AIXMLElement, AIXMLParser and AIXMLElementParser The AIXML* classes provide an Object Oriented way to serialize objects to and from an XML file. Documentation is added at the top of aixml.cpp These classes will be used by AIMultiGrid. --- indra/llxml/CMakeLists.txt | 2 + indra/llxml/aixml.cpp | 609 ++++++++++++++++++ indra/llxml/aixml.h | 374 +++++++++++ .../skins/default/xui/en-us/strings.xml | 25 + 4 files changed, 1010 insertions(+) create mode 100644 indra/llxml/aixml.cpp create mode 100644 indra/llxml/aixml.h 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..9f21cf56e --- /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 HelloWord { + public: + // Write object to XML. + void toXML(std::ostream& os, int indentation) const; + // Read object from XML. + HelloWord(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 HelloWord::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. +HelloWord::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/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index 980bbcaa1..8ed04b942 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -4375,4 +4375,29 @@ Try enclosing path to the editor with double quotes. Failed to remove file [FILENAME]: [ERROR] Failed to rename file [FILE] to [NEWFILE]: [ERROR] + + Failed to write '[DATA]', writing attribute data. + ostream not good after calling child.toXML + Failed to write '[DATA]', writing child data. + Failed to write '[DATA]', opening new child. + Failed to write '[DATA]', closing previous child. + + + Failed to write XML header. + + + Cannot parse [FILEDESC] "[FILENAME]". + No root node found in [FILEDESC] "[FILENAME]". + Missing header '[NAME]' ; invalid [FILEDESC] "[FILENAME]". + Invalid or missing [NAME] 'version' attribute in [FILEDESC] "[FILENAME]". + Incompatible '[NAME]' version, [MAJOR].[MINOR], in [FILEDESC] "[FILENAME]". + + + Invalid MD5 ([VALUE]) in [FILEDESC] "[FILENAME]". + Invalid boolean ([VALUE]) in [FILEDESC] "[FILENAME]". + Missing '[NAME]' attribute in [NODENAME] of [FILEDESC] "[FILENAME]"." + Invalid '[NAME]' attribute (should be '[REQUIRED]') in [NODENAME] of [FILEDESC] "[FILENAME]". + Invalid UUID in [FILEDESC] "[FILENAME]". + Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]". + From 2453c13e117cd1af9221a0e1272995448145556d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 2 Nov 2013 22:31:27 +0100 Subject: [PATCH 10/14] Add AIXMLLindenGenepool - redo of archetype export/import using AIXML*. In order to remove code duplication, and as prove of concept, I redid the linden_genepool archetype export and import (from the Edit Appearance Floater), using AIXML and AIAlert. The import/export code did shrink significantly, and became a lot simpler. Although a new file pair (aixmllindengenepool.{cpp,h}) is added to define the (de)serialization of the linden_genepool XML format, that code is now centralized in one place, and still a lot simpler. As a bonus however, every possible error is now semi-automatically reported to the user with all details that might help to overcome the problem, like file names and system errors. --- indra/llappearance/lldriverparam.h | 3 +- indra/llappearance/llpolymorph.h | 1 + indra/llappearance/llpolyskeletaldistortion.h | 3 +- indra/llappearance/lltexlayerparams.h | 3 +- indra/llappearance/llviewervisualparam.cpp | 6 + indra/llappearance/llviewervisualparam.h | 1 + indra/llappearance/llwearable.h | 1 + indra/llcharacter/llvisualparam.h | 4 + indra/newview/CMakeLists.txt | 2 + indra/newview/aixmllindengenepool.cpp | 205 +++++++++++ indra/newview/aixmllindengenepool.h | 150 ++++++++ indra/newview/llfloatercustomize.cpp | 332 +++++++----------- indra/newview/llviewerwearable.cpp | 25 +- indra/newview/llviewerwearable.h | 6 +- indra/newview/llvoavatar.cpp | 211 ++++------- .../skins/default/xui/de/notifications.xml | 10 - .../skins/default/xui/en-us/notifications.xml | 42 +-- .../skins/default/xui/en-us/strings.xml | 9 + .../skins/default/xui/es/notifications.xml | 20 -- 19 files changed, 605 insertions(+), 429 deletions(-) create mode 100644 indra/newview/aixmllindengenepool.cpp create mode 100644 indra/newview/aixmllindengenepool.h 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/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0ebcc899f..c9ccca66f 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 @@ -598,6 +599,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..00890eb78 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,117 @@ 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; - } - - //------------------------------------------------------------------------- - // (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); - } + llwarns << "Invalid or incompatible linden_genepool metaversion: " << metaversion << " in file: " << filename << llendl; + args["TAG"] = "metaversion"; + args["VERSIONMAJOR"] = "1"; + LLNotificationsUtil::add("AIXMLImportRootVersionError", args); + return; } - 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 const& alert) + { + LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLImportError", AIArgs("[TYPE]", label), alert)); + return; + } + if (found_param || found_texture) { edit_wearable->writeToAvatar(gAgentAvatarp); @@ -558,10 +461,15 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) } } } - else + else if (found_type) { - args["TYPE"] = panel_edit_wearable->LLPanel::getLabel(); - args["ARCHETYPENAME"] = label; + args["TYPE"] = label; + LLNotificationsUtil::add("AIXMLImportEmptyArchetype", args); + } + else if (!wearable_types.empty()) + { + args["TYPE"] = label; + args["ARCHETYPENAME"] = wearable_types; LLNotificationsUtil::add("AIXMLImportWearableTypeMismatch", args); } } @@ -615,21 +523,29 @@ 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; + LLFILE* outfile = AIFile::fopen(filename, "wb"); + + AIXMLLindenGenepool linden_genepool(outfile); + linden_genepool.child(edit_wearable->getArchetype()); + + AIFile::close(outfile); + success = true; + } + catch (AIAlert const& alert) + { + LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLExportWriteError", AIArgs("[FILE]", filename), alert)); } - LLVOAvatar::dumpArchetypeXML_header(outfile, edit_wearable->getTypeName()); - edit_wearable->archetypeExport(outfile); - LLVOAvatar::dumpArchetypeXML_footer(outfile); + if (success) + { + LLSD args(LLSD::emptyMap()); + args["FILE"] = filename; + LLNotificationsUtil::add("AIXMLExportSuccess", args); + } } void LLFloaterCustomize::onBtnOk() diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index bdd03c0eb..7a8ea7338 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 ed242be4e..f45787f25 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" @@ -7676,29 +7678,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, @@ -8356,127 +8339,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 const& alert) { - llinfos << "xmlfile write handle obtained : " << fullpath << llendl; + LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLdumpArchetypeXMLError", AIArgs("[FILENAME]", fullpath), alert)); } - - 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..45fbc535a 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -69,18 +69,8 @@ - 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! diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 919e2595a..62f3cf1e2 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -149,30 +149,9 @@ -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. +Successfully exported wearable to "[FILE]". - -Import Failure: the file "[FILE]" contains invalid data. - - + +Import warning: the file "[FILE]" contains a wearable of the selected type ([TYPE]), +but contains no parameters or textures for that type. Nothing was imported. + + -Import Warning: the file "[FILE]" does not contain a wearable of type [TYPE]. +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. diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index 8ed04b942..c384090a3 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -4400,4 +4400,13 @@ Try enclosing path to the editor with double quotes. Invalid UUID in [FILEDESC] "[FILENAME]". Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]". + + archetype has no <meta> element. + archetype <meta> element has no '[ATTRIBUTE]' attribute. + + Failure dumping archetype to "[FILENAME]": + The export to file "[FILE]" failed: + Failed to import the [TYPE] wearable: + No archetype found in wearable import file "[FILE]". + diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 97f3dff5b..423b2c941 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -73,30 +73,10 @@ - - Fallo de Exportación: no se puede abrir el archivo "[FILE]" para escritura. - - - - Aviso de Importación: No se pueden aplicar las texturas: El [FILE] fue exportado en el grid "[GRIDNAME]" mientras que el grid actual es "[CURGRID]". - - - - Fallo de Importación: no se puede leer o analizar el archivo de vestimenta para importar "[FILE]". - - - - Fallo de Importación: el archivo "[FILE]" no es un archivo linden_genepool XML. - - Fallo de Importación: el archivo "[FILE]" contiene datos linden_genepool XML con [TAG] erróneo. Se requiere Versión "[VERSIONMAJOR].0" o inferior. - - Fallo de Importación: el archivo "[FILE]" contiene datos inválidos. - - La importación se ha realizado con exito, pero ten presente que el objeto vestible fue exportado desde el grid [EXPORTGRID] (y el grid actual es [CURRENTGRID]). ¡No se aplicaron las UUIDs de las Texturas! From 1a9cf9b0133522740aaee9e617aaca0c5c39d4e9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 03:55:40 +0100 Subject: [PATCH 11/14] Bug fix for 54b43f1ad7c7649ebba9e10767cf1e597c539a9e Initialize mLastSimAccess. --- indra/newview/llviewerregion.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 4146aa40b..7258fc212 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), From b5b51bd73b2b216ab1f37a1ad635f975f3f55cd9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 15:11:18 +0100 Subject: [PATCH 12/14] Typo fix in example code. --- indra/llxml/aixml.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llxml/aixml.cpp b/indra/llxml/aixml.cpp index 9f21cf56e..db5bba7e3 100644 --- a/indra/llxml/aixml.cpp +++ b/indra/llxml/aixml.cpp @@ -53,12 +53,12 @@ #ifdef EXAMPLE_CODE // undefined -class HelloWord { +class HelloWorld { public: // Write object to XML. void toXML(std::ostream& os, int indentation) const; // Read object from XML. - HelloWord(AIXMLElementParser const& parser); + HelloWorld(AIXMLElementParser const& parser); private: // Example member variables... @@ -73,7 +73,7 @@ class HelloWord { }; // Typical serialization member function. -void HelloWord::toXML(std::ostream& os, int indentation) const +void HelloWorld::toXML(std::ostream& os, int indentation) const { AIXMLElement tag(os, "helloworld", indentation); @@ -93,7 +93,7 @@ void HelloWord::toXML(std::ostream& os, int indentation) const } // Typical deserialization member function. -HelloWord::HelloWorld(AIXMLElementParser const& parser) +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". From 8ab9039ff631ac93b29c0d0f27ce844956d2b442 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 18:10:05 +0100 Subject: [PATCH 13/14] Use " and ' instead of " and ' for new msgs in strings.xml. Since it works... this might actually be better readable and therefore easier to work with for the translators. --- .../skins/default/xui/en-us/strings.xml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/indra/newview/skins/default/xui/en-us/strings.xml b/indra/newview/skins/default/xui/en-us/strings.xml index c384090a3..275bdf05f 100644 --- a/indra/newview/skins/default/xui/en-us/strings.xml +++ b/indra/newview/skins/default/xui/en-us/strings.xml @@ -4370,43 +4370,43 @@ Try enclosing path to the editor with double quotes. Failed to create folder [DIRNAME]: [ERROR] Failed to remove folder [DIRNAME]: [ERROR] - Failed to open file "[FILENAME]": [ERROR] + Failed to open file "[FILENAME]": [ERROR] Failed to close file: [ERROR] Failed to remove file [FILENAME]: [ERROR] Failed to rename file [FILE] to [NEWFILE]: [ERROR] - Failed to write '[DATA]', writing attribute data. + Failed to write '[DATA]', writing attribute data. ostream not good after calling child.toXML - Failed to write '[DATA]', writing child data. - Failed to write '[DATA]', opening new child. - Failed to write '[DATA]', closing previous child. + Failed to write '[DATA]', writing child data. + Failed to write '[DATA]', opening new child. + Failed to write '[DATA]', closing previous child. Failed to write XML header. - Cannot parse [FILEDESC] "[FILENAME]". - No root node found in [FILEDESC] "[FILENAME]". - Missing header '[NAME]' ; invalid [FILEDESC] "[FILENAME]". - Invalid or missing [NAME] 'version' attribute in [FILEDESC] "[FILENAME]". - Incompatible '[NAME]' version, [MAJOR].[MINOR], in [FILEDESC] "[FILENAME]". + Cannot parse [FILEDESC] "[FILENAME]". + No root node found in [FILEDESC] "[FILENAME]". + Missing header '[NAME]' ; invalid [FILEDESC] "[FILENAME]". + Invalid or missing [NAME] 'version' attribute in [FILEDESC] "[FILENAME]". + Incompatible '[NAME]' version, [MAJOR].[MINOR], in [FILEDESC] "[FILENAME]". - Invalid MD5 ([VALUE]) in [FILEDESC] "[FILENAME]". - Invalid boolean ([VALUE]) in [FILEDESC] "[FILENAME]". - Missing '[NAME]' attribute in [NODENAME] of [FILEDESC] "[FILENAME]"." - Invalid '[NAME]' attribute (should be '[REQUIRED]') in [NODENAME] of [FILEDESC] "[FILENAME]". - Invalid UUID in [FILEDESC] "[FILENAME]". - Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]". + Invalid MD5 ([VALUE]) in [FILEDESC] "[FILENAME]". + Invalid boolean ([VALUE]) in [FILEDESC] "[FILENAME]". + Missing '[NAME]' attribute in [NODENAME] of [FILEDESC] "[FILENAME]"." + Invalid '[NAME]' attribute (should be '[REQUIRED]') in [NODENAME] of [FILEDESC] "[FILENAME]". + Invalid UUID in [FILEDESC] "[FILENAME]". + Invalid DATE ([DATE]) in [FILEDESC] "[FILENAME]". archetype has no <meta> element. - archetype <meta> element has no '[ATTRIBUTE]' attribute. + archetype <meta> element has no '[ATTRIBUTE]' attribute. - Failure dumping archetype to "[FILENAME]": - The export to file "[FILE]" failed: + Failure dumping archetype to "[FILENAME]": + The export to file "[FILE]" failed: Failed to import the [TYPE] wearable: - No archetype found in wearable import file "[FILE]". + No archetype found in wearable import file "[FILE]". From c516a71804cd9e11391ef7686d1585d91db4bbf3 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Nov 2013 22:30:02 +0100 Subject: [PATCH 14/14] Move AIAlert* into namespace AIAlert and rename classes. Instead of having several AI* classes, it turned out to be easier to have a namespace: this allows me to define the classes in llcommon but add (new) 'add' and 'add_modal' functions to 'AIAlert' in llui. This is needed to avoid a collision with the 'add' functions in LLNotificationsUtil. The new add/add_modal makes it a lot easier to just show a caught alert, prepending or appending new text: it turns out that that is way more common then a re-throw. Adjusted code as appropriate. --- indra/llcommon/aialert.cpp | 46 ++-- indra/llcommon/aialert.h | 214 +++++++++--------- indra/llcommon/aifile.cpp | 2 +- indra/llui/llnotifications.cpp | 6 +- indra/llui/llnotifications.h | 4 +- indra/llui/llnotificationsutil.cpp | 39 +++- indra/llui/llnotificationsutil.h | 43 +++- indra/newview/llfloatercustomize.cpp | 39 ++-- indra/newview/llvoavatar.cpp | 4 +- .../skins/default/xui/de/notifications.xml | 9 - .../skins/default/xui/en-us/notifications.xml | 44 ---- .../skins/default/xui/en-us/strings.xml | 8 +- .../skins/default/xui/es/notifications.xml | 17 -- .../newview/skins/default/xui/es/strings.xml | 55 ++++- 14 files changed, 300 insertions(+), 230 deletions(-) diff --git a/indra/llcommon/aialert.cpp b/indra/llcommon/aialert.cpp index 44d2c3230..f3d4f5af3 100644 --- a/indra/llcommon/aialert.cpp +++ b/indra/llcommon/aialert.cpp @@ -25,43 +25,51 @@ * * 02/11/2013 * - Initial version, written by Aleric Inglewood @ SL + * + * 05/11/2013 + * Moved everything in namespace AIAlert, except AIArgs. */ #include "aialert.h" -AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, - std::string const& xml_desc, AIArgs const& args) : mModal(type) +namespace AIAlert { - if (prefix) mLines.push_back(AIAlertLine(prefix)); - mLines.push_back(AIAlertLine(xml_desc, args)); + +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)); } -AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, - AIAlert const& alert, - std::string const& xml_desc, AIArgs const& args) : mLines(alert.mLines), mModal(type) +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(AIAlertLine(prefix, !mLines.empty())); - mLines.push_back(AIAlertLine(xml_desc, args)); + if (prefix) mLines.push_back(Line(prefix, !mLines.empty())); + mLines.push_back(Line(xml_desc, args)); } -AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, - std::string const& xml_desc, - AIAlert const& alert) : mLines(alert.mLines), mModal(type) +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(AIAlertLine(xml_desc)); - if (prefix) mLines.push_front(AIAlertLine(prefix)); + mLines.push_front(Line(xml_desc)); + if (prefix) mLines.push_front(Line(prefix)); } -AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type, - std::string const& xml_desc, AIArgs const& args, - AIAlert const& alert) : mLines(alert.mLines), mModal(type) +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(AIAlertLine(xml_desc, args)); - if (prefix) mLines.push_front(AIAlertLine(prefix)); + 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 index 655a4890d..83a0da300 100644 --- a/indra/llcommon/aialert.h +++ b/indra/llcommon/aialert.h @@ -1,6 +1,6 @@ /** * @file aialert.h - * @brief Declaration of AIArgs, AIAlertPrefix, AIAlertLine, AIAlert and AIAlertCode + * @brief Declaration of AIArgs and AIAlert classes. * * Copyright (c) 2013, Aleric Inglewood. * @@ -26,6 +26,9 @@ * * 02/11/2013 * Initial version, written by Aleric Inglewood @ SL + * + * 05/11/2013 + * Moved everything in namespace AIAlert, except AIArgs. */ #ifndef AI_ALERT @@ -40,28 +43,28 @@ // 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(AIAlertPrefix(), AIAlert::not_modal, __VA_ARGS__) -#define THROW_MALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(), AIAlert::modal, __VA_ARGS__) -#define THROW_FALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::not_modal, __VA_ARGS__) -#define THROW_FMALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::modal, __VA_ARGS__) +#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. -#define THROW_ALERT(...) THROW_ALERT_CLASS(AIAlert, __VA_ARGS__) -#define THROW_MALERT(...) THROW_MALERT_CLASS(AIAlert, __VA_ARGS__) -#define THROW_FALERT(...) THROW_FALERT_CLASS(AIAlert, __VA_ARGS__) -#define THROW_FMALERT(...) THROW_FMALERT_CLASS(AIAlert, __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 AIAlertCode. -#define THROW_ALERTC(...) THROW_ALERT_CLASS(AIAlertCode, __VA_ARGS__) -#define THROW_MALERTC(...) THROW_MALERT_CLASS(AIAlertCode, __VA_ARGS__) -#define THROW_FALERTC(...) THROW_FALERT_CLASS(AIAlertCode, __VA_ARGS__) -#define THROW_FMALERTC(...) THROW_FMALERT_CLASS(AIAlertCode, __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 AIAlertCode with errno as code. -#define THROW_ALERTE(...) do { int errn = errno; THROW_ALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) -#define THROW_MALERTE(...) do { int errn = errno; THROW_MALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) -#define THROW_FALERTE(...) do { int errn = errno; THROW_FALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) -#define THROW_FMALERTE(...) do { int errn = errno; THROW_FMALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0) +// 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 @@ -70,18 +73,18 @@ //---------------------------------------------------------- // To show the alert box: - catch (AIAlert const& alert) + catch (AIAlert::Error const& error) { - LLNotificationsUtil::add(alert); // Optionally pass alert_line_pretty_function_prefix as second parameter to *suppress* that output. + AIAlert::add(error); // Optionally pass pretty_function_prefix as second parameter to *suppress* that output. } // or, for example - catch (AIAlertCode const& alert) + catch (AIAlert::ErrorCode const& error) { - if (alert.getCode() != EEXIST) + if (error.getCode() != EEXIST) { - LLNotificationsUtil::add(alert, alert_line_pretty_function_prefix); + AIAlert::add(alert, AIAlert::pretty_function_prefix); } } //---------------------------------------------------------- @@ -89,15 +92,15 @@ 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", alert); // C) As A, but followed by a colon and a newline, and then the text of 'alert'. - THROW_ALERT(alert, "ExampleKey"); // D) The text of 'alert', followed by a colon and a newline and then as A. - THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second), alert); // E) As B, but followed by a colon and a newline, and then the text of 'alert'. - THROW_ALERT(alert, "ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)); // F) The text of 'alert', followed by a colon and a newline and then as B. - // where 'alert' is a caught AIAlert object (as above) in a rethrow. + 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 AIAlertCode class that contains the current errno. + // 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. @@ -106,34 +109,6 @@ // //=================================================================================================================================== -enum alert_line_type_nt -{ - alert_line_normal = 0, - alert_line_empty_prefix = 1, - alert_line_pretty_function_prefix = 2 - // These must exist of single bits (a mask). -}; - -// An AIAlertPrefix currently comes only in two flavors: -// -// alert_line_empty_prefix : An empty prefix. -// alert_line_pretty_function_prefix : A function name prefix, this is the function from which the alert was thrown. - -class LL_COMMON_API AIAlertPrefix -{ - public: - AIAlertPrefix(void) : mType(alert_line_empty_prefix) { } - AIAlertPrefix(char const* str, alert_line_type_nt type) : mStr(str), mType(type) { } - - operator bool(void) const { return mType != alert_line_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 wrapper around LLStringUtil::format_map_t to allow constructing a dictionary // on one line by doing: // @@ -158,25 +133,62 @@ class LL_COMMON_API AIArgs 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 AIAlertLine +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: alert_line_normal for normal lines, other for prefixes. + alert_line_type_nt mType; // The type of this line: normal for normal lines, other for prefixes. public: - AIAlertLine(std::string const& xml_desc, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mType(alert_line_normal) { } - AIAlertLine(std::string const& xml_desc, AIArgs const& args, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mArgs(args), mType(alert_line_normal) { } - AIAlertLine(AIAlertPrefix const& prefix, bool newline = false) : mNewline(newline), mXmlDesc("AIPrefix"), mArgs("[PREFIX]", prefix.str()), mType(prefix.type()) { } + 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. - ~AIAlertLine() throw() { } + ~Line() throw() { } // Prepend a newline before this line. void set_newline(void) { mNewline = true; } @@ -188,7 +200,7 @@ class LL_COMMON_API AIAlertLine // Accessors. bool suppressed(unsigned int suppress_mask) const { return (suppress_mask & mType) != 0; } - bool is_prefix(void) const { return mType != alert_line_normal; } + bool is_prefix(void) const { return mType != normal; } }; // This class is used to throw an error that will cause @@ -200,82 +212,82 @@ class LL_COMMON_API AIAlertLine // 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(AIAlert const&). +// Instead call LLNotificationUtil::add(Error const&). -class LL_COMMON_API AIAlert : public std::exception +class LL_COMMON_API Error : public std::exception { public: - typedef std::deque lines_type; - enum modal_nt { not_modal, modal }; + typedef std::deque lines_type; // The destructor may not throw. - ~AIAlert() 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. - AIAlert(AIAlertPrefix const& prefix, modal_nt modal, - std::string const& xml_desc, AIArgs const& args = AIArgs()); + 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. - AIAlert(AIAlertPrefix const& prefix, modal_nt modal, - AIAlert const& alert, - std::string const& xml_desc, AIArgs const& args = AIArgs()); + 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) - AIAlert(AIAlertPrefix const& prefix, modal_nt modal, - std::string const& xml_desc, - AIAlert const& alert); + Error(Prefix const& prefix, modal_nt type, + std::string const& xml_desc, + Error const& alert); // (with args) - AIAlert(AIAlertPrefix const& prefix, modal_nt modal, - std::string const& xml_desc, AIArgs const& args, - AIAlert const& alert); + 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 AIAlert but allows to pass an additional error code. +// Same as Error but allows to pass an additional error code. -class LL_COMMON_API AIAlertCode : public AIAlert +class LL_COMMON_API ErrorCode : public Error { private: int mCode; public: // The destructor may not throw. - ~AIAlertCode() throw() { } + ~ErrorCode() throw() { } // Accessor. int getCode(void) const { return mCode; } // A string with zero or more replacements. - AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, - std::string const& xml_desc, AIArgs const& args = AIArgs()) : - AIAlert(prefix, modal, xml_desc, args) { } + 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. - AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, - AIAlert const& alert, - std::string const& xml_desc, AIArgs const& args = AIArgs()) : - AIAlert(prefix, modal, alert, xml_desc, args) { } + 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) - AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, - std::string const& xml_desc, - AIAlert const& alert) : - AIAlert(prefix, modal, xml_desc, alert) { } + 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) - AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code, - std::string const& xml_desc, AIArgs const& args, - AIAlert const& alert) : - AIAlert(prefix, modal, xml_desc, args, alert) { } - + 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 index 4013f3cf7..c963f5109 100644 --- a/indra/llcommon/aifile.cpp +++ b/indra/llcommon/aifile.cpp @@ -55,7 +55,7 @@ AIFile::~AIFile() int errn = errno; \ std::ostringstream error; \ error << LLFile::strerr(errn) << " (" << errn << ')'; \ - THROW_MALERT_CLASS(AIAlertCode, errn, __VA_ARGS__ ("[ERROR]", error.str())); \ + THROW_MALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__ ("[ERROR]", error.str())); \ } while(0) //static diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 2f60b99fa..73dd99109 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1480,12 +1480,12 @@ LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) return pNotif; } -LLNotificationPtr LLNotifications::add(AIAlert const& alert, unsigned int suppress_mask) +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::lines_type::const_iterator line = alert.lines().begin(); line != alert.lines().end(); ++line) + 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()) @@ -1503,7 +1503,7 @@ LLNotificationPtr LLNotifications::add(AIAlert const& alert, unsigned int suppre } LLSD substitutions = LLSD::emptyMap(); substitutions["[PAYLOAD]"] = alert_text; - return add(LLNotification::Params(alert.is_modal() ? "AIAlertModal" : "AIAlert").substitutions(substitutions)); + return add(LLNotification::Params((type == AIAlert::modal || error.is_modal()) ? "AIAlertModal" : "AIAlert").substitutions(substitutions)); } diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 4adfabbcb..bb769cae7 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -108,7 +108,7 @@ #include "llnotificationptr.h" #include "llnotificationcontext.h" -class AIAlert; +namespace AIAlert { class Error; } typedef enum e_notification_priority { @@ -739,7 +739,7 @@ public: const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor); - LLNotificationPtr add(AIAlert const& alert, unsigned int suppress_mask); + 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 13e2b897d..134a5c267 100644 --- a/indra/llui/llnotificationsutil.cpp +++ b/indra/llui/llnotificationsutil.cpp @@ -30,11 +30,46 @@ #include "llsd.h" #include "llxmlnode.h" // apparently needed to call LLNotifications::instance() -LLNotificationPtr LLNotificationsUtil::add(AIAlert const& alert, unsigned int suppress_mask) +namespace AIAlert { - return LLNotifications::instance().add(alert, suppress_mask); + +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 586b0d0bc..fb0fb1ba4 100644 --- a/indra/llui/llnotificationsutil.h +++ b/indra/llui/llnotificationsutil.h @@ -30,16 +30,53 @@ // to avoid including the heavyweight llnotifications.h #include "llnotificationptr.h" +#include "aialert.h" #include class LLSD; -class AIAlert; + +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(AIAlert const& alert, unsigned int suppress_mask = 0); // Singu extension. - LLNotificationPtr add(const std::string& name); LLNotificationPtr add(const std::string& name, diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 00890eb78..0df0663b9 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -336,8 +336,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) std::string const filename = filepicker->getFilename(); - LLSD args(LLSD::emptyMap()); - args["FILE"] = gDirUtilp->getBaseFileName(filename); + AIArgs args("[FILE]", gDirUtilp->getBaseFileName(filename)); bool found_param = false; bool found_texture = false; @@ -362,11 +361,7 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) 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; + THROW_MALERT("AIXMLImportRootVersionError", args("[TAG]", "metaversion")("[VERSIONMAJOR]", "1")); } //------------------------------------------------------------------------- @@ -436,9 +431,9 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) } } } - catch (AIAlert const& alert) + catch (AIAlert::Error const& error) { - LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLImportError", AIArgs("[TYPE]", label), alert)); + AIAlert::add_modal("AIXMLImportError", AIArgs("[TYPE]", label), error); return; } @@ -449,28 +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 if (found_type) { - args["TYPE"] = label; - LLNotificationsUtil::add("AIXMLImportEmptyArchetype", args); + AIAlert::add("AIXMLImportEmptyArchetype", args("[TYPE]", label)); } else if (!wearable_types.empty()) { - args["TYPE"] = label; - args["ARCHETYPENAME"] = wearable_types; - LLNotificationsUtil::add("AIXMLImportWearableTypeMismatch", args); + AIAlert::add("AIXMLImportWearableTypeMismatch", args("[TYPE]", label)("[ARCHETYPENAME]", wearable_types)); } } @@ -527,24 +519,21 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, bool success = false; try { - LLFILE* outfile = AIFile::fopen(filename, "wb"); + AIFile outfile(filename, "wb"); AIXMLLindenGenepool linden_genepool(outfile); linden_genepool.child(edit_wearable->getArchetype()); - AIFile::close(outfile); success = true; } - catch (AIAlert const& alert) + catch (AIAlert::Error const& error) { - LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLExportWriteError", AIArgs("[FILE]", filename), alert)); + AIAlert::add_modal("AIXMLExportWriteError", AIArgs("[FILE]", filename), error); } if (success) { - LLSD args(LLSD::emptyMap()); - args["FILE"] = filename; - LLNotificationsUtil::add("AIXMLExportSuccess", args); + AIAlert::add_modal("AIXMLExportSuccess", AIArgs("[FILE]", filename)); } } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index f45787f25..c23def96e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -8412,9 +8412,9 @@ void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_b } #endif } - catch(AIAlert const& alert) + catch (AIAlert::Error const& error) { - LLNotificationsUtil::add(AIAlert(AIAlertPrefix(), AIAlert::modal, "AIXMLdumpArchetypeXMLError", AIArgs("[FILENAME]", fullpath), alert)); + AIAlert::add_modal("AIXMLdumpArchetypeXMLError", AIArgs("[FILE]", fullpath), error); } } diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index 45fbc535a..6d6f2bba0 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -69,15 +69,6 @@ - Import Failure: the file "[FILE]" contains linden_genepool XML data with the wrong [TAG]. Version "[VERSIONMAJOR].0" or less is required. - - 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.