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); }
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); }
/// <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()); } }
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()); }
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); }
/// <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; } } }
/// <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); }
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; } }
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); }
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)); }
/// <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); } }
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()); }
/// <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}")); } }
/// <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}")); } }
/// <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}")); } }
/// <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)); }
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); }
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(); } }
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); }