示例#1
0
        async public Task <Dictionary <string, object> > IsPatchApplicable(JObject data,
                                                                           CoreDelegates coreDelegates)
        {
            string      message;
            bool        result   = false;
            PatchConfig config   = new PatchConfig((JObject)data ["patchConfig"]);
            string      dataPath = await coreDelegates.context.GetDataPath();

            try {
                if (IsReflectionEnabled(dataPath))
                {
                    // Reflection already enabled - no need to do anything.
                    message = "Reflection is enabled";
                    result  = false;
                }
                else
                {
                    message = "Can be applied";
                    result  = true;
                }
            } catch (Exception exc) {
                result  = false;
                message = exc.Message;
            }
            var addendum = new Dictionary <string, object> ()
            {
                { "Source", config.SourceEntryPoint.ToString() },
                { "Targets", config.TargetEntryPoints.ToString() }
            };

            return(PatchHelper.CreatePatchResult(result, message, addendum));
        }
        public void SerializeFileTest()
        {
            // Serialize

            var a    = new PatchConfig("Test");
            var json = SerializationHelper.SerializeToJson(a, true);

            Assert.IsNotNull(json);

            // Deserialize

            var file = Path.GetTempFileName();

            File.WriteAllText(file, json);
            var b = SerializationHelper.DeserializeFromFile <PatchConfig>(file).FirstOrDefault();

            File.Delete(file);

            Assert.IsTrue(a.Equals(b));

            // Deserialize wrong value

            file = Path.GetTempFileName();
            File.WriteAllText(file, "null");
            b = SerializationHelper.DeserializeFromFile <PatchConfig>(file).FirstOrDefault();
            File.Delete(file);

            Assert.IsNull(b);
        }
示例#3
0
        public async Task<Dictionary<string, object>> IsPatchApplicable (JObject data,
                                                                   CoreDelegates core)
        {
            PatchConfig config = new PatchConfig ((JObject)data ["patchConfig"]);
            Dictionary<string, object> res = await ReflectionPatch.Instance.IsPatchApplicable (data, core);
            if (res ["Result"].ToString () == "false")
                return res;

            return await Injector.Instance.IsPatchApplicable (data, (ProgressDelegate)((int progress) => { }), core);
        }
示例#4
0
        /// <summary>
        /// Apply patch
        /// </summary>
        /// <param name="data">Data</param>
        /// <param name="logs">Logs</param>
        /// <returns>Data result</returns>
        private static byte[] Apply(byte[] data, PatchChange[] logs)
        {
            var cfg = new PatchConfig("", logs);

            using (var mem = new MemoryStream())
                using (var stream = new FuzzingStream(cfg, data))
                {
                    stream.CopyTo(mem, 1024);
                    return(mem.ToArray());
                }
        }
示例#5
0
        public void Equals()
        {
            var cfg  = new PatchConfig();
            var stat = new FuzzerStat <PatchConfig>(cfg);
            var copy = new FuzzerStat <PatchConfig>(stat.Source);

            Assert.IsTrue(stat.Equals(copy));
            Assert.IsTrue(stat.Equals((object)copy));
            Assert.IsFalse(stat.Equals(new object()));
            Assert.AreEqual(stat.GetHashCode(), copy.GetHashCode());

            stat.Crashes++;

            Assert.AreNotEqual(stat.GetHashCode(), copy.GetHashCode());
        }
示例#6
0
        async public Task <Dictionary <string, object> > ApplyPatch(JObject data,
                                                                    ProgressDelegate progress,
                                                                    CoreDelegates coreDelegates)
        {
            PatchConfig config   = new PatchConfig((JObject)data ["patchConfig"]);
            string      dataPath = await coreDelegates.context.GetDataPath();

            string modLoaderPath = await coreDelegates.context.GetModLoaderPath();

            // We know this is a dictionary, but I'm too lazy to type.
            var result = await EnableReflection(dataPath, modLoaderPath);

            result.Add("Source", config.SourceEntryPoint.ToString());
            result.Add("Targets", config.TargetEntryPoints.ToString());
            return(result);
        }
示例#7
0
        /// <summary>
        /// Add config
        /// </summary>
        /// <param name="file">File</param>
        public void AddConfig(string file)
        {
            if (string.IsNullOrEmpty(file))
            {
                return;
            }

            if (_Invoker != null && _Invoker.InvokeRequired)
            {
                _Invoker.Invoke(new delAddCOnfig(AddConfig), new object[] { file });
                return;
            }

            if (!File.Exists(file))
            {
                return;
            }

            switch (Path.GetExtension(file).ToLowerInvariant())
            {
            case ".fmut":
            {
                MutationConfig c = null;
                try { c = MutationConfig.FromJson(File.ReadAllText(file, Encoding.UTF8)); } catch { }
                if (c != null)
                {
                    lock (Configurations) Configurations.Add(new FuzzerStat <IFuzzingConfig>(c));
                }
                break;
            }

            case ".fpatch":
            {
                PatchConfig c = null;
                try { c = PatchConfig.FromJson(File.ReadAllText(file, Encoding.UTF8)); } catch { }
                if (c != null)
                {
                    lock (Configurations) Configurations.Add(new FuzzerStat <IFuzzingConfig>(c));
                }
                break;
            }
            }
        }
示例#8
0
        /// <summary>
        /// Saves the configs using to the Blizzard standard location
        /// </summary>
        /// <param name="directory"></param>
        public void Save(string directory)
        {
            // save and update patch config value
            if (PatchConfig != null)
            {
                PatchConfig?.Write(directory);
                BuildConfig?.SetValue("patch-config", PatchConfig.Checksum.ToString());
            }

            // save the localised configs
            BuildConfig?.Write(directory);
            CDNConfig?.Write(directory);

            // update the hashes
            VersionsFile.SetValue("buildconfig", BuildConfig.Checksum.ToString());
            VersionsFile.SetValue("cdnconfig", CDNConfig.Checksum.ToString());

            // save the primary configs
            CDNsFile.Write(directory, Product);
            VersionsFile.Write(directory, Product);
        }
示例#9
0
        public void LoadFile(string file)
        {
            _LastFile = null;
            switch (Path.GetExtension(file).ToLowerInvariant())
            {
            case ".fmut":
            {
                LoadConfig(MutationConfig.FromJson(File.ReadAllText(file, Encoding.UTF8)));
                _LastFile = file;
                break;
            }

            case ".fpatch":
            {
                LoadConfig(PatchConfig.FromJson(File.ReadAllText(file, Encoding.UTF8)));
                _LastFile = file;
                break;
            }

            default: LoadConfig(null); break;
            }
        }
示例#10
0
        public void IncrementTest()
        {
            var cfg  = new PatchConfig();
            var stat = new FuzzerStat <PatchConfig>(cfg);

            Assert.AreEqual(cfg, stat.Source);
            Assert.AreEqual(cfg.Description, stat.Description);
            Assert.AreEqual(cfg.Id, stat.Id);

            Assert.AreEqual(0, stat.Crashes);
            Assert.AreEqual(0, stat.Tests);
            Assert.AreEqual(0, stat.Errors);

            stat.Increment();

            Assert.AreEqual(0, stat.Crashes);
            Assert.AreEqual(1, stat.Tests);
            Assert.AreEqual(0, stat.Errors);

            stat.Increment(FuzzerError.EFuzzingErrorType.Crash);

            Assert.AreEqual(1, stat.Crashes);
            Assert.AreEqual(2, stat.Tests);
            Assert.AreEqual(0, stat.Errors);

            stat.Increment(FuzzerError.EFuzzingErrorType.Fail);

            Assert.AreEqual(1, stat.Crashes);
            Assert.AreEqual(3, stat.Tests);
            Assert.AreEqual(1, stat.Errors);

            stat.Reset();

            Assert.AreEqual(0, stat.Crashes);
            Assert.AreEqual(0, stat.Tests);
            Assert.AreEqual(0, stat.Errors);
        }
        public void SerializeArrayFileTest()
        {
            // Serialize array

            var a    = new PatchConfig[] { new PatchConfig("Test") };
            var json = SerializationHelper.SerializeToJson(a, true);

            Assert.IsNotNull(json);

            // Deserialize

            var file = Path.GetTempFileName();

            File.WriteAllText(file, json);
            var b = SerializationHelper.DeserializeFromFile <PatchConfig>(file).ToArray();

            File.Delete(file);

            CollectionAssert.AreEqual(a, b);

            // Serialize empty array

            a    = new PatchConfig[] { };
            json = SerializationHelper.SerializeToJson(a, true);

            Assert.IsNotNull(json);

            // Deserialize

            file = Path.GetTempFileName();
            File.WriteAllText(file, json);
            b = SerializationHelper.DeserializeFromFile <PatchConfig>(file).ToArray();
            File.Delete(file);

            CollectionAssert.AreEqual(a, b);
        }
示例#12
0
        public void Initialize(Mod mtn, IMonitor monitor)
        {
            this.helper = mtn.Helper;
            patchConfig = helper.Data.ReadJsonFile <PatchConfig>("darkmagic.json");
            if (patchConfig == null || patchConfig.Version != mtn.ModManifest.Version.ToString())
            {
                patchConfig = PatchConfig.Default(mtn);
                helper.Data.WriteJsonFile("darkmagic.json", patchConfig);
            }

            ImpInitialize(patchConfig.EventPatch["SetExitLocation"], "Event", "setExitLocation", new setExitLocationPatch(customManager), typeof(setExitLocationPatch));
            ImpInitialize(patchConfig.FarmPatch["CheckAction"], "Farm", "checkAction", new checkActionPatch(customManager), typeof(checkActionPatch));
            ImpInitialize(patchConfig.FarmPatch["Constructor"], "Farm", "", new ConstructorFarmPatch(customManager), typeof(ConstructorFarmPatch), new Type[] { typeof(string), typeof(string) });
            ImpInitialize(patchConfig.FarmPatch["Draw"], "Farm", "draw", new drawPatch(customManager), typeof(drawPatch));
            ImpInitialize(patchConfig.FarmPatch["GetFrontDoorPositionForFarmer"], "Farm", "getFrontDoorPositionForFarmer", new getFrontDoorPositionForFarmerPatch(customManager), typeof(getFrontDoorPositionForFarmerPatch));
            ImpInitialize(patchConfig.FarmPatch["LeftClick"], "Farm", "leftClick", new leftClickPatch(customManager), typeof(leftClickPatch));
            ImpInitialize(patchConfig.FarmPatch["ResetLocalState"], "Farm", "resetLocalState", new resetLocalStatePatch(customManager), typeof(resetLocalStatePatch));
            ImpInitialize(patchConfig.FarmPatch["UpdateWhenCurrentLocation"], "Farm", "UpdateWhenCurrentLocation", new UpdateWhenCurrentLocationPatch(customManager), typeof(UpdateWhenCurrentLocationPatch));
            ImpInitialize(patchConfig.FarmHousePatch["Constructor"], "Locations.FarmHouse", "", new ConstructorFarmHousePatch(customManager), typeof(ConstructorFarmHousePatch), new Type[] { typeof(string), typeof(string) });
            ImpInitialize(patchConfig.FarmHousePatch["GetPorchStandingSpot"], "Locations.FarmHouse", "GetPorchStandingSpot", new getPorchStandingSpotPatch(customManager), typeof(getPorchStandingSpotPatch));
            ImpInitialize(patchConfig.FarmHousePatch["UpdateMap"], "Locations.FarmHouse", "updateMap", new updateMapPatch(customManager), typeof(updateMapPatch));
            ImpInitialize(patchConfig.Game1Patch["LoadForNewGame"], "Game1", "loadForNewGame", new loadForNewGamePatch(customManager, monitor), typeof(loadForNewGamePatch));
            ImpInitialize(patchConfig.GameLocationPatch["LoadObjects"], "GameLocation", "loadObjects", new loadObjectsPatch(customManager), typeof(loadObjectsPatch));
            ImpInitialize(patchConfig.GameLocationPatch["PerformAction"], "GameLocation", "performAction", new performActionPatch(customManager), typeof(performActionPatch));
            ImpInitialize(patchConfig.GameLocationPatch["StartEvent"], "GameLocation", "startEvent", new startEventPatch(customManager), typeof(startEventPatch));
            ImpInitialize(patchConfig.NPCPatch["UpdateConstructionAnimation"], "NPC", "updateConstructionAnimation", new updateConstructionAnimationPatch(customManager), typeof(updateConstructionAnimationPatch));
            ImpInitialize(patchConfig.ObjectPatch["TotemWarpForReal"], "Object", "totemWarpForReal", new totemWarpForRealPatch(customManager), typeof(totemWarpForRealPatch));
            ImpInitialize(patchConfig.PetPatch["DayUpdate"], "Characters.Pet", "dayUpdate", new dayUpdatePatch(customManager), typeof(dayUpdatePatch));
            ImpInitialize(patchConfig.PetPatch["SetAtFarmPosition"], "Characters.Pet", "setAtFarmPosition", new setAtFarmPositionPatch(customManager), typeof(setAtFarmPositionPatch));
            ImpInitialize(patchConfig.SaveGamePatch["LoadDataForLocations"], "SaveGame", "loadDataToLocations", new loadDataToLocationsPatch(), typeof(loadDataToLocationsPatch));
            ImpInitialize(patchConfig.TitleMenuPatch["SetUpIcons"], "Menus.TitleMenu", "setUpIcons", new setUpIconsPatch(), typeof(setUpIconsPatch));
            ImpInitialize(patchConfig.WandPatch["WandWarpForReal"], "Tools.Wand", "wandWarpForReal", new wandWarpForRealPatch(customManager), typeof(wandWarpForRealPatch));
            ImpInitialize(patchConfig.WorldChangeEventPatch["SetUp"], "Events.WorldChangeEvent", "setUp", new setUpPatch(customManager), typeof(setUpPatch));

            //ImpInitialize(helper, "Network.NetBuildingRef", "get_Value", new ValueGetterPatch(), typeof(ValueGetterPatch));
        }
示例#13
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="fs">Stream</param>
        public FuzzingLogInfo(FuzzingStream fs)
        {
            if (fs == null)
            {
                return;
            }

            Info = "";
            if (!string.IsNullOrEmpty(fs.InputName))
            {
                Info = "Input: " + fs.InputName;
            }
            if (!string.IsNullOrEmpty(fs.ConfigName))
            {
                Info += (Info != "" ? "\n" : "") + "Config: " + fs.ConfigName;
            }

            OriginalData = fs.OriginalData;

            if (fs.Log != null && fs.Log.Length > 0)
            {
                Patch = new PatchConfig(fs.SampleId.ToString(), fs.Log);
            }
        }
示例#14
0
        public void Test_Patch_Serialization()
        {
            // Test deserialization

            var value  = File.ReadAllText("Samples/PatchSample.fpatch");
            var config = (PatchConfig)SerializationHelper.DeserializeFromJson <FuzzingConfigBase>(value);

            Assert.AreEqual("8492c051-3acc-4681-8c42-51088cfa0f13", config.Id.ToString());
            Assert.AreEqual("Test", config.Description);
            Assert.AreEqual("Patch", config.Type);
            Assert.AreEqual(1, config.Changes.Count);

            var entry = config.Changes[0];

            Assert.IsTrue(new PatchChange("Buffer overflow (by char)", 16, 1,
                                          Convert.FromBase64String("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"))
                          .Equals(entry));

            // Test PatchConfig and default constructor

            var json  = SerializationHelper.SerializeToJson(config, true);
            var copy  = SerializationHelper.DeserializeFromJson <PatchConfig>(json);
            var copy2 = new PatchConfig(copy.Description, copy.Changes.ToArray())
            {
                Id = copy.Id
            };

            Assert.AreEqual(JObject.Parse(value).ToString(Formatting.Indented), SerializationHelper.SerializeToJson(config, true));

            Assert.AreEqual(SerializationHelper.SerializeToJson(config, true), SerializationHelper.SerializeToJson(copy, true));
            Assert.AreEqual(SerializationHelper.SerializeToJson(copy, true), SerializationHelper.SerializeToJson(copy2, true));
            var copy3 = SerializationHelper.DeserializeFromJson <FuzzingConfigBase>(json);

            Assert.IsTrue(copy.Equals(copy3));

            // Get null

            var stream = new FuzzingStream(config, new byte[100]);

            config.InitFor(stream);

            var change = config.Get(stream);

            Assert.IsNull(change);

            // Seek Offset

            stream.Position = 16;
            change          = config.Get(stream);

            Assert.AreEqual(change, config.Changes[0]);

            // Test PatchChange Equals

            Assert.IsTrue(entry.Equals(copy.Changes[0]));
            Assert.IsTrue(entry.Equals((object)copy.Changes[0]));
            Assert.IsFalse(entry.Equals(new object()));
            Assert.IsFalse(entry.Equals((FuzzingConfigBase) new MutationConfig()));
            Assert.AreEqual(entry.GetHashCode(), copy.Changes[0].GetHashCode());

            entry.Offset++;

            Assert.AreNotEqual(entry.GetHashCode(), copy.Changes[0].GetHashCode());

            // Test PatchConfig Equals

            config = SerializationHelper.DeserializeFromJson <PatchConfig>(json);
            copy   = SerializationHelper.DeserializeFromJson <PatchConfig>(json);

            Assert.IsTrue(config.Equals(copy));
            Assert.IsTrue(config.Equals((object)copy));
            Assert.IsFalse(config.Equals(new object()));
            Assert.IsFalse(config.Equals((FuzzingConfigBase) new MutationConfig()));
            Assert.AreEqual(config.GetHashCode(), copy.GetHashCode());

            config.Id = Guid.NewGuid();

            Assert.AreNotEqual(config.GetHashCode(), copy.GetHashCode());
        }
示例#15
0
        /// <summary>Load one patch from a content pack's <c>content.json</c> file.</summary>
        /// <param name="pack">The content pack being loaded.</param>
        /// <param name="entry">The change to load.</param>
        /// <param name="tokenContext">The tokens available for this content pack.</param>
        /// <param name="migrator">The migrator which validates and migrates content pack data.</param>
        /// <param name="logSkip">The callback to invoke with the error reason if loading it fails.</param>
        private bool LoadPatch(ManagedContentPack pack, PatchConfig entry, IContext tokenContext, IMigration migrator, Action <string> logSkip)
        {
            bool TrackSkip(string reason, bool warn = true)
            {
                this.PatchManager.AddPermanentlyDisabled(new DisabledPatch(entry.LogName, entry.Action, entry.Target, pack, reason));
                if (warn)
                {
                    logSkip(reason);
                }
                return(false);
            }

            try
            {
                // normalise patch fields
                if (entry.When == null)
                {
                    entry.When = new InvariantDictionary <string>();
                }

                // parse action
                if (!Enum.TryParse(entry.Action, true, out PatchType action))
                {
                    return(TrackSkip(string.IsNullOrWhiteSpace(entry.Action)
                        ? $"must set the {nameof(PatchConfig.Action)} field."
                        : $"invalid {nameof(PatchConfig.Action)} value '{entry.Action}', expected one of: {string.Join(", ", Enum.GetNames(typeof(PatchType)))}."
                                     ));
                }

                // parse target asset
                TokenString assetName;
                {
                    if (string.IsNullOrWhiteSpace(entry.Target))
                    {
                        return(TrackSkip($"must set the {nameof(PatchConfig.Target)} field."));
                    }
                    if (!this.TryParseTokenString(entry.Target, tokenContext, migrator, out string error, out assetName))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Target)} is invalid: {error}"));
                    }
                }

                // parse 'enabled'
                bool enabled = true;
                {
                    if (entry.Enabled != null && !this.TryParseEnabled(entry.Enabled, tokenContext, migrator, out string error, out enabled))
                    {
                        return(TrackSkip($"invalid {nameof(PatchConfig.Enabled)} value '{entry.Enabled}': {error}"));
                    }
                }

                // parse conditions
                ConditionDictionary conditions;
                {
                    if (!this.TryParseConditions(entry.When, tokenContext, migrator, out conditions, out string error))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.When)} field is invalid: {error}."));
                    }
                }

                // get patch instance
                IPatch patch;
                switch (action)
                {
                // load asset
                case PatchType.Load:
                {
                    // init patch
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, tokenContext, migrator, out string error, out TokenString fromAsset))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new LoadPatch(entry.LogName, pack, assetName, conditions, fromAsset, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit data
                case PatchType.EditData:
                {
                    // validate
                    if (entry.Entries == null && entry.Fields == null)
                    {
                        return(TrackSkip($"either {nameof(PatchConfig.Entries)} or {nameof(PatchConfig.Fields)} must be specified for a '{action}' change."));
                    }
                    if (entry.Entries != null && entry.Entries.Any(p => p.Value != null && p.Value.Trim() == ""))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Entries)} can't contain empty values."));
                    }
                    if (entry.Fields != null && entry.Fields.Any(p => p.Value == null || p.Value.Any(n => n.Value == null)))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Fields)} can't contain empty values."));
                    }

                    // parse entries
                    List <EditDataPatchRecord> entries = new List <EditDataPatchRecord>();
                    if (entry.Entries != null)
                    {
                        foreach (KeyValuePair <string, string> pair in entry.Entries)
                        {
                            if (!this.TryParseTokenString(pair.Key, tokenContext, migrator, out string keyError, out TokenString key))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Entries)} > '{key}' key is invalid: {keyError}."));
                            }
                            if (!this.TryParseTokenString(pair.Value, tokenContext, migrator, out string error, out TokenString value))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Entries)} > '{key}' value is invalid: {error}."));
                            }
                            entries.Add(new EditDataPatchRecord(key, value));
                        }
                    }

                    // parse fields
                    List <EditDataPatchField> fields = new List <EditDataPatchField>();
                    if (entry.Fields != null)
                    {
                        foreach (KeyValuePair <string, IDictionary <int, string> > recordPair in entry.Fields)
                        {
                            if (!this.TryParseTokenString(recordPair.Key, tokenContext, migrator, out string keyError, out TokenString key))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} is invalid: {keyError}."));
                            }

                            foreach (var fieldPair in recordPair.Value)
                            {
                                int field = fieldPair.Key;
                                if (!this.TryParseTokenString(fieldPair.Value, tokenContext, migrator, out string valueError, out TokenString value))
                                {
                                    return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} > field {field} is invalid: {valueError}."));
                                }
                                if (value.Raw?.Contains("/") == true)
                                {
                                    return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} > field {field} is invalid: value can't contain field delimiter character '/'."));
                                }

                                fields.Add(new EditDataPatchField(key, field, value));
                            }
                        }
                    }

                    // save
                    patch = new EditDataPatch(entry.LogName, pack, assetName, conditions, entries, fields, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit image
                case PatchType.EditImage:
                {
                    // read patch mode
                    PatchMode patchMode = PatchMode.Replace;
                    if (!string.IsNullOrWhiteSpace(entry.PatchMode) && !Enum.TryParse(entry.PatchMode, true, out patchMode))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.PatchMode)} is invalid. Expected one of these values: [{string.Join(", ", Enum.GetNames(typeof(PatchMode)))}]."));
                    }

                    // save
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, tokenContext, migrator, out string error, out TokenString fromAsset))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new EditImagePatch(entry.LogName, pack, assetName, conditions, fromAsset, entry.FromArea, entry.ToArea, patchMode, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                default:
                    return(TrackSkip($"unsupported patch type '{action}'."));
                }

                // skip if not enabled
                // note: we process the patch even if it's disabled, so any errors are caught by the modder instead of only failing after the patch is enabled.
                if (!enabled)
                {
                    return(TrackSkip($"{nameof(PatchConfig.Enabled)} is false.", warn: false));
                }

                // save patch
                this.PatchManager.Add(patch);
                return(true);
            }
            catch (Exception ex)
            {
                return(TrackSkip($"error reading info. Technical details:\n{ex}"));
            }
        }
示例#16
0
        /// <summary>Load one patch from a content pack's <c>content.json</c> file.</summary>
        /// <param name="pack">The content pack being loaded.</param>
        /// <param name="entry">The change to load.</param>
        /// <param name="tokenContext">The tokens available for this content pack.</param>
        /// <param name="migrator">The migrator which validates and migrates content pack data.</param>
        /// <param name="logSkip">The callback to invoke with the error reason if loading it fails.</param>
        private bool LoadPatch(ManagedContentPack pack, PatchConfig entry, IContext tokenContext, IMigration migrator, Action <string> logSkip)
        {
            bool TrackSkip(string reason, bool warn = true)
            {
                reason = reason.TrimEnd('.', ' ');
                this.PatchManager.AddPermanentlyDisabled(new DisabledPatch(entry.LogName, entry.Action, entry.Target, pack, reason));
                if (warn)
                {
                    logSkip(reason + '.');
                }
                return(false);
            }

            try
            {
                // normalise patch fields
                if (entry.When == null)
                {
                    entry.When = new InvariantDictionary <string>();
                }

                // parse action
                if (!Enum.TryParse(entry.Action, true, out PatchType action))
                {
                    return(TrackSkip(string.IsNullOrWhiteSpace(entry.Action)
                        ? $"must set the {nameof(PatchConfig.Action)} field"
                        : $"invalid {nameof(PatchConfig.Action)} value '{entry.Action}', expected one of: {string.Join(", ", Enum.GetNames(typeof(PatchType)))}"
                                     ));
                }

                // parse target asset
                ITokenString assetName;
                {
                    if (string.IsNullOrWhiteSpace(entry.Target))
                    {
                        return(TrackSkip($"must set the {nameof(PatchConfig.Target)} field"));
                    }
                    if (!this.TryParseStringTokens(entry.Target, tokenContext, migrator, out string error, out assetName))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Target)} is invalid: {error}"));
                    }
                }

                // parse 'enabled'
                bool enabled = true;
                {
                    if (entry.Enabled != null && !this.TryParseEnabled(entry.Enabled, tokenContext, migrator, out string error, out enabled))
                    {
                        return(TrackSkip($"invalid {nameof(PatchConfig.Enabled)} value '{entry.Enabled}': {error}"));
                    }
                }

                // parse conditions
                IList <Condition> conditions;
                {
                    if (!this.TryParseConditions(entry.When, tokenContext, migrator, out conditions, out string error))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.When)} field is invalid: {error}"));
                    }
                }

                // get patch instance
                IPatch patch;
                switch (action)
                {
                // load asset
                case PatchType.Load:
                {
                    // init patch
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, tokenContext, migrator, out string error, out ITokenString fromAsset))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new LoadPatch(entry.LogName, pack, assetName, conditions, fromAsset, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit data
                case PatchType.EditData:
                {
                    // validate
                    if (entry.Entries == null && entry.Fields == null && entry.MoveEntries == null)
                    {
                        return(TrackSkip($"one of {nameof(PatchConfig.Entries)}, {nameof(PatchConfig.Fields)}, or {nameof(PatchConfig.MoveEntries)} must be specified for an '{action}' change"));
                    }

                    // parse entries
                    List <EditDataPatchRecord> entries = new List <EditDataPatchRecord>();
                    if (entry.Entries != null)
                    {
                        foreach (KeyValuePair <string, JToken> pair in entry.Entries)
                        {
                            if (!this.TryParseStringTokens(pair.Key, tokenContext, migrator, out string keyError, out ITokenString key))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Entries)} > '{key}' key is invalid: {keyError}"));
                            }
                            if (!this.TryParseJsonTokens(pair.Value, tokenContext, migrator, out string error, out TokenisableJToken value))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Entries)} > '{key}' value is invalid: {error}"));
                            }

                            entries.Add(new EditDataPatchRecord(key, value));
                        }
                    }

                    // parse fields
                    List <EditDataPatchField> fields = new List <EditDataPatchField>();
                    if (entry.Fields != null)
                    {
                        foreach (KeyValuePair <string, IDictionary <string, JToken> > recordPair in entry.Fields)
                        {
                            // parse entry key
                            if (!this.TryParseStringTokens(recordPair.Key, tokenContext, migrator, out string keyError, out ITokenString key))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} is invalid: {keyError}"));
                            }

                            // parse fields
                            foreach (var fieldPair in recordPair.Value)
                            {
                                // parse field key
                                if (!this.TryParseStringTokens(fieldPair.Key, tokenContext, migrator, out string fieldError, out ITokenString fieldKey))
                                {
                                    return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} > field {fieldPair.Key} key is invalid: {fieldError}"));
                                }

                                // parse value
                                if (!this.TryParseJsonTokens(fieldPair.Value, tokenContext, migrator, out string valueError, out TokenisableJToken value))
                                {
                                    return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} > field {fieldKey} is invalid: {valueError}"));
                                }
                                if (value?.Value is JValue jValue && jValue.Value <string>()?.Contains("/") == true)
                                {
                                    return(TrackSkip($"{nameof(PatchConfig.Fields)} > entry {recordPair.Key} > field {fieldKey} is invalid: value can't contain field delimiter character '/'"));
                                }

                                fields.Add(new EditDataPatchField(key, fieldKey, value));
                            }
                        }
                    }

                    // parse move entries
                    List <EditDataPatchMoveRecord> moveEntries = new List <EditDataPatchMoveRecord>();
                    if (entry.MoveEntries != null)
                    {
                        foreach (PatchMoveEntryConfig moveEntry in entry.MoveEntries)
                        {
                            // validate
                            string[] targets = new[] { moveEntry.BeforeID, moveEntry.AfterID, moveEntry.ToPosition };
                            if (string.IsNullOrWhiteSpace(moveEntry.ID))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > move entry is invalid: must specify an {nameof(PatchMoveEntryConfig.ID)} value"));
                            }
                            if (targets.All(string.IsNullOrWhiteSpace))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' is invalid: must specify one of {nameof(PatchMoveEntryConfig.ToPosition)}, {nameof(PatchMoveEntryConfig.BeforeID)}, or {nameof(PatchMoveEntryConfig.AfterID)}"));
                            }
                            if (targets.Count(p => !string.IsNullOrWhiteSpace(p)) > 1)
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' is invalid: must specify only one of {nameof(PatchMoveEntryConfig.ToPosition)}, {nameof(PatchMoveEntryConfig.BeforeID)}, and {nameof(PatchMoveEntryConfig.AfterID)}"));
                            }

                            // parse IDs
                            if (!this.TryParseStringTokens(moveEntry.ID, tokenContext, migrator, out string idError, out ITokenString moveId))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' > {nameof(PatchMoveEntryConfig.ID)} is invalid: {idError}"));
                            }
                            if (!this.TryParseStringTokens(moveEntry.BeforeID, tokenContext, migrator, out string beforeIdError, out ITokenString beforeId))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' > {nameof(PatchMoveEntryConfig.BeforeID)} is invalid: {beforeIdError}"));
                            }
                            if (!this.TryParseStringTokens(moveEntry.AfterID, tokenContext, migrator, out string afterIdError, out ITokenString afterId))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' > {nameof(PatchMoveEntryConfig.AfterID)} is invalid: {afterIdError}"));
                            }

                            // parse position
                            MoveEntryPosition toPosition = MoveEntryPosition.None;
                            if (!string.IsNullOrWhiteSpace(moveEntry.ToPosition) && (!Enum.TryParse(moveEntry.ToPosition, true, out toPosition) || toPosition == MoveEntryPosition.None))
                            {
                                return(TrackSkip($"{nameof(PatchConfig.MoveEntries)} > entry '{moveEntry.ID}' > {nameof(PatchMoveEntryConfig.ToPosition)} is invalid: must be one of {nameof(MoveEntryPosition.Bottom)} or {nameof(MoveEntryPosition.Top)}"));
                            }

                            // create move entry
                            moveEntries.Add(new EditDataPatchMoveRecord(moveId, beforeId, afterId, toPosition));
                        }
                    }

                    // save
                    patch = new EditDataPatch(entry.LogName, pack, assetName, conditions, entries, fields, moveEntries, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit image
                case PatchType.EditImage:
                {
                    // read patch mode
                    PatchMode patchMode = PatchMode.Replace;
                    if (!string.IsNullOrWhiteSpace(entry.PatchMode) && !Enum.TryParse(entry.PatchMode, true, out patchMode))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.PatchMode)} is invalid. Expected one of these values: [{string.Join(", ", Enum.GetNames(typeof(PatchMode)))}]"));
                    }

                    // save
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, tokenContext, migrator, out string error, out ITokenString fromAsset))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new EditImagePatch(entry.LogName, pack, assetName, conditions, fromAsset, entry.FromArea, entry.ToArea, patchMode, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit map
                case PatchType.EditMap:
                {
                    // read map asset
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, tokenContext, migrator, out string error, out ITokenString fromAsset))
                    {
                        return(TrackSkip(error));
                    }

                    // validate
                    if (entry.ToArea == Rectangle.Empty)
                    {
                        return(TrackSkip($"must specify {nameof(entry.ToArea)} (use \"Action\": \"Load\" if you want to replace the whole map file)"));
                    }

                    // save
                    patch = new EditMapPatch(entry.LogName, pack, assetName, conditions, fromAsset, entry.FromArea, entry.ToArea, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                default:
                    return(TrackSkip($"unsupported patch type '{action}'"));
                }

                // skip if not enabled
                // note: we process the patch even if it's disabled, so any errors are caught by the modder instead of only failing after the patch is enabled.
                if (!enabled)
                {
                    return(TrackSkip($"{nameof(PatchConfig.Enabled)} is false", warn: false));
                }

                // save patch
                this.PatchManager.Add(patch);
                return(true);
            }
            catch (Exception ex)
            {
                return(TrackSkip($"error reading info. Technical details:\n{ex}"));
            }
        }
示例#17
0
        /// <summary>Load one patch from a content pack's <c>content.json</c> file.</summary>
        /// <param name="pack">The content pack being loaded.</param>
        /// <param name="entry">The change to load.</param>
        /// <param name="config">The content pack's config values.</param>
        /// <param name="logSkip">The callback to invoke with the error reason if loading it fails.</param>
        private bool LoadPatch(IContentPack pack, PatchConfig entry, InvariantDictionary <ConfigField> config, Action <string> logSkip)
        {
            bool TrackSkip(string reason, bool warn = true)
            {
                this.PatchManager.AddPermanentlyDisabled(new DisabledPatch(entry.LogName, entry.Action, entry.Target, pack, reason));
                if (warn)
                {
                    logSkip(reason);
                }
                return(false);
            }

            try
            {
                // normalise patch fields
                if (entry.When == null)
                {
                    entry.When = new InvariantDictionary <string>();
                }

                // parse action
                if (!Enum.TryParse(entry.Action, true, out PatchType action))
                {
                    return(TrackSkip(string.IsNullOrWhiteSpace(entry.Action)
                        ? $"must set the {nameof(PatchConfig.Action)} field."
                        : $"invalid {nameof(PatchConfig.Action)} value '{entry.Action}', expected one of: {string.Join(", ", Enum.GetNames(typeof(PatchType)))}."
                                     ));
                }

                // parse target asset
                TokenString assetName;
                {
                    if (string.IsNullOrWhiteSpace(entry.Target))
                    {
                        return(TrackSkip($"must set the {nameof(PatchConfig.Target)} field."));
                    }
                    if (!this.TryParseTokenString(entry.Target, config, out string error, out TokenStringBuilder builder))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Target)} is invalid: {error}"));
                    }
                    assetName = builder.Build();
                }

                // parse 'enabled'
                bool enabled = true;
                {
                    if (entry.Enabled != null && !this.TryParseBoolean(entry.Enabled, config, out string error, out enabled))
                    {
                        return(TrackSkip($"invalid {nameof(PatchConfig.Enabled)} value '{entry.Enabled}': {error}"));
                    }
                }

                // apply config
                foreach (string key in config.Keys)
                {
                    if (entry.When.TryGetValue(key, out string values))
                    {
                        InvariantHashSet expected = this.PatchManager.ParseCommaDelimitedField(values);
                        if (!expected.Intersect(config[key].Value).Any())
                        {
                            return(TrackSkip($"disabled: config field '{key}' must have one of '{string.Join(", ", expected)}', but found '{string.Join(", ", config[key].Value)}'.", warn: false));
                        }

                        entry.When.Remove(key);
                    }
                }

                // parse conditions
                ConditionDictionary conditions;
                {
                    if (!this.PatchManager.TryParseConditions(entry.When, out conditions, out string error))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.When)} field is invalid: {error}."));
                    }
                }

                // get patch instance
                IPatch patch;
                switch (action)
                {
                // load asset
                case PatchType.Load:
                {
                    // init patch
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, config, conditions, out string error, out TokenString fromAsset, checkOnly: !enabled))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new LoadPatch(entry.LogName, this.AssetLoader, pack, assetName, conditions, fromAsset, this.Helper.Content.NormaliseAssetName);

                    // detect conflicting loaders
                    if (enabled)
                    {
                        InvariantDictionary <IPatch> conflicts = this.PatchManager.GetConflictingLoaders(patch);
                        if (conflicts.Any())
                        {
                            IEnumerable <string> conflictNames = (from conflict in conflicts
                                                                  orderby conflict.Key
                                                                  select $"'{conflict.Value.LogName}' already loads {conflict.Key}");
                            return(TrackSkip(
                                       $"{nameof(entry.Target)} '{patch.TokenableAssetName.Raw}' conflicts with other load patches ({string.Join(", ", conflictNames)}). Each file can only be loaded by one patch, unless their conditions can never overlap."));
                        }
                    }
                }
                break;

                // edit data
                case PatchType.EditData:
                {
                    // validate
                    if (entry.Entries == null && entry.Fields == null)
                    {
                        return(TrackSkip($"either {nameof(PatchConfig.Entries)} or {nameof(PatchConfig.Fields)} must be specified for a '{action}' change."));
                    }
                    if (entry.Entries != null && entry.Entries.Any(p => string.IsNullOrWhiteSpace(p.Value)))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Entries)} can't contain empty values."));
                    }
                    if (entry.Fields != null && entry.Fields.Any(p => p.Value == null || p.Value.Any(n => n.Value == null)))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.Fields)} can't contain empty values."));
                    }

                    // save
                    patch = new EditDataPatch(entry.LogName, this.AssetLoader, pack, assetName, conditions, entry.Entries, entry.Fields, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                // edit image
                case PatchType.EditImage:
                {
                    // read patch mode
                    PatchMode patchMode = PatchMode.Replace;
                    if (!string.IsNullOrWhiteSpace(entry.PatchMode) && !Enum.TryParse(entry.PatchMode, true, out patchMode))
                    {
                        return(TrackSkip($"the {nameof(PatchConfig.PatchMode)} is invalid. Expected one of these values: [{string.Join(", ", Enum.GetNames(typeof(PatchMode)))}]."));
                    }

                    // save
                    if (!this.TryPrepareLocalAsset(pack, entry.FromFile, config, conditions, out string error, out TokenString fromAsset, checkOnly: !enabled))
                    {
                        return(TrackSkip(error));
                    }
                    patch = new EditImagePatch(entry.LogName, this.AssetLoader, pack, assetName, conditions, fromAsset, entry.FromArea, entry.ToArea, patchMode, this.Monitor, this.Helper.Content.NormaliseAssetName);
                }
                break;

                default:
                    return(TrackSkip($"unsupported patch type '{action}'."));
                }

                // only apply patch when its tokens are available
                HashSet <ConditionKey> tokensUsed = new HashSet <ConditionKey>(patch.GetTokensUsed());
                foreach (ConditionKey key in tokensUsed)
                {
                    if (!patch.Conditions.ContainsKey(key))
                    {
                        patch.Conditions.Add(key, patch.Conditions.GetValidValues(key));
                    }
                }

                // skip if not enabled
                // note: we process the patch even if it's disabled, so any errors are caught by the modder instead of only failing after the patch is enabled.
                if (!enabled)
                {
                    return(TrackSkip($"{nameof(PatchConfig.Enabled)} is false.", warn: false));
                }

                // save patch
                this.PatchManager.Add(patch);
                return(true);
            }
            catch (Exception ex)
            {
                return(TrackSkip($"error reading info. Technical details:\n{ex}"));
            }
        }
示例#18
0
 /// <summary>Get the action type for a patch.</summary>
 /// <param name="patch">The patch to parse.</param>
 protected PatchType?GetAction(PatchConfig patch)
 {
     return(this.GetEnum <PatchType>(patch.Action));
 }
示例#19
0
        private static PatchConfig LoadingPatchConfig(string directory)
        {
            var    patchConfig = new PatchConfig();
            string path        = directory + @"\patch.ini";

            if (File.Exists(path))
            {
                using (TextReader reader = File.OpenText(path))
                {
                    string input;
                    while ((input = reader.ReadLine()) != null)
                    {
                        #region
                        switch (input)
                        {
                        case "[redirect_gateway_server]":
                            input = reader.ReadLine();
                            string[] split = input.Trim().Split(':');
                            if (split.Length > 1)
                            {
                                IPAddress ip = IPAddress.Parse("127.0.0.1");
                                IPAddress.TryParse(split[0], out ip);

                                int port = 20001; int.TryParse(split[1], out port);

                                patchConfig.RedirectGatewayServer = new IPEndPoint(ip, port);
                            }
                            break;

                        case "[redirect_agent_server]":
                            input = reader.ReadLine();
                            split = input.Trim().Split(':');
                            if (split.Length > 1)
                            {
                                IPAddress ip = IPAddress.Parse("127.0.0.1");
                                IPAddress.TryParse(split[0], out ip);

                                int port = 20001; int.TryParse(split[1], out port);

                                patchConfig.RedirectAgentServer = new IPEndPoint(ip, port);
                            }
                            break;

                        case "[multi_client]":
                            input = reader.ReadLine().Trim();
                            patchConfig.MultiClient = input.Equals("1");
                            break;

                        case "[redirect_ip]":
                            input = reader.ReadLine().Trim();
                            patchConfig.RedirectIP = input.Equals("1");
                            break;

                        case "[nude_patch]":
                            input = reader.ReadLine().Trim();
                            patchConfig.NudePatch = input.Equals("1");
                            break;

                        case "[zoom_hack]":
                            input = reader.ReadLine().Trim();
                            patchConfig.ZoomHack = input.Equals("1");
                            break;

                        case "[swear_filter]":
                            input = reader.ReadLine().Trim();
                            patchConfig.SwearFilter = input.Equals("1");
                            break;

                        case "[server_status]":
                            input = reader.ReadLine().Trim();
                            patchConfig.ServerStatus = input.Equals("1");
                            break;

                        case "[no_game_guard]":
                            input = reader.ReadLine().Trim();
                            patchConfig.NoGameGuard = input.Equals("1");
                            break;

                        case "[english_patch]":
                            input = reader.ReadLine().Trim();
                            patchConfig.EnglishPatch = input.Equals("1");
                            break;

                        case "[patch_seed]":
                            input = reader.ReadLine().Trim();
                            patchConfig.PatchSeed = input.Equals("1");
                            break;
                        }
                        #endregion
                    }
                }
            }
            else
            {
                IPAddress ip = IPAddress.Parse("127.0.0.1");
                patchConfig.RedirectGatewayServer = new IPEndPoint(ip, 20001);
                patchConfig.RedirectAgentServer   = new IPEndPoint(ip, 20002);
                patchConfig.MultiClient           = true;
                patchConfig.RedirectIP            = true;
            }

            return(patchConfig);
        }
示例#20
0
        private static void Run(CmdOptions opts)
        {
            using (var server = new FuzzerServer())
            {
                RpcServer rpc = null;

                if (!string.IsNullOrEmpty(opts.RpcEndPoint))
                {
                    rpc = new RpcServer(opts.RpcEndPoint.ToIpEndPoint(), server, opts.RpcHttpsCertificate, opts.RpcHttpsCertificatePassword);
                    rpc.Start();
                }

                // Parse

                foreach (var file in CmdOptions.GetFiles(opts.Inputs))
                {
                    foreach (var i in SerializationHelper.DeserializeFromFile <FuzzingInputBase>(file))
                    {
                        server.Inputs.Add(i.Id, new FuzzerStat <FuzzingInputBase>(i));
                    }
                }

                foreach (var file in CmdOptions.GetFiles(opts.Configs))
                {
                    foreach (var i in SerializationHelper.DeserializeFromFile <FuzzingConfigBase>(file))
                    {
                        server.Configurations.Add(i.Id, new FuzzerStat <FuzzingConfigBase>(i));
                    }
                }

                // Checks

                if (server.Inputs.Count == 0 && rpc == null)
                {
                    Console.WriteLine("No inputs found");
                    return;
                }

                if (server.Configurations.Count == 0)
                {
                    // Create an empty mutation, only the original value

                    _showMutations = false;
                    var empty = new PatchConfig()
                    {
                        Changes     = new List <PatchChange>(),
                        Description = "Original input",
                        Id          = Guid.Empty
                    };
                    server.Configurations.Add(empty.Id, new FuzzerStat <FuzzingConfigBase>(empty));
                }

                server.Start(opts.GetConnection());

                // Listener

                server.OnNewConnection += (s, e) =>
                {
                    PrintStats(server);
                };

                server.OnReceiveLog += (s2, logs) =>
                {
                    foreach (var log in logs)
                    {
                        log.Error?.Save(opts.OnlyUniques);
                    }
                    PrintStats(server);
                };

                // UI

                do
                {
                    Console.Clear();
                    PrintStats(server);
                }while (Console.ReadKey().Key != ConsoleKey.Enter);

                rpc?.Dispose();
            }
        }
示例#21
0
    public GameObject GeneratePatch(PatchConfig aConf, int u, int v)
    {
        // Create new GO and name it
        GameObject patch = new GameObject("Patch_" + aConf.name + "_" + u + "_" + v);

        // Setup mesh-filter/renderer/material for GO
        MeshFilter   mf   = patch.AddComponent <MeshFilter>();
        MeshRenderer rend = patch.AddComponent <MeshRenderer>();

        rend.sharedMaterial = patchMaterial;
        Mesh m = mf.sharedMesh = new Mesh();

        // Parent GO and setup position and rotation
        patch.transform.parent           = transform;
        patch.transform.localEulerAngles = Vector3.zero;
        patch.transform.localPosition    = Vector3.zero;

        // Setup mesh info
        Vector2 UVstep = new Vector2(1f / uPatchCount, 1f / vPatchCount);
        Vector2 step   = new Vector2(UVstep.x / (xVertCount - 1), UVstep.y / (yVertCount - 1));
        Vector2 offset = new Vector3((-0.5f + u * UVstep.x), (-0.5f + v * UVstep.y));

        Vector3[] vertices = new Vector3[xVertCount * yVertCount];
        Vector3[] normals  = new Vector3[vertices.Length];
        Vector2[] uvs      = new Vector2[vertices.Length];

        // Create meshdata and populate it with vertices, uvs and normals
        MeshData meshdata    = new MeshData(flatShading, yVertCount);
        int      vertexIndex = 0;

        for (int y = 0; y < yVertCount; y++)
        {
            for (int x = 0; x < xVertCount; x++)
            {
                // Define vertex position and normal
                Vector2 p = offset + new Vector2(x * step.x, y * step.y);
                uvs[vertexIndex] = p + Vector2.one * 0.5f;
                Vector3 vec = aConf.uAxis * p.x + aConf.vAxis * p.y + aConf.height * 0.5f;
                vec = vec.normalized;

                normals[vertexIndex]  = vec;
                vertices[vertexIndex] = vec * radius;

                // Define triangles
                if (x < xVertCount - 1 && y < yVertCount - 1)     // guard to ignore right bottom edge and far right edge
                {
                    meshdata.AddTriangle(vertexIndex + xVertCount, vertexIndex + xVertCount + 1, vertexIndex);
                    meshdata.AddTriangle(vertexIndex + 1, vertexIndex, vertexIndex + xVertCount + 1);
                }

                vertexIndex++;
            }
        }

        // Assign meshdata to Mesh
        m.vertices  = vertices;
        m.normals   = normals;
        m.uv        = uvs;
        m.triangles = meshdata.triangles;
        m.RecalculateNormals();
        m.RecalculateBounds();

        return(patch);
    }