From 0bf6386c61c0c28b298c2cccd82c786f754024af Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Fri, 17 Nov 2017 13:04:28 -0800
Subject: [PATCH] Now with validation.

---
 indra/newview/llsettingsbase.cpp     | 335 +++++++++++++++++++++++++--
 indra/newview/llsettingsbase.h       |  50 +++-
 indra/newview/llsettingsdaycycle.cpp |  90 ++++++-
 indra/newview/llsettingsdaycycle.h   |   4 +-
 indra/newview/llsettingssky.cpp      | 126 +++++++---
 indra/newview/llsettingssky.h        |   3 +
 indra/newview/llsettingswater.cpp    |  65 +++++-
 indra/newview/llsettingswater.h      |   1 +
 indra/newview/llvosky.cpp            |   2 +-
 9 files changed, 607 insertions(+), 69 deletions(-)

diff --git a/indra/newview/llsettingsbase.cpp b/indra/newview/llsettingsbase.cpp
index c538cbe320c..904d0dd07c1 100644
--- a/indra/newview/llsettingsbase.cpp
+++ b/indra/newview/llsettingsbase.cpp
@@ -248,20 +248,6 @@ LLSD LLSettingsBase::cloneSettings() const
     return combineSDMaps(mSettings, LLSD());
 }
 
-LLSettingsBase::ptr_t LLSettingsBase::buildBlend(const ptr_t &begin, const ptr_t &end, F32 blendf)
-{
-//     if (begin->getSettingType() != end->getSettingType())
-//     {
-//         LL_WARNS("SETTINGS") << "Attempt to blend settings of different types! " << 
-//             begin->getSettingType() << "<->" << end->getSettingType() << LL_ENDL;
-// 
-//         return LLSettingsBase::ptr_t();
-//     }
-
-//    return begin->blend(end, blendf);
-    return LLSettingsBase::ptr_t();
-}
-
 void LLSettingsBase::exportSettings(std::string name) const
 {
     LLSD exprt = LLSDMap("type", LLSD::String(getSettingType()))
@@ -279,7 +265,6 @@ void LLSettingsBase::exportSettings(std::string name) const
         presetsXML.close();
 
         LL_DEBUGS() << "saved preset '" << name << "'; " << mSettings.size() << " settings" << LL_ENDL;
-
     }
     else
     {
@@ -287,6 +272,326 @@ void LLSettingsBase::exportSettings(std::string name) const
     }
 }
 
+#ifdef VALIDATION_DEBUG
+namespace
+{
+    LLSD clone_llsd(LLSD value)
+    {
+        LLSD clone;
+
+        switch (value.type())
+        {
+//         case LLSD::TypeMap:
+//             newSettings[key_name] = combineSDMaps(value, LLSD());
+//             break;
+        case LLSD::TypeArray:
+            clone = LLSD::emptyArray();
+            for (LLSD::array_const_iterator ita = value.beginArray(); ita != value.endArray(); ++ita)
+            {
+                clone.append( clone_llsd(*ita) );
+            }
+            break;
+        case LLSD::TypeInteger:
+            clone = LLSD::Integer(value.asInteger());
+            break;
+        case LLSD::TypeReal:
+            clone = LLSD::Real(value.asReal());
+            break;
+        case LLSD::TypeBoolean:
+            clone = LLSD::Boolean(value.asBoolean());
+            break;
+        case LLSD::TypeString:
+            clone = LLSD::String(value.asString());
+            break;
+        case LLSD::TypeUUID:
+            clone = LLSD::UUID(value.asUUID());
+            break;
+        case LLSD::TypeURI:
+            clone = LLSD::URI(value.asURI());
+            break;
+        case LLSD::TypeDate:
+            clone = LLSD::Date(value.asDate());
+            break;
+        //case LLSD::TypeBinary:
+        //    break;
+        //default:
+        //    newSettings[key_name] = value;
+        //    break;
+        }
+
+        return clone;
+    }
+
+    bool compare_llsd(LLSD valA, LLSD valB)
+    {
+        if (valA.type() != valB.type())
+            return false;
+
+        switch (valA.type())
+        {
+        //         case LLSD::TypeMap:
+        //             newSettings[key_name] = combineSDMaps(value, LLSD());
+        //             break;
+        case LLSD::TypeArray:
+            if (valA.size() != valB.size())
+                return false;
+
+            for (S32 idx = 0; idx < valA.size(); ++idx)
+            {
+                if (!compare_llsd(valA[idx], valB[idx]))
+                    return false;
+            }
+            return true;
+
+        case LLSD::TypeInteger:
+            return valA.asInteger() == valB.asInteger();
+
+        case LLSD::TypeReal:
+            return is_approx_equal(valA.asReal(), valB.asReal());
+
+        case LLSD::TypeBoolean:
+            return valA.asBoolean() == valB.asBoolean();
+
+        case LLSD::TypeString:
+            return valA.asString() == valB.asString();
+
+        case LLSD::TypeUUID:
+            return valA.asUUID() == valB.asUUID();
+
+        case LLSD::TypeURI:
+            return valA.asString() == valB.asString();
+
+        case LLSD::TypeDate:
+            return valA.asDate() == valB.asDate();
+        }
+
+        return true;
+    }
+}
+#endif
+
+bool LLSettingsBase::validate()
+{
+    static Validator  validateName(SETTING_NAME, false, LLSD::TypeString);
+    static Validator  validateId(SETTING_ID, false, LLSD::TypeUUID);
+    validation_list_t validations = getValidationList();
+    stringset_t       validated;
+    stringset_t       strip;
+
+    // Fields common to all settings.
+    if (!validateName.verify(mSettings))
+    {
+        LL_WARNS("SETTINGS") << "Unable to validate name." << LL_ENDL;
+        mIsValid = false;
+        return false;
+    }
+    validated.insert(validateName.getName());
+
+    if (!validateId.verify(mSettings))
+    {
+        LL_WARNS("SETTINGS") << "Unable to validate Id." << LL_ENDL;
+        mIsValid = false;
+        return false;
+    }
+    validated.insert(validateId.getName());
+
+    // Fields for specific settings.
+    for (validation_list_t::iterator itv = validations.begin(); itv != validations.end(); ++itv)
+    {
+#ifdef VALIDATION_DEBUG
+        LLSD oldvalue;
+        if (mSettings.has((*itv).getName()))
+        {
+            oldvalue = clone_llsd(mSettings[(*itv).getName()]);
+        }
+#endif
+
+        if (!(*itv).verify(mSettings))
+        {
+            LL_WARNS("SETTINGS") << "Settings LLSD fails validation and could not be corrected!" << LL_ENDL;
+            mIsValid = false;
+            return false;
+        }
+        validated.insert((*itv).getName());
+
+#ifdef VALIDATION_DEBUG
+        if (!oldvalue.isUndefined())
+        {
+            if (!compare_llsd(mSettings[(*itv).getName()], oldvalue))
+            {
+                LL_WARNS("SETTINGS") << "Setting '" << (*itv).getName() << "' was changed: " << oldvalue << " -> " << mSettings[(*itv).getName()] << LL_ENDL;
+            }
+        }
+#endif
+    }
+
+    // strip extra entries
+    for (LLSD::map_iterator itm = mSettings.beginMap(); itm != mSettings.endMap(); ++itm)
+    {
+        if (validated.find((*itm).first) == validated.end())
+        {
+            LL_WARNS("SETTINGS") << "Stripping setting '" << (*itm).first << "'" << LL_ENDL;
+            strip.insert((*itm).first);
+        }
+    }
+
+    for (stringset_t::iterator its = strip.begin(); its != strip.end(); ++its)
+    {
+        mSettings.erase(*its);
+    }
+
+    return true;
+}
+
+//=========================================================================
+bool LLSettingsBase::Validator::verify(LLSD &data)
+{
+    if (!data.has(mName))
+    {
+        if (mRequired)
+            LL_WARNS("SETTINGS") << "Missing required setting '" << mName << "'" << LL_ENDL;
+        return !mRequired;
+    }
+
+    if (data[mName].type() != mType)
+    {
+        LL_WARNS("SETTINGS") << "Setting '" << mName << "' is incorrect type." << LL_ENDL;
+        return false;
+    }
+
+    if (!mVerify.empty() && !mVerify(data[mName]))
+    {
+        LL_WARNS("SETTINGS") << "Setting '" << mName << "' fails validation." << LL_ENDL;
+        return false;
+    }
+
+    return true;
+}
+
+bool LLSettingsBase::Validator::verifyColor(LLSD &value)
+{
+    return (value.size() == 3 || value.size() == 4);
+}
+
+bool LLSettingsBase::Validator::verifyVector(LLSD &value, S32 length)
+{
+    return (value.size() == length);
+}
+
+bool LLSettingsBase::Validator::verifyVectorNormalized(LLSD &value, S32 length)
+{
+    if (value.size() != length)
+        return false;
+
+    LLSD newvector;
+
+    switch (length)
+    {
+    case 2:
+    {
+        LLVector2 vect(value);
+
+        if (is_approx_equal(vect.normalize(), 1.0f))
+            return true;
+        newvector = vect.getValue();
+        break;
+    }
+    case 3:
+    {
+        LLVector3 vect(value);
+
+        if (is_approx_equal(vect.normalize(), 1.0f))
+            return true;
+        newvector = vect.getValue();
+        break;
+    }
+    case 4:
+    {
+        LLVector4 vect(value);
+
+        if (is_approx_equal(vect.normalize(), 1.0f))
+            return true;
+        newvector = vect.getValue();
+        break;
+    }
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+bool LLSettingsBase::Validator::verifyVectorMinMax(LLSD &value, LLSD minvals, LLSD maxvals)
+{
+    for (S32 index = 0; index < value.size(); ++index)
+    {
+        if (minvals[index].asString() != "*")
+        {
+            if (minvals[index].asReal() > value[index].asReal())
+            {
+                value[index] = minvals[index].asReal();
+            }
+        }
+        if (maxvals[index].asString() != "*") 
+        {
+            if (maxvals[index].asReal() < value[index].asReal())
+            {
+                value[index] = maxvals[index].asReal();
+            }
+        }
+    }
+
+    return true;
+}
+
+bool LLSettingsBase::Validator::verifyQuaternion(LLSD &value)
+{
+    return (value.size() == 4);
+}
+
+bool LLSettingsBase::Validator::verifyQuaternionNormal(LLSD &value)
+{
+    if (value.size() != 4)
+        return false;
+
+    LLQuaternion quat(value);
+
+    if (is_approx_equal(quat.normalize(), 1.0f))
+        return true;
+
+    LLSD newquat = quat.getValue();
+    for (S32 index = 0; index < 4; ++index)
+    {
+        value[index] = newquat[index];
+    }
+    return true;
+}
+
+bool LLSettingsBase::Validator::verifyFloatRange(LLSD &value, LLSD range)
+{
+    F32 real = value.asReal();
+
+    F32 clampedval = llclamp(LLSD::Real(real), range[0].asReal(), range[1].asReal());
+
+    if (is_approx_equal(clampedval, real))
+        return true;
+
+    value = LLSD::Real(clampedval);
+    return true;
+}
+
+bool LLSettingsBase::Validator::verifyIntegerRange(LLSD &value, LLSD range)
+{
+    S32 ival = value.asInteger();
+
+    S32 clampedval = llclamp(LLSD::Integer(ival), range[0].asInteger(), range[1].asInteger());
+
+    if (clampedval == ival)
+        return true;
+
+    value = LLSD::Integer(clampedval);
+    return true;
+}
 
 //=========================================================================
 
diff --git a/indra/newview/llsettingsbase.h b/indra/newview/llsettingsbase.h
index bf4053a4b11..d32fcb26e88 100644
--- a/indra/newview/llsettingsbase.h
+++ b/indra/newview/llsettingsbase.h
@@ -41,7 +41,9 @@
 #include "llquaternion.h"
 #include "v4color.h"
 
-class LLSettingsBase: private boost::noncopyable
+class LLSettingsBase : 
+    public boost::enable_shared_from_this<LLSettingsBase>,
+    private boost::noncopyable
 {
     friend class LLEnvironment;
     friend class LLSettingsDay;
@@ -59,8 +61,6 @@ class LLSettingsBase: private boost::noncopyable
     //---------------------------------------------------------------------
     virtual std::string getSettingType() const = 0;
 
-    static ptr_t buildBlend(const ptr_t &begin, const ptr_t &end, F32 blendf);
-
     //---------------------------------------------------------------------
     // Settings status 
     inline bool hasSetting(const std::string &param) const { return mSettings.has(param); }
@@ -153,13 +153,50 @@ class LLSettingsBase: private boost::noncopyable
 
     virtual void blend(const ptr_t &end, F32 blendf) = 0;
 
+    virtual bool validate();
+
 protected:
+    class Validator
+    {
+    public:
+        typedef boost::function<bool(LLSD &)> verify_pr;
+
+        Validator(std::string name, bool required, LLSD::Type type, verify_pr verify = verify_pr()) :
+            mName(name),
+            mRequired(required),
+            mType(type),
+            mVerify(verify)
+        {   }
+
+        std::string getName() const { return mName; }
+        bool        isRequired() const { return mRequired; }
+        LLSD::Type  getType() const { return mType; }
+
+        bool        verify(LLSD &data);
+
+        // Some basic verifications
+        static bool verifyColor(LLSD &value);
+        static bool verifyVector(LLSD &value, S32 length);
+        static bool verifyVectorMinMax(LLSD &value, LLSD minvals, LLSD maxvals);
+        static bool verifyVectorNormalized(LLSD &value, S32 length);
+        static bool verifyQuaternion(LLSD &value);
+        static bool verifyQuaternionNormal(LLSD &value);
+        static bool verifyFloatRange(LLSD &value, LLSD range);
+        static bool verifyIntegerRange(LLSD &value, LLSD range);
+
+    private:
+        std::string mName;
+        bool        mRequired;
+        LLSD::Type  mType;
+        verify_pr   mVerify;
+    };
+    typedef std::vector<Validator> validation_list_t;
+
     LLSettingsBase();
     LLSettingsBase(const LLSD setting);
 
     typedef std::set<std::string>   stringset_t;
-
-
+    
     // combining settings objects. Customize for specific setting types
     virtual void lerpSettings(const LLSettingsBase &other, F32 mix);
     LLSD    interpolateSDMap(const LLSD &settings, const LLSD &other, F32 mix) const;
@@ -176,12 +213,15 @@ class LLSettingsBase: private boost::noncopyable
     // Calculate any custom settings that may need to be cached.
     virtual void updateSettings() { mDirty = false; };
 
+    virtual validation_list_t getValidationList() const = 0;
+
     // Apply any settings that need special handling. 
     virtual void applySpecial(void *) { };
 
     virtual parammapping_t getParameterMap() const { return parammapping_t(); }
 
     LLSD    mSettings;
+    bool    mIsValid;
 
     LLSD    cloneSettings() const;
 
diff --git a/indra/newview/llsettingsdaycycle.cpp b/indra/newview/llsettingsdaycycle.cpp
index da384304d67..391aeddf1cc 100644
--- a/indra/newview/llsettingsdaycycle.cpp
+++ b/indra/newview/llsettingsdaycycle.cpp
@@ -113,7 +113,6 @@ const std::string LLSettingsDay::SETTING_DAYLENGTH("day_length");
 const std::string LLSettingsDay::SETTING_KEYID("key_id");
 const std::string LLSettingsDay::SETTING_KEYNAME("key_name");
 const std::string LLSettingsDay::SETTING_KEYKFRAME("key_keyframe");
-const std::string LLSettingsDay::SETTING_NAME("name");
 const std::string LLSettingsDay::SETTING_TRACKS("tracks");
 
 //const S64 LLSettingsDayCycle::MINIMUM_DAYLENGTH(  300); // 5 mins
@@ -123,6 +122,7 @@ const S64 LLSettingsDay::MAXIMUM_DAYLENGTH(604800); // 7 days
 
 const S32 LLSettingsDay::TRACK_WATER(0);   // water track is 0
 const S32 LLSettingsDay::TRACK_MAX(5);     // 5 tracks, 4 skys, 1 water
+const S32 LLSettingsDay::FRAME_MAX(56);
 
 //=========================================================================
 LLSettingsDay::LLSettingsDay(const LLSD &data) :
@@ -178,7 +178,10 @@ LLSettingsDay::ptr_t LLSettingsDay::buildFromLegacyPreset(const std::string &nam
     LLSettingsDay::ptr_t dayp = boost::make_shared<LLSettingsDay>(newsettings);
     dayp->parseFromLLSD(dayp->mSettings);
 
-    return dayp;
+    if (dayp->validate())
+        return dayp;
+
+    return LLSettingsDay::ptr_t();
 }
 
 LLSettingsDay::ptr_t LLSettingsDay::buildFromLegacyMessage(const LLUUID &regionId, LLSD daycycle, LLSD skydefs, LLSD waterdef)
@@ -195,9 +198,9 @@ LLSettingsDay::ptr_t LLSettingsDay::buildFromLegacyMessage(const LLUUID &regionI
         LL_WARNS("WindlightCaps") << "created region sky '" << name << "'" << LL_ENDL;
     }
 
-    LLSettingsDay::ptr_t day = buildFromLegacyPreset("Region (legacy)", daycycle);
+    LLSettingsDay::ptr_t dayp = buildFromLegacyPreset("Region (legacy)", daycycle);
 
-    day->setWaterAtKeyframe(water, 0.0f);
+    dayp->setWaterAtKeyframe(water, 0.0f);
 
     for (LLSD::array_iterator ita = daycycle.beginArray(); ita != daycycle.endArray(); ++ita)
     {
@@ -208,14 +211,17 @@ LLSettingsDay::ptr_t LLSettingsDay::buildFromLegacyMessage(const LLUUID &regionI
 
         if (it == skys.end())
             continue;
-        day->setSkyAtKeyframe(boost::static_pointer_cast<LLSettingsSky>((*it).second), frame, 1);
+        dayp->setSkyAtKeyframe(boost::static_pointer_cast<LLSettingsSky>((*it).second), frame, 1);
 
         LL_WARNS("WindlightCaps") << "Added '" << name << "' to region day cycle at " << frame << LL_ENDL;
     }
 
-    day->mHasParsed = true;
+    dayp->mHasParsed = true;
 
-    return day;
+    if (dayp->validate())
+        return dayp;
+
+    return LLSettingsDay::ptr_t();
 }
 
 LLSettingsDay::ptr_t LLSettingsDay::buildDefaultDayCycle()
@@ -225,7 +231,10 @@ LLSettingsDay::ptr_t LLSettingsDay::buildDefaultDayCycle()
     LLSettingsDay::ptr_t dayp = boost::make_shared<LLSettingsDay>(settings);
     dayp->parseFromLLSD(dayp->mSettings);
 
-    return dayp;
+    if (dayp->validate())
+        return dayp;
+
+    return LLSettingsDay::ptr_t();
 }
 
 void LLSettingsDay::parseFromLLSD(LLSD &data)
@@ -277,6 +286,71 @@ void LLSettingsDay::blend(const LLSettingsBase::ptr_t &other, F32 mix)
     LL_ERRS("DAYCYCLE") << "Day cycles are not blendable!" << LL_ENDL;
 }
 
+namespace
+{
+    bool validateDayCycleTrack(LLSD &value)
+    {
+        // Trim extra tracks.
+        while (value.size() > LLSettingsDay::TRACK_MAX)
+        {
+            value.erase(value.size() - 1);
+        }
+
+        for (LLSD::array_iterator track = value.beginArray(); track != value.endArray(); ++track)
+        {
+            S32 index = 0;
+            while (index < (*track).size())
+            {
+                if (index >= LLSettingsDay::FRAME_MAX)
+                {
+                    (*track).erase(index);
+                    continue;
+                }
+
+                if (!(*track)[index].has(LLSettingsDay::SETTING_KEYKFRAME) ||
+                        !(*track)[index][LLSettingsDay::SETTING_KEYKFRAME].isReal())
+                {
+                    (*track).erase(index);
+                    continue;
+                }
+
+                if (!(*track)[index].has(LLSettingsDay::SETTING_KEYNAME) &&
+                    !(*track)[index].has(LLSettingsDay::SETTING_KEYID))
+                {
+                    (*track).erase(index);
+                    continue;
+                }
+
+                F32 frame = (*track)[index][LLSettingsDay::SETTING_KEYKFRAME].asReal();
+                if ((frame < 0.0) || (frame > 1.0))
+                {
+                    frame = llclamp(frame, 0.0f, 1.0f);
+                    (*track)[index][LLSettingsDay::SETTING_KEYKFRAME] = frame;
+                }
+                ++index;
+            }
+
+        }
+        return true;
+    }
+}
+
+LLSettingsDay::validation_list_t LLSettingsDay::getValidationList() const
+{
+    static validation_list_t validation;
+
+    if (validation.empty())
+    {
+        validation.push_back(Validator(SETTING_TRACKS, true, LLSD::TypeArray, 
+            &validateDayCycleTrack));
+        validation.push_back(Validator(SETTING_DAYLENGTH, false, LLSD::TypeInteger,
+            boost::bind(&Validator::verifyIntegerRange, _1, 
+                LLSD(LLSDArray(LLSD::Integer(MINIMUM_DAYLENGTH))(LLSD::Integer(MAXIMUM_DAYLENGTH))))));
+    }
+
+    return validation;
+}
+
 //=========================================================================
 F32 LLSettingsDay::secondsToKeyframe(S64Seconds seconds)
 {
diff --git a/indra/newview/llsettingsdaycycle.h b/indra/newview/llsettingsdaycycle.h
index ba3d592bd68..804d7aee266 100644
--- a/indra/newview/llsettingsdaycycle.h
+++ b/indra/newview/llsettingsdaycycle.h
@@ -43,7 +43,6 @@ class LLSettingsDay : public LLSettingsBase
     static const std::string    SETTING_KEYID;
     static const std::string    SETTING_KEYNAME;
     static const std::string    SETTING_KEYKFRAME;
-    static const std::string    SETTING_NAME;
     static const std::string    SETTING_TRACKS;
 
     static const S64            MINIMUM_DAYLENGTH;
@@ -51,6 +50,7 @@ class LLSettingsDay : public LLSettingsBase
 
     static const S32            TRACK_WATER;
     static const S32            TRACK_MAX;
+    static const S32            FRAME_MAX;
 
     typedef std::map<F32, LLSettingsBase::ptr_t>    CycleTrack_t;
     typedef std::vector<CycleTrack_t>               CycleList_t;
@@ -110,6 +110,8 @@ class LLSettingsDay : public LLSettingsBase
 
     virtual void                updateSettings();
 
+    virtual validation_list_t   getValidationList() const;
+
 private:
     LLSettingsBlender::ptr_t    mSkyBlender;    // convert to [] for altitudes 
     LLSettingsBlender::ptr_t    mWaterBlender;
diff --git a/indra/newview/llsettingssky.cpp b/indra/newview/llsettingssky.cpp
index ccab35f6697..1109099ff57 100644
--- a/indra/newview/llsettingssky.cpp
+++ b/indra/newview/llsettingssky.cpp
@@ -90,7 +90,6 @@ const std::string LLSettingsSky::SETTING_LEGACY_EAST_ANGLE("east_angle");
 const std::string LLSettingsSky::SETTING_LEGACY_ENABLE_CLOUD_SCROLL("enable_cloud_scroll");
 const std::string LLSettingsSky::SETTING_LEGACY_SUN_ANGLE("sun_angle");
 
-
 //=========================================================================
 LLSettingsSky::LLSettingsSky(const LLSD &data) :
     LLSettingsBase(data)
@@ -150,6 +149,86 @@ LLSettingsSky::stringset_t LLSettingsSky::getSlerpKeys() const
     return slepSet;
 }
 
+LLSettingsSky::validation_list_t LLSettingsSky::getValidationList() const
+{
+    static validation_list_t validation;
+
+    if (validation.empty())
+    {   // Note the use of LLSD(LLSDArray()()()...) This is due to an issue with the 
+        // copy constructor for LLSDArray.  Directly binding the LLSDArray as 
+        // a parameter without first wrapping it in a pure LLSD object will result 
+        // in deeply nested arrays like this [[[[[[[[[[v1,v2,v3]]]]]]]]]]
+        
+        validation.push_back(Validator(SETTING_AMBIENT, true, LLSD::TypeArray,
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(3.0f)(3.0f)(3.0f)("*")))));
+        validation.push_back(Validator(SETTING_BLOOM_TEXTUREID,     true,  LLSD::TypeUUID));
+        validation.push_back(Validator(SETTING_BLUE_DENSITY,        true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(2.0f)(2.0f)(2.0f)("*")))));
+        validation.push_back(Validator(SETTING_BLUE_HORIZON,        true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(2.0f)(2.0f)(2.0f)("*")))));
+        validation.push_back(Validator(SETTING_CLOUD_COLOR,         true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(1.0f)(1.0f)(1.0f)("*")))));
+        validation.push_back(Validator(SETTING_CLOUD_POS_DENSITY1,  true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(1.68841f)(1.0f)(1.0f)("*")))));
+        validation.push_back(Validator(SETTING_CLOUD_POS_DENSITY2,  true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(1.68841f)(1.0f)(1.0f)("*")))));
+        validation.push_back(Validator(SETTING_CLOUD_SCALE,         true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.001f)(0.999f)))));
+        validation.push_back(Validator(SETTING_CLOUD_SCROLL_RATE,   true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)),
+                LLSD(LLSDArray(20.0f)(20.0f)))));
+        validation.push_back(Validator(SETTING_CLOUD_SHADOW,        true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_CLOUD_TEXTUREID,     false, LLSD::TypeUUID));
+        validation.push_back(Validator(SETTING_DENSITY_MULTIPLIER,  true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(0.0009f)))));
+        validation.push_back(Validator(SETTING_DISTANCE_MULTIPLIER, true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(100.0f)))));
+        validation.push_back(Validator(SETTING_DOME_OFFSET,         false, LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_DOME_RADIUS,         false, LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(1000.0f)(2000.0f)))));
+        validation.push_back(Validator(SETTING_GAMMA,               true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(10.0f)))));
+        validation.push_back(Validator(SETTING_GLOW,                true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.2f)("*")(-2.5f)("*")),
+                LLSD(LLSDArray(20.0f)("*")(0.0f)("*")))));
+        validation.push_back(Validator(SETTING_HAZE_DENSITY,        true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(4.0f)))));
+        validation.push_back(Validator(SETTING_HAZE_HORIZON,        true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_LIGHT_NORMAL,        false, LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorNormalized, _1, 3)));
+        validation.push_back(Validator(SETTING_MAX_Y,               true,  LLSD::TypeReal,  
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(4000.0f)))));
+        validation.push_back(Validator(SETTING_MOON_ROTATION,       true,  LLSD::TypeArray, &Validator::verifyQuaternionNormal));
+        validation.push_back(Validator(SETTING_MOON_TEXTUREID,      false, LLSD::TypeUUID));
+        validation.push_back(Validator(SETTING_STAR_BRIGHTNESS,     true,  LLSD::TypeReal, 
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(2.0f)))));
+        validation.push_back(Validator(SETTING_SUNLIGHT_COLOR,      true,  LLSD::TypeArray, 
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)("*")),
+                LLSD(LLSDArray(3.0f)(3.0f)(3.0f)("*")))));
+        validation.push_back(Validator(SETTING_SUN_ROTATION,        true,  LLSD::TypeArray, &Validator::verifyQuaternionNormal));
+        validation.push_back(Validator(SETTING_SUN_TEXUTUREID,      false, LLSD::TypeUUID));
+    }
+
+    return validation;
+}
 
 LLSettingsSky::ptr_t LLSettingsSky::buildFromLegacyPreset(const std::string &name, const LLSD &oldsettings)
 {
@@ -231,7 +310,7 @@ LLSettingsSky::ptr_t LLSettingsSky::buildFromLegacyPreset(const std::string &nam
     }
     if (oldsettings.has(SETTING_LIGHT_NORMAL))
     {
-        newsettings[SETTING_LIGHT_NORMAL] = LLVector4(oldsettings[SETTING_LIGHT_NORMAL]).getValue();
+        newsettings[SETTING_LIGHT_NORMAL] = LLVector3(oldsettings[SETTING_LIGHT_NORMAL]).getValue();
     }
     if (oldsettings.has(SETTING_MAX_Y))
     {
@@ -246,18 +325,6 @@ LLSettingsSky::ptr_t LLSettingsSky::buildFromLegacyPreset(const std::string &nam
         newsettings[SETTING_SUNLIGHT_COLOR] = LLColor4(oldsettings[SETTING_SUNLIGHT_COLOR]).getValue();
     }
 
-
-//     dfltsetting[SETTING_DOME_OFFSET] = LLSD::Real(0.96f);
-//     dfltsetting[SETTING_DOME_RADIUS] = LLSD::Real(15000.f);
-// 
-//     dfltsetting[SETTING_MOON_ROTATION] = moonquat.getValue();
-//     dfltsetting[SETTING_SUN_ROTATION] = sunquat.getValue();
-// 
-//     dfltsetting[SETTING_BLOOM_TEXTUREID] = LLUUID::null;
-//     dfltsetting[SETTING_CLOUD_TEXTUREID] = LLUUID::null;
-//     dfltsetting[SETTING_MOON_TEXTUREID] = IMG_SUN; // gMoonTextureID;   // These two are returned by the login... wow!
-//     dfltsetting[SETTING_SUN_TEXUTUREID] = IMG_MOON; // gSunTextureID;
-
     if (oldsettings.has(SETTING_LEGACY_EAST_ANGLE) && oldsettings.has(SETTING_LEGACY_SUN_ANGLE))
     {   // convert the east and sun angles into a quaternion.
         F32 azimuth = oldsettings[SETTING_LEGACY_EAST_ANGLE].asReal();
@@ -275,7 +342,10 @@ LLSettingsSky::ptr_t LLSettingsSky::buildFromLegacyPreset(const std::string &nam
 
     LLSettingsSky::ptr_t skyp = boost::make_shared<LLSettingsSky>(newsettings);
  
-    return skyp;    
+    if (skyp->validate())
+        return skyp;
+
+    return LLSettingsSky::ptr_t();
 }
 
 LLSettingsSky::ptr_t LLSettingsSky::buildDefaultSky()
@@ -283,8 +353,10 @@ LLSettingsSky::ptr_t LLSettingsSky::buildDefaultSky()
     LLSD settings = LLSettingsSky::defaults();
 
     LLSettingsSky::ptr_t skyp = boost::make_shared<LLSettingsSky>(settings);
+    if (skyp->validate())
+        return skyp;
 
-    return skyp;
+    return LLSettingsSky::ptr_t();
 }
 
 LLSettingsSky::ptr_t LLSettingsSky::buildClone()
@@ -293,23 +365,11 @@ LLSettingsSky::ptr_t LLSettingsSky::buildClone()
 
     LLSettingsSky::ptr_t skyp = boost::make_shared<LLSettingsSky>(settings);
 
-    return skyp;
-}
+    if (skyp->validate())
+        return skyp;
 
-// Settings status 
-
-// LLSettingsSky::ptr_t LLSettingsSky::blend(const LLSettingsSky::ptr_t &other, F32 mix) const
-// {
-//     LL_RECORD_BLOCK_TIME(FTM_BLEND_SKYVALUES);
-//     LL_INFOS("WINDLIGHT", "SKY", "EEP") << "Blending new sky settings object." << LL_ENDL;
-// 
-//     LLSettingsSky::ptr_t skyp = boost::make_shared<LLSettingsSky>(mSettings);
-//     // the settings in the initial constructor are references to this' settings block.  
-//     // They will be replaced in the following lerp
-//     skyp->lerpSettings(*other, mix);
-// 
-//     return skyp;
-// }
+    return LLSettingsSky::ptr_t();
+}
 
 
 LLSD LLSettingsSky::defaults()
@@ -339,7 +399,7 @@ LLSD LLSettingsSky::defaults()
     dfltsetting[SETTING_GLOW]               = LLColor4(5.000, 0.0010, -0.4799, 1.0).getValue();
     dfltsetting[SETTING_HAZE_DENSITY]       = LLSD::Real(0.6999);
     dfltsetting[SETTING_HAZE_HORIZON]       = LLSD::Real(0.1899);
-    dfltsetting[SETTING_LIGHT_NORMAL]       = LLVector4(0.0000, 0.9126, -0.4086, 0.0000).getValue();
+    dfltsetting[SETTING_LIGHT_NORMAL]       = LLVector3(0.0000, 0.9126, -0.4086).getValue();
     dfltsetting[SETTING_MAX_Y]              = LLSD::Real(1605);
     dfltsetting[SETTING_MOON_ROTATION]      = moonquat.getValue();
     dfltsetting[SETTING_NAME]               = std::string("_default_");
diff --git a/indra/newview/llsettingssky.h b/indra/newview/llsettingssky.h
index ade5a065537..8efadf44b04 100644
--- a/indra/newview/llsettingssky.h
+++ b/indra/newview/llsettingssky.h
@@ -416,6 +416,9 @@ class LLSettingsSky: public LLSettingsBase
 
     virtual stringset_t getSlerpKeys() const;
 
+    virtual validation_list_t getValidationList() const;
+
+
     virtual void        updateSettings();
 
     virtual parammapping_t getParameterMap() const;
diff --git a/indra/newview/llsettingswater.cpp b/indra/newview/llsettingswater.cpp
index 70688ee1ef0..dda92659039 100644
--- a/indra/newview/llsettingswater.cpp
+++ b/indra/newview/llsettingswater.cpp
@@ -114,7 +114,6 @@ LLSD LLSettingsWater::defaults()
     return dfltsetting;
 }
 
-
 LLSettingsWater::ptr_t LLSettingsWater::buildFromLegacyPreset(const std::string &name, const LLSD &oldsettings)
 {
     LLSD newsettings(defaults());
@@ -173,25 +172,34 @@ LLSettingsWater::ptr_t LLSettingsWater::buildFromLegacyPreset(const std::string
 
     LLSettingsWater::ptr_t waterp = boost::make_shared<LLSettingsWater>(newsettings);
 
-    return waterp;
+    if (waterp->validate())
+        return waterp;
+
+    return LLSettingsWater::ptr_t();
 }
 
 LLSettingsWater::ptr_t LLSettingsWater::buildDefaultWater()
 {
     LLSD settings = LLSettingsWater::defaults();
 
-    LLSettingsWater::ptr_t skyp = boost::make_shared<LLSettingsWater>(settings);
+    LLSettingsWater::ptr_t waterp = boost::make_shared<LLSettingsWater>(settings);
 
-    return skyp;
+    if (waterp->validate())
+        return waterp;
+
+    return LLSettingsWater::ptr_t();
 }
 
 LLSettingsWater::ptr_t LLSettingsWater::buildClone()
 {
     LLSD settings = cloneSettings();
 
-    LLSettingsWater::ptr_t skyp = boost::make_shared<LLSettingsWater>(settings);
+    LLSettingsWater::ptr_t waterp = boost::make_shared<LLSettingsWater>(settings);
+
+    if (waterp->validate())
+        return waterp;
 
-    return skyp;
+    return LLSettingsWater::ptr_t();
 }
 
 void LLSettingsWater::blend(const LLSettingsBase::ptr_t &end, F32 blendf) 
@@ -202,6 +210,51 @@ void LLSettingsWater::blend(const LLSettingsBase::ptr_t &end, F32 blendf)
     replaceSettings(blenddata);
 }
 
+LLSettingsWater::validation_list_t LLSettingsWater::getValidationList() const
+{
+    static validation_list_t validation;
+
+    if (validation.empty())
+    {   // Note the use of LLSD(LLSDArray()()()...) This is due to an issue with the 
+        // copy constructor for LLSDArray.  Directly binding the LLSDArray as 
+        // a parameter without first wrapping it in a pure LLSD object will result 
+        // in deeply nested arrays like this [[[[[[[[[[v1,v2,v3]]]]]]]]]]
+
+        validation.push_back(Validator(SETTING_BLUR_MULTIPILER, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(0.16f)))));
+        validation.push_back(Validator(SETTING_FOG_COLOR, true, LLSD::TypeArray,
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)(1.0f)),
+                LLSD(LLSDArray(1.0f)(1.0f)(1.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_FOG_DENSITY, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(1.0f)(1024.0f)))));
+        validation.push_back(Validator(SETTING_FOG_MOD, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(1.0f)(1024.0f)))));
+        validation.push_back(Validator(SETTING_FRESNEL_OFFSET, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_FRESNEL_SCALE, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_NORMAL_MAP, true, LLSD::TypeUUID));
+        validation.push_back(Validator(SETTING_NORMAL_SCALE, true, LLSD::TypeArray,
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(0.0f)(0.0f)(0.0f)),
+                LLSD(LLSDArray(10.0f)(10.0f)(10.0f)))));
+        validation.push_back(Validator(SETTING_SCALE_ABOVE, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_SCALE_BELOW, true, LLSD::TypeReal,
+            boost::bind(&Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(1.0f)))));
+        validation.push_back(Validator(SETTING_WAVE1_DIR, true, LLSD::TypeArray,
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(-4.0f)(-4.0f)),
+                LLSD(LLSDArray(4.0f)(4.0f)))));
+        validation.push_back(Validator(SETTING_WAVE2_DIR, true, LLSD::TypeArray,
+            boost::bind(&Validator::verifyVectorMinMax, _1,
+                LLSD(LLSDArray(-4.0f)(-4.0f)),
+                LLSD(LLSDArray(4.0f)(4.0f)))));
+    }
+
+    return validation;
+}
 
 //=========================================================================
 
diff --git a/indra/newview/llsettingswater.h b/indra/newview/llsettingswater.h
index 1550ba4004d..a62fa355dee 100644
--- a/indra/newview/llsettingswater.h
+++ b/indra/newview/llsettingswater.h
@@ -209,6 +209,7 @@ class LLSettingsWater : public LLSettingsBase
 
     virtual void        applySpecial(void *);
 
+    virtual validation_list_t getValidationList() const;
 
 private:
     static const std::string SETTING_LEGACY_BLUR_MULTIPILER;
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index 0c211b259c3..2ab273df911 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -833,7 +833,7 @@ void LLVOSky::calcAtmospherics(void)
     LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
 
     mSun.setColor(psky->getSunlightColor());
-	mMoon.setColor(LLColor3(2.0f, 2.0f, 2.0f));
+	mMoon.setColor(LLColor3(1.0f, 1.0f, 1.0f));
 
 	mSun.renewDirection();
 	mSun.renewColor();
-- 
GitLab