Beispiel #1
0
        private DataTreeObjectProperty[] MakeModelTransformPair(ConfigReference model, Transform3D transform, BooleanExpression expr = null)
        {
            string mdlName = model?.getName() ?? "null";
            string trs     = transform?.toString() ?? "null";

            if (expr != null)
            {
                string state = "???";
                try {
                    state = expr.createEvaluator(new DummyScope()).evaluate().ToString();
                } catch { }
                return(new DataTreeObjectProperty[] {
                    new DataTreeObjectProperty("Condition: " + expr.toString() + " = " + state, SilkImage.Conditional),
                    new DataTreeObjectProperty("Model: " + mdlName, SilkImage.Reference),
                    new DataTreeObjectProperty("Transform: " + trs, SilkImage.Matrix)
                });
            }
            else
            {
                return(new DataTreeObjectProperty[] {
                    new DataTreeObjectProperty("Model: " + mdlName, SilkImage.Reference),
                    new DataTreeObjectProperty("Transform: " + trs, SilkImage.Matrix)
                });
            }
        }
Beispiel #2
0
        /// <summary>
        /// Takes in a <see cref="ConfigReference"/> and loads its data. It then returns the loaded model and all of its descendants as a list of <see cref="Model3D"/> instances.
        /// </summary>
        /// <param name="sourceFile">The original base-level file that contains the reference.</param>
        /// <param name="reference">The reference itself.</param>
        /// <param name="modelCollection">A list of every model that has been loaded recursively.</param>
        /// <param name="dataTreeParent">For cases where the GUI is used, this is the data tree representation.</param>
        /// <param name="globalTransform">The transformation to apply to all loaded models.</param>
        /// <param name="appendModelsToModelCollection">If true, the loaded models will be appended to <paramref name="modelCollection"/>.</param>
        /// <param name="extraData">Any extra data that should be included. This is mainly used by references (e.g. a reference is a <see cref="StaticSetConfig"/>, the target model in the set may be included as extra data)</param>
        public static List <Model3D> HandleConfigReference(FileInfo sourceFile, ConfigReference reference, List <Model3D> modelCollection, DataTreeObject dataTreeParent, Transform3D globalTransform, bool appendModelsToModelCollection = true, Dictionary <string, dynamic> extraData = null)
        {
            if (reference == null)
            {
                return(null);
            }
            string filePathRelativeToRsrc = reference.getName();

            if (extraData == null)
            {
                extraData = reference.ArgumentsToExtraData();
            }
            else
            {
                extraData = extraData.MergeWith(reference.ArgumentsToExtraData());
            }
            return(HandleConfigReferenceFromLiteralPath(sourceFile, filePathRelativeToRsrc, modelCollection, dataTreeParent, globalTransform, appendModelsToModelCollection, extraData));
        }
Beispiel #3
0
        /// <summary>
        /// Given a <see cref="ParameterizedConfig"/> and a path from a direct as well as a target value, this will modify the <see cref="ParameterizedConfig"/> so that its fields reflect the given value.<para/>
        /// If this data cannot be set due to it being on a direct chain (the end value is a <see cref="ConfigReference"/>), that <see cref="ConfigReference"/> will be returned
        /// </summary>
        /// <param name="config"></param>
        /// <param name="path"></param>
        /// <param name="argValue"></param>
        /// <param name="setToNull">If true, and if argValue is null, the property will actually be set to null (if this is false, it will skip applying it)</param>
        public static ConfigReference SetDataOn(ParameterizedConfig config, string path, object argValue, bool setToNull = false)
        {
            if (argValue == null && !setToNull)
            {
                return(null);
            }
            // implementation.material_mappings[0].material["Texture"]["File"]

            // A bit of a hack to make splitting this path easier:
            path = path.Replace("[", ".[");

            // The latest object stored when traversing this direct's path.
            object latestObject   = config;
            string previousIndex  = null;
            object previousObject = null;

            // Split it by the segments of this path, and get rid of the implementation word at the start if needed.
            string[] pathSegments = path.Split('.');
            if (pathSegments[0] == "implementation")
            {
                latestObject = ReflectionHelper.Get(latestObject, "implementation");
                pathSegments = pathSegments.Skip(1).ToArray();
            }

            for (int idx = 0; idx < pathSegments.Length; idx++)
            {
                string currentIndex    = pathSegments[idx].SnakeToCamel();
                string betweenBrackets = currentIndex.BetweenBrackets();
                if (betweenBrackets != null)
                {
                    // This is either an array index, or a reference to a config.
                    // The simple way to test this is that if it's a numeric index, it's an array index.
                    if (int.TryParse(betweenBrackets, out int arrayIndex))
                    {
                        // Access this array index. It is a number in brackets like [0]
                        previousObject = latestObject;
                        latestObject   = ReflectionHelper.GetArray(latestObject, arrayIndex);
                    }
                    else
                    {
                        // Access the config reference. This is branching from a config reference and accesses a parameter ["Parameter Name"]
                        ConfigReference latestAsCfg   = (ConfigReference)latestObject;
                        string          parameterName = betweenBrackets.Substring(1, betweenBrackets.Length - 2);

                        // First things first: Resolve the config reference.
                        string configRefPath = latestAsCfg.getName();

                        // cloning this is super important as the tryget method will return a template object.
                        // Do not edit the template!
                        if (!latestAsCfg.IsRealReference())
                        {
                            // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference.
                            latestAsCfg.getArguments().put(parameterName, argValue);
                            return(null);
                        }
                        ParameterizedConfig referencedConfig = ConfigReferenceBootstrapper.ConfigReferences.TryGetReferenceFromName(configRefPath)?.CloneAs <ParameterizedConfig>();
                        if (referencedConfig == null)
                        {
                            XanLogger.WriteLine("Something failed to reference a ConfigReference (It tried to search for \"" + configRefPath + "\", which doesn't exist). Some information on this model may not load properly!", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                            return(null);
                        }

                        // So there's our reference. Now we need to get a parameter from it.
                        Parameter referencedParam = referencedConfig.getParameter(parameterName);
                        if (referencedParam is Parameter.Direct referencedDirect)
                        {
                            ConfigReference[] chainRefs = SetDataOn(referencedConfig, referencedDirect.paths, argValue);
                            if (chainRefs != null)
                            {
                                // We're pointing to other ConfigReferences which means that this is a direct chain. Oh brother.
                                foreach (ConfigReference reference in chainRefs)
                                {
                                    if (reference != null)
                                    {
                                        if (File.Exists(ResourceDirectoryGrabber.ResourceDirectoryPath + configRefPath))
                                        {
                                            // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference.
                                            latestAsCfg.getArguments().put(parameterName, argValue);
                                        }
                                        else
                                        {
                                            ParameterizedConfig forwardRefConfig = reference.Resolve <ParameterizedConfig>();
                                            // Using as because it might be null.
                                            if (forwardRefConfig != null)
                                            {
                                                foreach (Parameter subRefParam in forwardRefConfig.parameters)
                                                {
                                                    if (subRefParam is Parameter.Direct subRefDirect)
                                                    {
                                                        WrappedDirect wrappedSubRefDir = new WrappedDirect(forwardRefConfig, subRefDirect);
                                                        wrappedSubRefDir.SetValue(argValue);
                                                    }
                                                }

                                                latestAsCfg.getArguments().put(parameterName, ConfigReferenceConstructor.MakeConfigReferenceTo(forwardRefConfig));
                                            }
                                            else
                                            {
                                                XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed because the target object was not a ParameterizedConfig! Some information may be incorrect on this model.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                                                return(null);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // This is by far one of the most hacky methods I've ever done in OOO stuff.
                                // So basically, a model has a property for something like say, materials.
                                // This property is a ConfigReference to a material object, and that ConfigReference has arguments in it
                                //    that tell the referenced material what it should be.
                                // Rather than trying to traverse that ConfigReference and set the data on the remote object (PAINFUL), I
                                //    instead decided to write a system that can wrap any ParameterizedConfig into a ConfigReference and just
                                //    call it a day.
                                ReflectionHelper.Set(previousObject, previousIndex, ConfigReferenceConstructor.MakeConfigReferenceTo(referencedConfig));
                            }
                        }
                        else
                        {
                            //throw new NotImplementedException("Cannot set data on referenced parameters that are not Directs (yet).");
                            XanLogger.WriteLine("Feature Not Implemented: Cannot set data on referenced parameters that aren't directs (e.g. parameters that are choices)", XanLogger.STANDARD, System.Drawing.Color.Orange);
                            return(null);
                        }
                        return(null);
                    }
                }
                else
                {
                    // This is referencing a property.
                    // But wait: If this is the second to last object, then we gotta modify it.
                    if (idx == pathSegments.Length - 1)
                    {
                        // Second to last object. latestObject will contain the property that we want to set.
                        // Let's manually find that field and set it
                        if (currentIndex.BetweenBrackets() == null)
                        {
                            // We're good here.
                            if (argValue is ConfigReference argValueCfg)
                            {
                                // There's some cases when a variant wants to set a config reference.
                                // In these cases, we need to make sure the property is also a config reference so we know it's safe to set.
                                // ... But before that, catch case: Not actually a config.
                                if (argValueCfg.IsRealReference())
                                {
                                    object ptr = ReflectionHelper.Get(previousObject, previousIndex);
                                    if (ptr is ConfigReference)
                                    {
                                        ReflectionHelper.Set(previousObject, previousIndex, argValueCfg);
                                    }
                                    else
                                    {
                                        if (ReflectionHelper.GetTypeOfField(latestObject, currentIndex) == argValueCfg.GetType())
                                        {
                                            ReflectionHelper.Set(latestObject, currentIndex, argValueCfg);
                                            return(null);
                                        }
                                        else
                                        {
                                            XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                                            return(null);
                                        }
                                    }
                                }
                                else
                                {
                                    if (ReflectionHelper.GetTypeOfField(latestObject, currentIndex) == argValueCfg.GetType())
                                    {
                                        ReflectionHelper.Set(latestObject, currentIndex, argValueCfg);
                                        return(null);
                                    }
                                    else
                                    {
                                        XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                                        return(null);
                                    }
                                }
                            }
                            else
                            {
                                // Contrary to the oddball case above, if the result value at the end of this direct is a ConfigReference
                                // then we need to return it so that whatever called this knows that it has more to traverse.
                                // Ideally, this is only returned in the nested call above.
                                string targetIndex = previousIndex;

                                /*if (int.TryParse(previousIndex.BetweenBrackets(), out int _)) {
                                 *      // The previous index was an array accessor. This means we want to actually reference the CURRENT index
                                 *      // on the PREVIOUS object. A bit odd but it's intentional.
                                 *      // This is done because the previous object will be the result of that indexer, which is identical
                                 *      // to the current object. As such, we need to use the current index to reference it.
                                 *      targetIndex = currentIndex;
                                 * }*/
                                if (previousObject == null || targetIndex == null)
                                {
                                    return(null);
                                }
                                object ptr = ReflectionHelper.Get(previousObject, targetIndex);
                                if (ptr is ConfigReference cfgRef)
                                {
                                    // Special handling. argValue goes to a property on the config reference
                                    return(cfgRef);
                                }

                                if (ptr.GetType() == argValue.GetType())
                                {
                                    ReflectionHelper.Set(previousObject, targetIndex, argValue);
                                }
                                else
                                {
                                    // In some cases, the object it's pointing to isn't the same time.
                                    // In cases where the previous index is used, this *might* mean we need to step forward like so:
                                    if (ReflectionHelper.GetTypeOfField(ptr, currentIndex) == argValue.GetType())
                                    {
                                        ReflectionHelper.Set(ptr, currentIndex, argValue);
                                    }
                                    else
                                    {
                                        // But in other cases, it's not pointing to a prop up ahead.
                                        if (ReflectionHelper.Get(ptr, currentIndex) is ConfigReference cfgRefLow)
                                        {
                                            return(cfgRefLow);
                                        }
                                        else
                                        {
                                            XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                                            return(null);
                                        }
                                    }
                                }
                                return(null);
                            }
                        }
                    }
                    if (previousIndex != null)
                    {
                        if (int.TryParse(previousIndex.BetweenBrackets(), out int _) && idx == pathSegments.Length - 1)
                        {
                            if (currentIndex.BetweenBrackets() == null)
                            {
                                // We're good here.
                                ReflectionHelper.Set(previousObject, previousIndex, argValue);
                                return(null);
                            }
                        }
                    }
                    previousObject = latestObject;
                    latestObject   = ReflectionHelper.Get(latestObject, currentIndex);
                    if (previousObject == null || latestObject == null)
                    {
                        return(null);                                                                    // Failed to traverse.
                    }
                }
                previousIndex = currentIndex;
            }
            return(null);
        }
Beispiel #4
0
        /// <summary>
        /// Given a full path from a <see cref="Parameter.Direct"/>, this will traverse it and acquire the data at the end.<para/>
        /// This will stop if it runs into another direct and instantiate a new <see cref="WrappedDirect"/>. This will occur if there is a reference chain, for instance, in many textures it references material["Texture"] (a direct) followed by a second direct ["File"]. Since each may have multiple paths, it's best to reference a new <see cref="WrappedDirect"/>.
        /// </summary>
        /// <param name="path"></param>
        private void TraverseDirectPath(string path)
        {
            // implementation.material_mappings[0].material["Texture"]["File"]

            // A bit of a hack to make splitting this path easier:
            path = path.Replace("[", ".[");

            // The latest object stored when traversing this direct's path.
            object latestObject = Config;

            // Split it by the segments of this path, and get rid of the implementation word at the start if needed.
            string[] pathSegments = path.Split('.');
            if (pathSegments[0] == "implementation")
            {
                latestObject = ReflectionHelper.Get(latestObject, "implementation");
                pathSegments = pathSegments.Skip(1).ToArray();
            }

            for (int idx = 0; idx < pathSegments.Length; idx++)
            {
                string currentIndex    = pathSegments[idx].SnakeToCamel();
                string betweenBrackets = currentIndex.BetweenBrackets();
                if (betweenBrackets != null)
                {
                    // This is either an array index, or a reference to a config.
                    // The simple way to test this is that if it's a numeric index, it's an array index.
                    if (int.TryParse(betweenBrackets, out int arrayIndex))
                    {
                        // Access this array index. It is a number in brackets like [0]
                        latestObject = ReflectionHelper.GetArray(latestObject, arrayIndex);
                    }
                    else
                    {
                        // Access the config reference. This is branching from a config reference and accesses a parameter ["Parameter Name"]
                        ConfigReference latestAsCfg   = (ConfigReference)latestObject;
                        string          parameterName = betweenBrackets.Substring(1, betweenBrackets.Length - 2);

                        // First things first: Resolve the config reference (AND CLONE IT. Don't edit the template object!)
                        string configRefPath = latestAsCfg.getName();

                        if (!latestAsCfg.IsRealReference())
                        {
                            // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference.
                            _EndReferences.Add(new DirectEndReference(configRefPath));
                            return;
                        }

                        ParameterizedConfig referencedConfig = ConfigReferenceBootstrapper.ConfigReferences.TryGetReferenceFromName(configRefPath)?.CloneAs <ParameterizedConfig>();
                        if (referencedConfig == null)
                        {
                            XanLogger.WriteLine("Something failed to reference a ConfigReference (It tried to search for \"" + configRefPath + "\", which doesn't exist). Some information on this model may not load properly!", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod);
                            return;
                        }

                        ArgumentMap args = latestAsCfg.getArguments();

                        // So there's our reference. Now we need to get a parameter from it.
                        ConfigReferenceContainerName = ConfigReferenceBootstrapper.ConfigReferences.GetCategoryFromEntryName(configRefPath);
                        Parameter referencedParam = referencedConfig.getParameter(parameterName);
                        if (referencedParam is Parameter.Direct referencedDirect)
                        {
                            _EndReferences.Add(new DirectEndReference(new WrappedDirect(referencedConfig, referencedDirect, null, args)));
                        }
                        else if (referencedParam is Parameter.Choice referencedChoice)
                        {
                            _EndReferences.Add(new DirectEndReference(new WrappedChoice(referencedConfig, referencedChoice, args)));
                        }
                        return;
                    }
                }
                else
                {
                    // This is referencing a property.
                    latestObject = ReflectionHelper.Get(latestObject, currentIndex);
                }
            }

            // Now here's something important: Does an argument override this?
            if (Arguments != null && Arguments.containsKey(Name) && latestObject != null)
            {
                // This direct is included as an argument...
                // And if we're down here, then we're not referencing another direct, we're referencing a property.
                // But as a final sanity check:
                if (latestObject.GetType() == Arguments.get(Name)?.GetType())
                {
                    // Yep! Our argument is the same type as the latestObject.
                    // Let's set latestObject to that argument.
                    latestObject = Arguments.get(Name);
                }
            }
            _EndReferences.Add(new DirectEndReference(latestObject));
        }
Beispiel #5
0
        /// <summary>
        /// Attempts to resolve this <see cref="ConfigReference"/>, which is expected to point to a file, and returns the Clyde object that it points to.<para/>
        /// This is intended for use in cases where data must absolutely be loaded in-line. Most cases are better suited for <see cref="ConfigReferenceUtil"/> and its methods.
        /// </summary>
        /// <param name="cfgRef"></param>
        /// <returns></returns>
        public static object ResolveFile(this ConfigReference cfgRef)
        {
            if (!cfgRef.IsFileReference())
            {
                throw new InvalidOperationException("Cannot resolve this ConfigReference as a file because the file it points to does not exist (or it references an actual config object)!");
            }
            object clydeObject = ClydeFileHandler.GetRaw(new FileInfo(ResourceDirectoryGrabber.ResourceDirectoryPath + cfgRef.getName()));

            if (clydeObject == null)
            {
                throw new NullReferenceException("Failed to load Clyde file!");
            }

            // Apply any arguments.
            if (clydeObject is ParameterizedConfig paramCfg)
            {
                paramCfg.ApplyArguments(cfgRef.getArguments());
                return(paramCfg);
            }
            return(clydeObject);
        }
Beispiel #6
0
 /// <summary>
 /// Returns <see langword="true"/> if this <see cref="ConfigReference"/> points to an actual config, and <see langword="false"/> if it does not (for instance, it points to a model file instead).
 /// </summary>
 /// <param name="cfgRef"></param>
 /// <returns>False if this ConfigReference points to a literal file (points to a .dat file in the rsrc directory), and true if it points to an actual configuration object (e.g. Texture/2D/Default for textures)</returns>
 public static bool IsRealReference(this ConfigReference cfgRef)
 {
     return(!File.Exists(ResourceDirectoryGrabber.ResourceDirectoryPath + cfgRef.getName()));
 }
Beispiel #7
0
        /// <summary>
        /// Using the information in this <see cref="ConfigReference"/>, the object this reference points to will be resolved and returned. This <see cref="ConfigReference"/> must point to an actual configuration. If it points to a file, an <see cref="InvalidOperationException"/> will be thrown.<para/>
        /// This will automatically populate the arguments in the referenced config if applicable, making usage of the returned object relatively straightforward.
        /// </summary>
        /// <param name="cfgRef">The ConfigReference to resolve the reference to.</param>
        /// <typeparam name="T">The destination type for the new ManagedConfig</typeparam>
        /// <returns>The ManagedConfig the given ConfigReference is pointing to.</returns>
        /// <exception cref="InvalidOperationException">If IsFileReference() returns true on this ConfigReference.</exception>
        public static T Resolve <T>(this ConfigReference cfgRef) where T : ManagedConfig
        {
            if (cfgRef.IsFileReference())
            {
                throw new InvalidOperationException("Cannot resolve the path to a non-config reference (this ConfigReference points to a file!)");
            }
            T mgCfg = ConfigReferenceBootstrapper.ConfigReferences.TryGetReferenceFromName(cfgRef.getName())?.CloneAs <T>();

            // Populate the values of the referenced object if possible.
            if (mgCfg is ParameterizedConfig paramCfg)
            {
                // This can store arguments.
                paramCfg.ApplyArguments(cfgRef.getArguments());
                return(paramCfg as T);
            }
            // It's gotta stay as a ManagedConfig
            // Can't make use of this to apply arguments to. How do I handle this?
            // For now: Return the object without any changes.
            return(mgCfg);
        }