public static void Error(string str, params object[] args) { if (IsGlobalEnabled && IsErrorEnabled) { LogInstance?.LogError(string.Format(str, args)); } }
private static void InitEliteTiers(CombatDirector.EliteTierDef[] eliteTiers) { try { var malachite = RoR2Content.Elites.Poison.eliteIndex; var celestine = RoR2Content.Elites.Haunted.eliteIndex; // Find the elite tier that contains Poison (Malachite) and Haunted (Celestine). // This tier is used after looping. var loopEliteTier = eliteTiers.FirstOrDefault(tier => 2 == tier.eliteTypes.Count(t => t != null && ( t.eliteIndex == malachite || t.eliteIndex == celestine ) ) ); if (loopEliteTier.eliteTypes.Contains(RoR2Content.Elites.Lunar)) { instanceLogger?.LogDebug("Loop elite tier already contains perfected elites; aborting"); return; } if (loopEliteTier == null) { instanceLogger?.LogDebug("Loop elites not found through search; falling back to vanilla index"); loopEliteTier = eliteTiers[VanillaLoopEliteTier]; } loopEliteTier.eliteTypes = loopEliteTier.eliteTypes.Concat(new[] { RoR2Content.Elites.Lunar, }).ToArray(); instanceLogger?.LogMessage("Perfected elites should now appear after looping"); } catch (Exception e) { instanceLogger?.LogError(e); } }
public void LogError(string message) { _logger.LogError(message); }
public static void LogE(object data, [CallerLineNumber] int i = 0, [CallerMemberName] string member = "") { logger.LogError(string.Format("ForgottenFoes :: {0} :: Line: {1}, Method {2}", data, i, member)); }
public static void LogE(object data) => logger.LogError(data);
public static void LogError(string message) => Logger?.LogError($"{message}");
public void LateUpdate() { //只觸發一次 if (!_isInited) { _isInited = true; Logger.LogDebug($"--Start listing loaded plugin infos--"); Logger.LogDebug($"--These are listed in order of loading--"); #region GetPlugins //IPA Logger.LogDebug($">>Try load IPA plugin infos"); string IPAAssPath = KoikatuHelper.TryGetPluginInstance("BepInEx.IPALoader", new Version(1, 2))?.Info.Location; //Logger.LogDebug($"Path: {IPAAssPath}"); if (null != IPAAssPath && File.Exists(IPAAssPath)) { Type IPlugin = Assembly.LoadFrom(IPAAssPath).GetType("IllusionPlugin.IPlugin"); Type PluginManager = Assembly.LoadFrom(IPAAssPath).GetType("IllusionInjector.PluginManager"); //呼叫 KK_PluginListTool.GetIPA<IPlugin>(Plugins); MethodInfo method = typeof(PluginListTool).GetMethod(nameof(GetIPA), BindingFlags.Public | BindingFlags.Static); method = method.MakeGenericMethod(IPlugin); method.Invoke(null, new object[] { PluginManager.GetProperties()[0].GetValue(null, null) }); } else { Logger.LogDebug($">>No IPALoader found."); } //BepPlugin Logger.LogDebug($">>Get {BepInEx.Bootstrap.Chainloader.PluginInfos.Count} BepInEx Plugins."); foreach (KeyValuePair <string, PluginInfo> kv in BepInEx.Bootstrap.Chainloader.PluginInfos) { pluginList.Add(new Plugin( kv.Value.Metadata.GUID, kv.Value.Metadata.Name, kv.Value.Metadata.Version.ToString(), kv.Value.Location.Replace(Paths.GameRootPath + "\\", "") )); } #endregion #region WriteFile _ = Directory.CreateDirectory(fullSavePath); try { File.WriteAllText(Path.Combine(fullSavePath, Path.GetFileNameWithoutExtension(Paths.ExecutablePath)) + "_LoadedPluginList.json", JsonHelper.FormatJson($"[{pluginList.Select(x => MakeJsonString(x.GUID, x.Name, x.Version, x.Location)).Join(delimiter: ",")}]")); } catch (Exception e) { Logger.LogError($">>Logged JSON FAILED"); Logger.LogError(e.Message); Logger.LogError(e.StackTrace); } try { File.WriteAllText(Path.Combine(fullSavePath, Path.GetFileNameWithoutExtension(Paths.ExecutablePath)) + "_LoadedPluginList.csv", $"GUID, Name, Version, Location\n{pluginList.Select(x => MakeCsvString(x.GUID, x.Name, x.Version, x.Location)).Join(delimiter: "\n")}"); } catch (Exception e) { Logger.LogError($">>Logged CSV FAILED"); Logger.LogError(e.Message); Logger.LogError(e.StackTrace); } #endregion Logger.LogDebug($"--List Finish--"); } }
//按鈕邏輯 internal static void OnButtonClick(CharaList __instance, int sex) { CharaFileSort charaFileSort = __instance.GetField("charaFileSort") as CharaFileSort; ChaFileControl chaFileControl = new ChaFileControl(); string fullPath = chaFileControl.ConvertCharaFilePath(charaFileSort.selectPath, (byte)sex, false); chaFileControl = null; OCIChar[] array = (from v in Singleton <GuideObjectManager> .Instance.selectObjectKey select Studio.Studio.GetCtrlInfo(v) as OCIChar into v where v != null select v).ToArray(); foreach (OCIChar ocichar in array) { ChaControl chaCtrl = ocichar.charInfo; foreach (OCIChar.BoneInfo boneInfo in (from v in ocichar.listBones where v.boneGroup == OIBoneInfo.BoneGroup.Hair select v).ToList <OCIChar.BoneInfo>()) { Singleton <GuideObjectManager> .Instance.Delete(boneInfo.guideObject, true); } _ = ocichar.charInfo.chaFile.Invoke("SaveFile", new object[] { Path.GetTempFileName() }); ocichar.listBones = (from v in ocichar.listBones where v.boneGroup != OIBoneInfo.BoneGroup.Hair select v).ToList <OCIChar.BoneInfo>(); int[] array2 = (from b in ocichar.oiCharInfo.bones where b.Value.@group == OIBoneInfo.BoneGroup.Hair select b.Key).ToArray <int>(); for (int j = 0; j < array2.Length; j++) { ocichar.oiCharInfo.bones.Remove(array2[j]); } ocichar.hairDynamic = null; ocichar.skirtDynamic = null; string oldName = ocichar.charInfo.chaFile.parameter.fullname; //用這種方式初始化不會觸發其他鉤子 ChaControl tmpCtrl = new ChaControl(); tmpCtrl.SetProperty <ChaInfo>("chaFile", new ChaFileControl()); //Main Load Control bool success = true; // This function always return false: chaCtrl.chaFile.LoadFileLimited _ = chaCtrl.chaFile.LoadFileLimited(fullPath, (byte)sex, true, true, true, true, false); success &= LoadExtendedData(ocichar, charaFileSort.selectPath, (byte)sex); success &= UpdateTreeNodeObjectName(ocichar); if (null != MoreAccessories) { UpdateMoreAccessoriesData(ocichar.charInfo); } if (!success) { Logger.LogError("Load Body FAILED"); } GameObject.Destroy(tmpCtrl); //ocichar.charInfo.AssignCoordinate((ChaFileDefine.CoordinateType)ocichar.charInfo.fileStatus.coordinateType); byte[] data = ocichar.charInfo.nowCoordinate.SaveBytes(); ocichar.charInfo.chaFile.coordinate[ocichar.charInfo.chaFile.status.coordinateType].LoadBytes(data, ocichar.charInfo.nowCoordinate.loadVersion); chaCtrl.Reload(false, false, false, false); AddObjectAssist.InitHairBone(ocichar, Singleton <Info> .Instance.dicBoneInfo); ocichar.hairDynamic = AddObjectFemale.GetHairDynamic(ocichar.charInfo.objHair); ocichar.skirtDynamic = AddObjectFemale.GetSkirtDynamic(ocichar.charInfo.objClothes); ocichar.InitFK(null); foreach (var tmp in FKCtrl.parts.Select((OIBoneInfo.BoneGroup p, int i2) => new { p, i2 })) { ocichar.ActiveFK(tmp.p, ocichar.oiCharInfo.activeFK[tmp.i2], ocichar.oiCharInfo.activeFK[tmp.i2]); } ocichar.ActiveKinematicMode(OICharInfo.KinematicMode.FK, ocichar.oiCharInfo.enableFK, true); ocichar.UpdateFKColor(new OIBoneInfo.BoneGroup[] { OIBoneInfo.BoneGroup.Hair }); ocichar.ChangeEyesOpen(ocichar.charFileStatus.eyesOpenMax); ocichar.ChangeBlink(ocichar.charFileStatus.eyesBlink); ocichar.ChangeMouthOpen(ocichar.oiCharInfo.mouthOpen); Logger.LogInfo($"Load Body: {oldName} -> {ocichar.charInfo.chaFile.parameter.fullname}"); } }
public void Init() { DontDestroyOnLoad(this); Extensions.UnityInjectorPath = UnityInjectorLocation.Value; if (!Directory.Exists(Extensions.UnityInjectorPath)) { Logger.LogInfo($"No UnityInjector path found in {Extensions.UnityInjectorPath}. Creating one..."); try { Directory.CreateDirectory(Extensions.UnityInjectorPath); Directory.CreateDirectory(Extensions.UserDataPath); } catch (Exception e) { Logger.LogFatal($"Failed to create UnityInjector folder! Error message: {e.Message}"); } Destroy(this); return; } managerObject = new GameObject("UnityInjector"); Logger.LogInfo("UnityInjector started"); string currentProcess = Process.GetCurrentProcess().ProcessName; var plugins = new List <Type>(); foreach (string pluginDll in Directory.GetFiles(Extensions.UnityInjectorPath, "*.dll")) { try { var pluginAssembly = Assembly.LoadFile(pluginDll); foreach (var type in pluginAssembly.GetTypes()) { if (type.IsAbstract || type.IsInterface || !typeof(PluginBase).IsAssignableFrom(type)) { continue; } var filterAttributes = (PluginFilterAttribute[])type.GetCustomAttributes(typeof(PluginFilterAttribute), false); if (filterAttributes.Length == 0 || filterAttributes.Select(attr => attr.ExeName) .Contains(currentProcess, StringComparer.InvariantCultureIgnoreCase)) { plugins.Add(type); var name = type.GetCustomAttributes(typeof(PluginNameAttribute), false).FirstOrDefault() as PluginNameAttribute; var version = type.GetCustomAttributes(typeof(PluginVersionAttribute), false).FirstOrDefault() as PluginVersionAttribute; Logger.LogInfo($"UnityInjector: loaded {name?.Name ?? pluginAssembly.GetName().Name} {version?.Version ?? pluginAssembly.GetName().Version.ToString()}"); } } } catch (Exception e) { Logger.LogError($"Failed to load {pluginDll}. Stack trace:\n{e}"); } } if (plugins.Count == 0) { Logger.LogInfo("UnityInjector: No plugins found!"); Destroy(managerObject); Destroy(this); return; } foreach (var plugin in plugins) { try { managerObject.AddComponent(plugin); } catch (Exception e) { Logger.LogError($"UnityInjector: Failed to initialize {plugin.Assembly.GetName().Name}\nError: {e}"); } } Logger.LogInfo("UnityInjector: All plugins loaded"); managerObject.SetActive(true); }
private static bool PatchVROptions(bool UnPatch = false) { string path = Path.Combine(ManagedPath, "../globalgamemanagers"); AssetsManager am = new AssetsManager(); AssetsFileInstance afi = am.LoadAssetsFile(path, false); am.LoadClassDatabase(Path.Combine(VRPatcherPath, "cldb.dat")); for (int i = 0; i < afi.table.assetFileInfoCount; i++) { try { AssetFileInfoEx info = afi.table.GetAssetInfo(i); AssetTypeInstance ati = am.GetATI(afi.file, info); AssetTypeValueField baseField = ati?.GetBaseField(); AssetTypeValueField enabledVRDevicesField = baseField?.Get("enabledVRDevices"); if (enabledVRDevicesField is null) { continue; } AssetTypeValueField vrArrayField = enabledVRDevicesField.Get("Array"); if (vrArrayField is null) { continue; } AssetTypeValueField OpenVR = ValueBuilder.DefaultValueFieldFromArrayTemplate(vrArrayField); OpenVR.GetValue().Set("OpenVR"); AssetTypeValueField Oculus = ValueBuilder.DefaultValueFieldFromArrayTemplate(vrArrayField); Oculus.GetValue().Set("Oculus"); AssetTypeValueField None = ValueBuilder.DefaultValueFieldFromArrayTemplate(vrArrayField); None.GetValue().Set("None"); if (!UnPatch) { vrArrayField.SetChildrenList(new AssetTypeValueField[] { None, OpenVR, Oculus }); Logger.LogMessage("Patching GGM"); } else { vrArrayField.SetChildrenList(new AssetTypeValueField[] { None }); Logger.LogMessage("UnPatching GGM"); } byte[] vrAsset; using (MemoryStream memStream = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(memStream)) { writer.bigEndian = false; baseField.Write(writer); vrAsset = memStream.ToArray(); } List <AssetsReplacer> rep = new List <AssetsReplacer>() { new AssetsReplacerFromMemory(0, i, (int)info.curFileType, 0xFFFF, vrAsset) }; using (MemoryStream memStream = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(memStream)) { afi.file.Write(writer, 0, rep, 0); afi.stream.Close(); File.WriteAllBytes(path, memStream.ToArray()); } return(true); } catch { } } Logger.LogError("VR enable location not found!"); return(false); }
private static void ConCmdYeet(ConCommandArgs args) { if (!args.senderBody) { _logger.LogError("ConCmdYeet: called by nonexistent player!"); return; } if (args.Count < 1) { _logger.LogError("ConCmdYeet: not enough arguments! Need at least 1 (item ID), received 0."); return; } bool isEquipment = args.TryGetArgBool(1) ?? false; if (isEquipment ? preventEquipment : preventItems) { return; } int rawInd; string itemSearch = args.TryGetArgString(0); if (itemSearch == null) { _logger.LogError("ConCmdYeet: could not read first argument (item ID)!"); return; } else if (int.TryParse(itemSearch, out rawInd)) { if (isEquipment) { if (!EquipmentCatalog.IsIndexValid((EquipmentIndex)rawInd)) { _logger.LogError("ConCmdYeet: first argument (equipment ID as integer EquipmentIndex) is out of range; no equipment with that ID exists!"); return; } } else { if (!ItemCatalog.IsIndexValid((ItemIndex)rawInd)) { _logger.LogError("ConCmdYeet: first argument (item ID as integer ItemIndex) is out of range; no item with that ID exists!"); return; } } } else { if (isEquipment) { var results = EquipmentCatalog.allEquipment.Where((searchInd) => { var iNameToken = EquipmentCatalog.GetEquipmentDef(searchInd).nameToken; var iName = Language.GetString(iNameToken); return(iName.ToUpper().Contains(itemSearch.ToUpper())); }); if (results.Count() < 1) { _logger.LogError("ConCmdYeet: first argument (equipment ID as string EquipmentName) not found in EquipmentCatalog; no equipment with a name containing that string exists!"); return; } else { if (results.Count() > 1) { _logger.LogWarning("ConCmdYeet: first argument (item ID as string EquipmentName) matched multiple equipments; using first."); } rawInd = (int)results.First(); } } else { var results = ItemCatalog.allItems.Where((searchInd) => { var iNameToken = ItemCatalog.GetItemDef(searchInd).nameToken; var iName = Language.GetString(iNameToken); return(iName.ToUpper().Contains(itemSearch.ToUpper())); }); if (results.Count() < 1) { _logger.LogError("ConCmdYeet: first argument (item ID as string ItemName) not found in ItemCatalog; no item with a name containing that string exists!"); return; } else { if (results.Count() > 1) { _logger.LogWarning("ConCmdYeet: first argument (item ID as string ItemName) matched multiple items; using first."); } rawInd = (int)results.First(); } } } float throwForce = Mathf.Lerp(lowThrowForce, highThrowForce, Mathf.Clamp01(args.TryGetArgFloat(2) ?? 0f)); if (isEquipment) { if (args.senderBody.inventory.GetEquipmentIndex() != (EquipmentIndex)rawInd) { _logger.LogWarning("ConCmdYeet: someone's trying to drop an equipment they don't have"); return; } var edef = EquipmentCatalog.GetEquipmentDef((EquipmentIndex)rawInd); args.senderBody.inventory.SetEquipmentIndex(EquipmentIndex.None); args.senderBody.inventory.RemoveItem((ItemIndex)rawInd); PickupDropletController.CreatePickupDroplet( PickupCatalog.FindPickupIndex((EquipmentIndex)rawInd), args.senderBody.inputBank.aimOrigin, args.senderBody.inputBank.aimDirection * throwForce); } else { int count; if (Compat_TILER2.enabled) { count = Compat_TILER2.GetRealItemCount(args.senderBody.inventory, (ItemIndex)rawInd); } else { count = args.senderBody.inventory.GetItemCount((ItemIndex)rawInd); } if (count < 1) { _logger.LogWarning("ConCmdYeet: someone's trying to drop an item they don't have any of"); return; } var idef = ItemCatalog.GetItemDef((ItemIndex)rawInd); if (idef.hidden || !idef.canRemove || (idef.tier == ItemTier.Lunar && preventLunar) || idef.tier == ItemTier.NoTier) { return; } args.senderBody.inventory.RemoveItem((ItemIndex)rawInd); var obj = GameObject.Instantiate(PickupDropletController.pickupDropletPrefab, args.senderBody.inputBank.aimOrigin, Quaternion.identity); obj.AddComponent <PickupDropletNoCommandFlag>(); obj.GetComponent <PickupDropletController>().NetworkpickupIndex = PickupCatalog.FindPickupIndex((ItemIndex)rawInd); var rbdy = obj.GetComponent <Rigidbody>(); rbdy.velocity = args.senderBody.inputBank.aimDirection * throwForce; rbdy.AddTorque(Random.Range(150f, 120f) * Random.onUnitSphere); NetworkServer.Spawn(obj); } }
private IEnumerator TakeCharScreenshot(bool in3D) { if (currentAlphaShot == null) { Logger.Log(LogLevel.Message, "Can't render a screenshot here, try UI screenshot instead"); yield break; } try { OnPreCapture?.Invoke(); } catch (Exception ex) { Logger.LogError(ex); } if (!in3D) { yield return(new WaitForEndOfFrame()); var capture = currentAlphaShot.CaptureTex(ResolutionX.Value, ResolutionY.Value, DownscalingRate.Value, CaptureAlpha.Value); var filename = GetUniqueFilename("Render"); File.WriteAllBytes(filename, EncodeToFile(capture)); Logger.Log(ScreenshotMessage.Value ? LogLevel.Message : LogLevel.Info, $"Character screenshot saved to {filename}"); Destroy(capture); } else { var targetTr = Camera.main.transform; ToggleCameraControllers(targetTr, false); Time.timeScale = 0.01f; yield return(new WaitForEndOfFrame()); targetTr.position += targetTr.right * EyeSeparation.Value / 2; // Let the game render at the new position yield return(new WaitForEndOfFrame()); var capture = currentAlphaShot.CaptureTex(ResolutionX.Value, ResolutionY.Value, DownscalingRate.Value, CaptureAlpha.Value); targetTr.position -= targetTr.right * EyeSeparation.Value; yield return(new WaitForEndOfFrame()); var capture2 = currentAlphaShot.CaptureTex(ResolutionX.Value, ResolutionY.Value, DownscalingRate.Value, CaptureAlpha.Value); targetTr.position += targetTr.right * EyeSeparation.Value / 2; ToggleCameraControllers(targetTr, true); Time.timeScale = 1; var result = StitchImages(capture, capture2, ImageSeparationOffset.Value); var filename = GetUniqueFilename("3D-Render"); File.WriteAllBytes(filename, EncodeToFile(result)); Logger.Log(ScreenshotMessage.Value ? LogLevel.Message : LogLevel.Info, $"3D Character screenshot saved to {filename}"); Destroy(capture); Destroy(capture2); Destroy(result); } try { OnPostCapture?.Invoke(); } catch (Exception ex) { Logger.LogError(ex); } Utils.Sound.Play(SystemSE.photo); }
private static void ShimQMods() { if (!Directory.Exists(QModsPath)) { Logger.LogInfo("No QMods folder found! Mod shimming aborted."); return; } Logger.LogInfo("Shimming QMods..."); var harmonyTypes = new HashSet <string>(); using (var shimmerAssemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location)) { foreach (var type in shimmerAssemblyDefinition.MainModule.Types) { if (type.Namespace.StartsWith("Harmony")) { harmonyTypes.Add(type.FullName); } } } var harmonyFullTypes = new Dictionary <string, string>(); using (var harmonyAssemblyDefinition = AssemblyDefinition.ReadAssembly(Path.Combine(Paths.BepInExAssemblyDirectory, "0Harmony.dll"))) { foreach (var type in harmonyAssemblyDefinition.MainModule.Types) { if (type.Namespace.StartsWith("HarmonyLib")) { harmonyFullTypes.Add(type.Name, type.Namespace); } } } foreach (var subfolderPath in Directory.GetDirectories(QModsPath)) { foreach (string filePath in Directory.GetFiles(subfolderPath, "*.dll", SearchOption.TopDirectoryOnly)) { try { using (var stream = new MemoryStream(File.ReadAllBytes(filePath))) { var assemblyDefinition = AssemblyDefinition.ReadAssembly(stream); if (assemblyDefinition.MainModule.AssemblyResolver is DefaultAssemblyResolver qModResolver) { qModResolver.ResolveFailure += ResolveAssemblies; } var harmonyShimReference = new AssemblyNameReference("0Harmony_Shim", new Version(1, 1, 0, 0)); var harmony2Reference = new AssemblyNameReference("0Harmony", new Version(2, 0, 0, 0)); var outdatedHarmonyReference = assemblyDefinition.MainModule.AssemblyReferences .FirstOrDefault(reference => reference.Name.StartsWith("0Harmony") && reference.Name != "0Harmony_Shim" && reference.Version.Major <= 1); bool shimmed = false; if (assemblyDefinition.MainModule.Types.Any(type => harmonyTypes.Contains(type.FullName))) { // Unmerge the assembly shimmed = true; Logger.LogInfo($"Unmerging {Path.GetFileNameWithoutExtension(filePath)}"); assemblyDefinition.MainModule.AssemblyReferences.Add(harmony2Reference); assemblyDefinition.MainModule.AssemblyReferences.Add(harmonyShimReference); foreach (var typeDefinition in assemblyDefinition.MainModule.Types.ToList()) { string @namespace = null; if (harmonyTypes.Contains(typeDefinition.FullName) || (typeDefinition.Namespace.StartsWith("Harmony") && harmonyFullTypes.TryGetValue(typeDefinition.Name, out @namespace))) { assemblyDefinition.MainModule.Types.Remove(typeDefinition); typeDefinition.Scope = harmonyTypes.Contains(typeDefinition.FullName) ? harmonyShimReference : harmony2Reference; typeDefinition.GetType().GetField("module", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(typeDefinition, assemblyDefinition.MainModule); typeDefinition.MetadataToken = new MetadataToken(TokenType.TypeRef, 0); if (@namespace != null) { typeDefinition.Namespace = @namespace; } foreach (var method in typeDefinition.Methods) { method.MetadataToken = new MetadataToken(TokenType.MemberRef, 0); } foreach (var member in typeDefinition.Fields.Cast <IMemberDefinition>().Concat(typeDefinition.Methods.Cast <IMemberDefinition>())) { member.MetadataToken = new MetadataToken(TokenType.MemberRef, 0); } } } } else if (outdatedHarmonyReference != null) { // Shim Harmony shimmed = true; Logger.LogInfo($"Shimming {Path.GetFileNameWithoutExtension(filePath)}"); outdatedHarmonyReference.Name = "0Harmony_Shim"; assemblyDefinition.MainModule.AssemblyReferences.Add(harmony2Reference); foreach (var typeReference in assemblyDefinition.MainModule.GetTypeReferences()) { if (typeReference.Namespace.StartsWith("Harmony") && !harmonyTypes.Contains(typeReference.FullName) && harmonyFullTypes.TryGetValue(typeReference.Name, out var @namespace)) { typeReference.Namespace = @namespace; typeReference.Scope = harmony2Reference; } } foreach (var memberReference in assemblyDefinition.MainModule.GetMemberReferences()) { if (memberReference is MethodReference methodReference && methodReference.DeclaringType.Name == "HarmonyInstance" && methodReference.DeclaringType.Scope == outdatedHarmonyReference && methodReference.Name == "Patch" && methodReference.ReturnType.FullName == "System.Void") { methodReference.Name = "PatchVoid"; } } } if (shimmed) { // backup the original to QMods\.backups and write the patched back to the QMods folder, where QModManager loads from. var pathPart = filePath.Substring(QModsPath.Length + 1); var backupPath = Path.Combine(QModBackupsPath, pathPart); Logger.LogInfo($"Backing up {Path.GetFileNameWithoutExtension(filePath)} to {backupPath}. Original path: {filePath}"); var backupDirInfo = Directory.CreateDirectory(QModBackupsPath); if ((backupDirInfo.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden) { backupDirInfo.Attributes |= FileAttributes.Hidden; } Directory.CreateDirectory(Path.GetDirectoryName(backupPath)); File.Copy(filePath, backupPath, true); TypeDefinitionIsDefinitionPatch.definitionsAreReferences = true; assemblyDefinition.Write(filePath); TypeDefinitionIsDefinitionPatch.definitionsAreReferences = false; } } } catch (BadImageFormatException) { if (Path.GetFileName(filePath).Contains("0Harmony")) { Logger.LogWarning($"QMod in folder {Path.GetDirectoryName(subfolderPath)} is shipping its own version of Harmony! " + "This is NOT RECOMMENDED and can lead to serious compatibility issues with other mods! " + "If you are a mod author, please do not ship Harmony with your mods, " + $"and instead rely on QModManager to load it for you. For more details, see the wiki:{Environment.NewLine}" + "https://github.com/SubnauticaModding/QModManager/wiki/Basic-ModLoading-Setup-(Basics)"); } else { Logger.LogInfo($"Cannot shim {Path.GetFileName(filePath)} as it is not a valid assembly, skipping..."); } continue; } catch (Exception e) { Logger.LogError($"Failed to shim {Path.GetFileName(filePath)}"); Logger.LogError(e); continue; } } } }
public override void Load() { // Register CoreCompontent try { ClassInjector.RegisterTypeInIl2Cpp <CoreCompontent>(); Logging.LogMessage("Successfully registered CoreCompontent in IL2CPP"); } catch { Logging.LogError("Failed to register CoreCompontent in IL2CPP"); } // Hook Events try { // Ingame Hook(typeof(SteamMainGameSceneBase).GetMethod("Awake"), "IngameStart"); Hook(typeof(SteamMainGameSceneBase).GetMethod("Update"), "RunCallbacks"); // Grab Hook(typeof(SongInfoDetailView).GetMethod("Awake"), "GetSongInfo"); Hook(typeof(DifficultChangeController).GetMethod("Awake"), "GetDifficultySwitcher"); Hook(typeof(BaseKeymodeChangeController).GetMethod("Awake"), "GetKeymodeSwitcher"); // Update & Destroy Hook(typeof(SongSelectCoverRotator).GetMethod("Update"), "SongSelectUpdate"); Hook(typeof(SongSelectCoverRotator).GetMethod("OnDestroy"), "RemoveReferences"); // Mode Catches Hook(typeof(AirModeNextTuneView).GetMethod("Update"), "AirCatch"); Hook(typeof(FreestylePanelController).GetMethod("Awake"), "FreestyleCatch"); Hook(typeof(MissionDetailRotator).GetMethod("Awake"), "MissionCatch"); Hook(typeof(MaingameResultView).GetMethod("Awake"), "ResultCatch"); // Difficulty Method /* * * Intended to grab the method to return the currently selected difficulty enum from DifficultChangeController * Only problem is that there's ~6 methods that return an enum, which are likely like... LowestDifficulty/HighestDifficulty * Not really sure what I can do here, as using obfuscated names will inevitably break backwards compatibility (if names are randomized) * * Could be worth checking other classes to see if the difficulty is stored elsewhere, as of v647 the difficulty enum is named EJLGBABDMIP.NACOBCHIEAO */ /* * MethodInfo[] _difficultyMethods = typeof(DifficultChangeController).GetMethods(); * for(int i = 0; i < _difficultyMethods.Length; i++) { * if (_difficultyMethods[i].GetParameters().Length == 0) { * if (_difficultyMethods[i].ReturnType.IsEnum == true) { * DifficultyMethod = _difficultyMethods[i].Name; * break; * } * } * } */ // CLKCGPLNLNC // Type: EJLGBABDMIP.NACOBCHIEAO Logging.LogMessage("Successfully hooked events for CoreCompontent"); } catch { Logging.LogError("Failed to hook events for CoreCompontent"); } // Setup Discord try { DiscordInstance = new Discord.Discord(Int64.Parse("773332255610372117"), (UInt64)CreateFlags.Default); DiscordActivity = DiscordInstance.GetActivityManager(); DiscordInstance.SetLogHook(Discord.LogLevel.Debug, (level, message) => { Logging.LogMessage("[DISCORD] " + message); }); Logging.LogMessage("Successfully loaded Discord API"); } catch { Logging.LogError("Failed to load Discord"); } }
public static void LogError(string message, Exception e = null) => Logger.LogError($"{message}; {e?.Message ?? ""}");
public static bool Prefix(ref Dictionary <string, UnlockableDef> ___nameToDefTable) { //Set up logging var unlockableLogger = new ManualLogSource("AchievementLoader.Unlock"); BepInEx.Logging.Logger.Sources.Add(unlockableLogger); unlockableLogger.LogMessage("__== Unlock Loader ==__"); unlockableLogger.LogInfo("Searching for custom unlockables..."); var customUnlockableDefs = from t in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name != "ConfigurationManager").SelectMany(a => { unlockableLogger.LogDebug(String.Format("Scanning assembly {0} for unlockable defs", a.FullName)); try { return(a.GetTypes()); } catch { unlockableLogger.LogError(String.Format("Unable to scan {0} for types", a.FullName)); return(new Type[0]); } }) let attributes = t.GetCustomAttributes(typeof(CustomUnlockable), true) where attributes != null && attributes.Length > 0 select(attributes.Cast <CustomUnlockable>().ToArray()[0]); if (customUnlockableDefs.Count() > 0) { unlockableLogger.LogInfo(string.Format("Found {0} custom unlock(s) to add to catalog", customUnlockableDefs.Count())); //Iterate through each custom unlock and add them to the unlock catalog foreach (CustomUnlockable customUnlock in customUnlockableDefs) { //Create new unlock def UnlockableDef newUnlock = customUnlock.GetUnlockableDef(); //Set the index of the unlock def newUnlock.index = new UnlockableIndex(___nameToDefTable.Count); //Add the def to the unlock table ___nameToDefTable.Add(newUnlock.name, newUnlock); unlockableLogger.LogDebug(string.Format("Added Custom Unlock {0}", newUnlock.name)); } } else { unlockableLogger.LogInfo("Found no custom unlocks to add"); } unlockableLogger.LogInfo("Done!"); //Continue on the the original unlock catalog return(true); }
public static bool Prefix(ref Dictionary <string, AchievementDef> ___achievementNamesToDefs, ref List <string> ___achievementIdentifiers, ref AchievementDef[] ___achievementDefs, ref AchievementDef[] ___serverAchievementDefs, ref Action ___onAchievementsRegistered, Dictionary <string, AchievementDef> map) { //Setup logging var achievementLogger = new ManualLogSource("AchievementLoader.Achievement"); BepInEx.Logging.Logger.Sources.Add(achievementLogger); achievementLogger.LogMessage("__== Achievement Loader ==__"); achievementLogger.LogInfo("Searching for overrides"); //Search and collect overrides IEnumerable <Type> achievementOverrides = from t in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name != "ConfigurationManager").SelectMany(a => { achievementLogger.LogDebug(String.Format("Scanning assembly {0} for override defs", a.FullName)); try { return(a.GetTypes()); } catch { achievementLogger.LogError(String.Format("Unable to scan {0} for override defs", a.FullName)); return(new Type[0]); } }) let attributes = t.GetCustomAttributes(typeof(OverrideAchievement), true) where attributes != null && attributes.Length > 0 select attributes.Cast <OverrideAchievement>().ToArray()[0].achievementType; //Convert from IEnum to List since it's way faster to search a list lmao List <Type> achievementOverrideTypes = achievementOverrides.ToList(); achievementLogger.LogInfo(string.Format("Found {0} overrides", achievementOverrides.Count())); //Used for building achievement unlock chains? List <AchievementDef> list = new List <AchievementDef>(); //Used for building achievement unlock chains? map.Clear(); //Get all classes that inherit from BaseAchievement foreach (Type achievementClass in from type in (AppDomain.CurrentDomain.GetAssemblies() .Where(a => !a.IsDynamic) .SelectMany(a => { try { return(a.GetTypes()); } catch { achievementLogger.LogError(String.Format("Unable to scan {0} for achievements", a.FullName)); return(new Type[0]); } })) where type.IsSubclassOf(typeof(BaseAchievement)) orderby type.Name select type) { //Check if the achievement has an override defined if (achievementOverrideTypes.Contains(achievementClass)) { //If it does, don't add it continue; } //Get the achievement attribute RegisterAchievementAttribute registerAchievementAttribute = (RegisterAchievementAttribute)Attribute.GetCustomAttribute(achievementClass, typeof(RegisterAchievementAttribute)); //Get the custom achievement attribute CustomAchievementAttribute customAchievementAttribute = (CustomAchievementAttribute)Attribute.GetCustomAttribute(achievementClass, typeof(CustomAchievementAttribute)); //If it's found, set the vanilla attribute to have the same values so we don't have to change a bunch of stuff if (customAchievementAttribute != null) { registerAchievementAttribute = customAchievementAttribute; } //Check if the achievement was registered if (registerAchievementAttribute != null) { //Make sure each achievement is unique if (map.ContainsKey(registerAchievementAttribute.identifier)) { Debug.LogErrorFormat("Class {0} attempted to register as achievement {1}, but class {2} has already registered as that achievement.", new object[] { achievementClass.FullName, registerAchievementAttribute.identifier, ___achievementNamesToDefs[registerAchievementAttribute.identifier].type.FullName }); } else { //Create the vanilla resource path string iconPath = "Textures/AchievementIcons/tex" + registerAchievementAttribute.identifier + "Icon"; //Create the icon name string iconName = "tex" + registerAchievementAttribute.identifier + "Icon"; //If it's a custom achievement, get the path for it if (customAchievementAttribute != null) { iconPath = customAchievementAttribute.GetPath(iconName); } //If the achievement is unique, build the achievement AchievementDef achievementDef2 = new AchievementDef { identifier = registerAchievementAttribute.identifier, unlockableRewardIdentifier = registerAchievementAttribute.unlockableRewardIdentifier, prerequisiteAchievementIdentifier = registerAchievementAttribute.prerequisiteAchievementIdentifier, nameToken = "ACHIEVEMENT_" + registerAchievementAttribute.identifier.ToUpper(CultureInfo.InvariantCulture) + "_NAME", descriptionToken = "ACHIEVEMENT_" + registerAchievementAttribute.identifier.ToUpper(CultureInfo.InvariantCulture) + "_DESCRIPTION", iconPath = iconPath, type = achievementClass, serverTrackerType = registerAchievementAttribute.serverTrackerType }; //Add the achievement identifier to the achievement identifier list ___achievementIdentifiers.Add(registerAchievementAttribute.identifier); //Used for building achievement unlock chains? map.Add(registerAchievementAttribute.identifier, achievementDef2); //Used to build achievement defs array? list.Add(achievementDef2); //Get the related unlockable def UnlockableDef unlockableDef = UnlockableCatalog.GetUnlockableDef(achievementDef2.unlockableRewardIdentifier); //If there is an unlockable def, set the how to unlock and unlocked text if (unlockableDef != null) { string achievementName = Language.GetString(achievementDef2.nameToken); string achievementDescription = Language.GetString(achievementDef2.descriptionToken); unlockableDef.getHowToUnlockString = (() => Language.GetStringFormatted("UNLOCK_VIA_ACHIEVEMENT_FORMAT", new object[] { achievementName, achievementDescription })); unlockableDef.getUnlockedString = (() => Language.GetStringFormatted("UNLOCKED_FORMAT", new object[] { achievementName, achievementDescription })); } } } else { //Warn that a class inheriting from BaseAchievement doesn't have a AchievementAttribute string msg = string.Format("Class {0} in {1} inherits from BaseAchievement but doesn't have a RegisterAchievement attribute.", new object[] { achievementClass.FullName, achievementClass.Assembly.GetName().Name }); achievementLogger.LogWarning(msg); } } ___achievementDefs = list.ToArray(); //Order achievements ___achievementDefs = ___achievementDefs.OrderBy(def => UnlockableCatalog.GetUnlockableSortScore(def.unlockableRewardIdentifier)).ToArray(); //Get achievements that need to be server tracked ___serverAchievementDefs = (from achievementDef in ___achievementDefs where achievementDef.serverTrackerType != null select achievementDef).ToArray <AchievementDef>(); //Build achievements to be tracked by client? for (int i = 0; i < ___achievementDefs.Length; i++) { ___achievementDefs[i].index = new AchievementIndex { intValue = i }; } //Build achievements to be tracked by server? for (int j = 0; j < ___serverAchievementDefs.Length; j++) { ___serverAchievementDefs[j].serverIndex = new ServerAchievementIndex { intValue = j }; } //Build achievement requirement chains? for (int k = 0; k < ___achievementIdentifiers.Count; k++) { string currentAchievementIdentifier = ___achievementIdentifiers[k]; map[currentAchievementIdentifier].childAchievementIdentifiers = (from v in ___achievementIdentifiers where map[v].prerequisiteAchievementIdentifier == currentAchievementIdentifier select v).ToArray <string>(); } //Let the system know all achievements have been created Action action = ___onAchievementsRegistered; if (action == null) { return(false); } action(); //Skip the original code return(false); }
private static void ReportException(Exception ex) { DetourLogger.LogError(ex.ToString()); }
public static void LogError(object data) { logger.LogError(data); }
private void LoadModsFromDirectories(params string[] modDirectories) { var stopWatch = Stopwatch.StartNew(); // Look for mods, load their manifests var allMods = new List <string>(); foreach (var modDirectory in modDirectories) { if (!modDirectory.IsNullOrWhiteSpace() && Directory.Exists(modDirectory)) { var prevCount = allMods.Count; allMods.AddRange(Directory.GetFiles(modDirectory, "*", SearchOption.AllDirectories) .Where(x => x.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".zipmod", StringComparison.OrdinalIgnoreCase))); Logger.LogInfo("Found " + (allMods.Count - prevCount) + " zipmods in directory: " + modDirectory); } } var archives = allMods.RunParallel(archivePath => { ZipFile archive = null; try { archive = new ZipFile(archivePath); if (Manifest.TryLoadFromZip(archive, out Manifest manifest)) { if (!manifest.Game.IsNullOrWhiteSpace() && !GameNameList.Contains(manifest.Game.ToLower().Replace("!", ""))) { Logger.LogInfo($"Skipping archive \"{GetRelativeArchiveDir(archivePath)}\" because it's meant for {manifest.Game}"); } else { return new { archive, manifest } }; } } catch (Exception ex) { Logger.LogError($"Failed to load archive \"{GetRelativeArchiveDir(archivePath)}\" with error: {ex}"); archive?.Close(); } return(null); }, 3).Where(x => x != null).ToList(); var enableModLoadingLogging = DebugLoggingModLoading.Value; var modLoadInfoSb = enableModLoadingLogging ? new StringBuilder(1000) : null; // Handle duplicate GUIDs and load unique mods foreach (var modGroup in archives.GroupBy(x => x.manifest.GUID).OrderBy(x => x.Key)) { // Order by version if available, else use modified dates (less reliable) // If versions match, prefer mods inside folders or with more descriptive names so modpacks are preferred var orderedModsQuery = modGroup.All(x => !string.IsNullOrEmpty(x.manifest.Version)) ? modGroup.OrderByDescending(x => x.manifest.Version, new ManifestVersionComparer()).ThenByDescending(x => x.archive.Name.Length) : modGroup.OrderByDescending(x => File.GetLastWriteTime(x.archive.Name)); var orderedMods = orderedModsQuery.ToList(); if (orderedMods.Count > 1) { var modList = string.Join(", ", orderedMods.Skip(1).Select(x => '"' + GetRelativeArchiveDir(x.archive.Name) + '"').ToArray()); Logger.LogWarning($"Multiple versions detected, only \"{GetRelativeArchiveDir(orderedMods[0].archive.Name)}\" will be loaded. Skipped versions: {modList}"); // Don't keep the duplicate archives in memory foreach (var dupeMod in orderedMods.Skip(1)) { dupeMod.archive.Close(); } } // Actually load the mods (only one per GUID, the newest one) var archive = orderedMods[0].archive; var manifest = orderedMods[0].manifest; try { Archives.Add(archive); Manifests[manifest.GUID] = manifest; LoadAllUnityArchives(archive, archive.Name); LoadAllLists(archive, manifest); BuildPngFolderList(archive); UniversalAutoResolver.GenerateMigrationInfo(manifest, _gatheredMigrationInfos); #if AI || HS2 UniversalAutoResolver.GenerateHeadPresetInfo(manifest, _gatheredHeadPresetInfos); UniversalAutoResolver.GenerateFaceSkinInfo(manifest, _gatheredFaceSkinInfos); #endif var trimmedName = manifest.Name?.Trim(); var displayName = !string.IsNullOrEmpty(trimmedName) ? trimmedName : Path.GetFileName(archive.Name); if (enableModLoadingLogging) { modLoadInfoSb.AppendLine($"Loaded {displayName} {manifest.Version}"); } } catch (Exception ex) { Logger.LogError($"Failed to load archive \"{GetRelativeArchiveDir(archive.Name)}\" with error: {ex}"); } } UniversalAutoResolver.SetResolveInfos(_gatheredResolutionInfos); UniversalAutoResolver.SetMigrationInfos(_gatheredMigrationInfos); #if AI || HS2 UniversalAutoResolver.SetHeadPresetInfos(_gatheredHeadPresetInfos); UniversalAutoResolver.SetFaceSkinInfos(_gatheredFaceSkinInfos); UniversalAutoResolver.ResolveFaceSkins(); #endif BuildPngOnlyFolderList(); #pragma warning disable CS0618 // Type or member is obsolete LoadedManifests = Manifests.Values.AsEnumerable().ToList(); #pragma warning restore CS0618 // Type or member is obsolete stopWatch.Stop(); if (enableModLoadingLogging) { Logger.LogInfo($"List of loaded mods:\n{modLoadInfoSb}"); } Logger.LogInfo($"Successfully loaded {Archives.Count} mods out of {allMods.Count} archives in {stopWatch.ElapsedMilliseconds}ms"); var failedPaths = allMods.Except(Archives.Select(x => x.Name)); var failedStrings = failedPaths.Select(GetRelativeArchiveDir).ToArray(); if (failedStrings.Length > 0) { Logger.LogWarning("Could not load " + failedStrings.Length + " mods, see previous warnings for more information. File names of skipped archives:\n" + string.Join(" | ", failedStrings)); } }
private void LoadModsFromDirectories(params string[] modDirectories) { Logger.LogInfo("Scanning the \"mods\" directory..."); var stopWatch = Stopwatch.StartNew(); // Look for mods, load their manifests var allMods = new List <string>(); foreach (var modDirectory in modDirectories) { if (!modDirectory.IsNullOrWhiteSpace() && Directory.Exists(modDirectory)) { allMods.AddRange(GetZipmodsFromDirectory(modDirectory)); } } var archives = new Dictionary <ZipFile, Manifest>(); foreach (var archivePath in allMods) { ZipFile archive = null; try { archive = new ZipFile(archivePath); if (Manifest.TryLoadFromZip(archive, out Manifest manifest)) { if (manifest.Game.IsNullOrWhiteSpace() || GameNameList.Contains(manifest.Game.ToLower().Replace("!", ""))) { archives.Add(archive, manifest); } else { Logger.LogInfo($"Skipping archive \"{GetRelativeArchiveDir(archivePath)}\" because it's meant for {manifest.Game}"); } } } catch (Exception ex) { Logger.LogError($"Failed to load archive \"{GetRelativeArchiveDir(archivePath)}\" with error: {ex}"); archive?.Close(); } } var modLoadInfoSb = new StringBuilder(); // Handle duplicate GUIDs and load unique mods foreach (var modGroup in archives.GroupBy(x => x.Value.GUID)) { // Order by version if available, else use modified dates (less reliable) // If versions match, prefer mods inside folders or with more descriptive names so modpacks are preferred var orderedModsQuery = modGroup.All(x => !string.IsNullOrEmpty(x.Value.Version)) ? modGroup.OrderByDescending(x => x.Value.Version, new ManifestVersionComparer()).ThenByDescending(x => x.Key.Name.Length) : modGroup.OrderByDescending(x => File.GetLastWriteTime(x.Key.Name)); var orderedMods = orderedModsQuery.ToList(); if (orderedMods.Count > 1) { var modList = string.Join(", ", orderedMods.Select(x => '"' + GetRelativeArchiveDir(x.Key.Name) + '"').ToArray()); Logger.LogWarning($"Archives with identical GUIDs detected! Archives: {modList}; Only \"{GetRelativeArchiveDir(orderedMods[0].Key.Name)}\" will be loaded because it's the newest"); // Don't keep the duplicate archives in memory foreach (var dupeMod in orderedMods.Skip(1)) { dupeMod.Key.Close(); } } // Actually load the mods (only one per GUID, the newest one) var archive = orderedMods[0].Key; var manifest = orderedMods[0].Value; try { Archives.Add(archive); Manifests[manifest.GUID] = manifest; LoadAllUnityArchives(archive, archive.Name); LoadAllLists(archive, manifest); BuildPngFolderList(archive); UniversalAutoResolver.GenerateMigrationInfo(manifest, _gatheredMigrationInfos); #if AI UniversalAutoResolver.GenerateHeadPresetInfo(manifest, _gatheredHeadPresetInfos); UniversalAutoResolver.GenerateFaceSkinInfo(manifest, _gatheredFaceSkinInfos); #endif var trimmedName = manifest.Name?.Trim(); var displayName = !string.IsNullOrEmpty(trimmedName) ? trimmedName : Path.GetFileName(archive.Name); modLoadInfoSb.AppendLine($"Loaded {displayName} {manifest.Version}"); } catch (Exception ex) { Logger.LogError($"Failed to load archive \"{GetRelativeArchiveDir(archive.Name)}\" with error: {ex}"); } } UniversalAutoResolver.SetResolveInfos(_gatheredResolutionInfos); UniversalAutoResolver.SetMigrationInfos(_gatheredMigrationInfos); #if AI UniversalAutoResolver.SetHeadPresetInfos(_gatheredHeadPresetInfos); UniversalAutoResolver.SetFaceSkinInfos(_gatheredFaceSkinInfos); UniversalAutoResolver.ResolveFaceSkins(); #endif BuildPngOnlyFolderList(); #pragma warning disable CS0618 // Type or member is obsolete LoadedManifests = Manifests.Values.AsEnumerable().ToList(); #pragma warning restore CS0618 // Type or member is obsolete stopWatch.Stop(); if (ModLoadingLogging.Value) { Logger.LogInfo($"List of loaded mods:\n{modLoadInfoSb}"); } Logger.LogInfo($"Successfully loaded {Archives.Count} mods out of {allMods.Count()} archives in {stopWatch.ElapsedMilliseconds}ms"); }
public static void Save(string path, string token) { //這裡用cleanedPath作"_compressed"字串清理 string cleanedPath = path; while (cleanedPath.Contains("_compressed")) { cleanedPath = cleanedPath.Replace("_compressed", ""); } string compressedPath = cleanedPath; if (!SaveLoadCompression.DeleteTheOri.Value) { compressedPath = cleanedPath.Substring(0, cleanedPath.Length - 4) + "_compressed.png"; } //Update Cache string decompressCacheDirName = SaveLoadCompression.CacheDirectory.CreateSubdirectory("Decompressed").FullName; if (!SaveLoadCompression.Enable.Value || !SaveLoadCompression.Notice.Value) { //Clear cache and out File.Delete(Path.Combine(decompressCacheDirName, Path.GetFileName(path))); File.Delete(Path.Combine(decompressCacheDirName, Path.GetFileName(cleanedPath))); File.Delete(Path.Combine(decompressCacheDirName, Path.GetFileName(compressedPath))); return; } File.Copy(path, Path.Combine(decompressCacheDirName, Path.GetFileName(compressedPath)), true); if (cleanedPath != path) { File.Copy(path, cleanedPath, true); Logger.LogDebug($"Clean Path: {cleanedPath}"); } byte[] pngData = MakeWatermarkPic(ImageHelper.LoadPngBytes(path), token, true); byte[] unzipPngData = MakeWatermarkPic(ImageHelper.LoadPngBytes(path), token, false); //New Thread, No Freeze Thread newThread = new Thread(saveThread); newThread.Start(); void saveThread() { Logger.LogInfo("Start Compress"); long newSize = 0; long originalSize = 0; float startTime = Time.time; string TempPath = Path.Combine(SaveLoadCompression.CacheDirectory.CreateSubdirectory("Compressed").FullName, Path.GetFileName(path)); SaveLoadCompression.Progress = ""; try { originalSize = new FileInfo(path).Length; newSize = new PngCompression.PngCompression().Save( path, TempPath, token: token, pngData: pngData, compressProgress: (decimal progress) => SaveLoadCompression.Progress = $"Compressing: {progress:p2}", doComapre: !SaveLoadCompression.SkipSaveCheck.Value, compareProgress: (decimal progress) => SaveLoadCompression.Progress = $"Comparing: {progress:p2}"); //複製或刪除檔案 if (newSize > 0) { LogLevel logLevel = SaveLoadCompression.DisplayMessage.Value ? (LogLevel.Message | LogLevel.Info) : LogLevel.Info; Logger.LogInfo($"Compression test SUCCESS"); Logger.Log(logLevel, $"Compression finish in {Time.time - startTime:n2} seconds"); Logger.Log(logLevel, $"Size compress from {originalSize} bytes to {newSize} bytes"); Logger.Log(logLevel, $"Compress ratio: {Convert.ToDecimal(originalSize) / newSize:n3}/1, which means it is now {Convert.ToDecimal(newSize) / originalSize:p3} big."); //寫入壓縮結果 File.Copy(TempPath, compressedPath, true); Logger.LogDebug($"Write to: {compressedPath}"); //如果壓縮路徑未覆寫,將原始圖檔加上unzip浮水印 if (cleanedPath != compressedPath) { ChangePNG(cleanedPath, unzipPngData); Logger.LogDebug($"Overwrite unzip watermark: {cleanedPath}"); } //如果原始路徑和上二存檔都不相同,刪除之 //因為File.Delete()不是立即執行完畢,不能有「砍掉以後立即在同位置寫入」的操作,所以是這個邏輯順序 //如果相同的話,上方就已經覆寫了;不同的話此處再做刪除 if (path != compressedPath && path != cleanedPath) { File.Delete(path); Logger.LogDebug($"Delete Original File: {path}"); } } else { Logger.LogError($"Compression FAILED"); } } catch (Exception e) { if (e is IOException && newSize > 0) { //覆寫時遇到讀取重整會IOException: Sharing violation on path,這在Compress太快時會發生 //Retry try { if (File.Exists(TempPath)) { if (SaveLoadCompression.DeleteTheOri.Value) { File.Copy(TempPath, path, true); } } } catch (Exception) { //Copy to a new name if failed twice File.Copy(TempPath, path.Substring(0, path.Length - 4) + "_compressed2.png"); Logger.LogError("Overwrite was FAILED twice. Fallback to use the '_compressed2' path."); } } else { Logger.Log(LogLevel.Error | LogLevel.Message, $"An unknown error occurred. If your files are lost, please find them at %TEMP%/{SaveLoadCompression.GUID}"); throw; } } finally { SaveLoadCompression.Progress = ""; if (File.Exists(TempPath)) { File.Delete(TempPath); } } } }
public static void LogError(string message, Exception e = null) => Logger?.LogError($"{message}\n{e?.Message ?? ""}\n{e?.StackTrace ?? ""}");
internal static void LogError(object data) => _logSource.LogError(data);
public IEnumerator CleanTranslationCacheCoroutine() { var reloadCoroutine = CoroutineUtils.CreateCoroutine(() => { }, ReloadTranslations); var cutoff = Time.realtimeSinceStartup + YieldSeconds; var notifyTime = Time.realtimeSinceStartup + NotifySeconds; Logger.LogMessage("Attempting to clean translation cache, please be patient..."); var cache = GeBoAPI.Instance.AutoTranslationHelper.DefaultCache; if (cache == null) { Logger.LogError("Unable to access translation cache"); yield break; } var translations = GeBoAPI.Instance.AutoTranslationHelper.GetTranslations(); if (translations == null) { Logger.LogError("Unable to access translation cache"); yield break; } var regexes = new List <Regex>(); var tmp = GeBoAPI.Instance.AutoTranslationHelper.GetRegisteredRegexes(); if (tmp != null) { regexes.AddRange(tmp.Select(s => new Regex(s))); } tmp = GeBoAPI.Instance.AutoTranslationHelper.GetRegisteredSplitterRegexes(); if (tmp != null) { regexes.AddRange(tmp.Select(s => new Regex(s))); } var newFile = GetWorkFileName(Path.GetDirectoryName(AutoTranslationsFilePath), Path.GetFileName(AutoTranslationsFilePath), "new"); var backupFile = GetWorkFileName(Path.GetDirectoryName(AutoTranslationsFilePath), Path.GetFileName(AutoTranslationsFilePath), "bak"); MoveReplaceFile(AutoTranslationsFilePath, backupFile); _latestBackup = backupFile; Logger.LogInfo("Reloading translations without existing cache file"); yield return(StartCoroutine(reloadCoroutine)); Logger.LogInfo("Reloading done"); char[] splitter = { '=' }; var changed = 0; using (var outStream = File.Open(newFile, FileMode.CreateNew, FileAccess.Write)) using (var writer = new StreamWriter(outStream, Encoding.UTF8)) using (var inStream = File.Open(backupFile, FileMode.Open, FileAccess.Read)) using (var reader = new StreamReader(inStream, Encoding.UTF8)) { string line; while ((line = reader.ReadLine()) != null) { var now = Time.realtimeSinceStartup; if (now > notifyTime) { Logger.LogMessage("Cleaning translation cache..."); notifyTime = now + NotifySeconds; } if (now > cutoff) { cutoff = now + YieldSeconds; yield return(null); } var parts = line.Split(splitter, StringSplitOptions.None); if (parts.Length == 2 && !parts[0].StartsWith("//", StringComparison.InvariantCulture)) { if (translations.ContainsKey(parts[0])) { Logger.LogInfo($"Removing cached line (static match): {line.TrimEnd()}"); changed++; continue; } if (regexes.Any(r => r.IsMatch(parts[0]))) { Logger.LogInfo($"Removing cached line (regex match): {line.TrimEnd()}"); changed++; continue; } } writer.WriteLine(line); } } yield return(null); if (changed > 0) { Logger.LogMessage($"Done. Removed {changed} entries from cache. Reloading translations."); MoveReplaceFile(newFile, AutoTranslationsFilePath); } else { Logger.LogMessage("Done. No changes made. Restoring/reloading translations"); MoveReplaceFile(backupFile, AutoTranslationsFilePath); } _latestBackup = null; yield return(StartCoroutine(reloadCoroutine)); }
private void InitUncensorConfigs <T>(string sex, string part, Dictionary <string, T> uncensorDictionary, string[] defaultChoices, int order, out ConfigEntry <string> defaultConf, out ConfigEntry <string> excludeConf) where T : IUncensorData { byte sexValue = (byte)(sex == "Male" ? 0 : 1); if (!RandomExcludedSets.TryGetValue(sexValue, out var sexExcluded)) { sexExcluded = RandomExcludedSets[sexValue] = new Dictionary <string, HashSet <string> >(StringComparer.OrdinalIgnoreCase); } if (!sexExcluded.TryGetValue(part, out var partExcluded)) { partExcluded = sexExcluded[part] = new HashSet <string>(StringComparer.OrdinalIgnoreCase); } var defaultDescAttrs = new ConfigurationManagerAttributes { Order = order }; var defaultDesc = new ConfigDescription($"{part} to use if the character does not have one set. The {(part == "Body" ? "censored" : "mosaic")} {part.ToLower()} will not be selected randomly if there are any alternatives.", new AcceptableValueList <string>(defaultChoices), defaultDescAttrs); defaultConf = Config.Bind("Config", $"Default {sex} {part}", "Random", defaultDesc); var settingsGUI = new SettingsGUI <T>(uncensorDictionary, sexValue, part); SettingsGUIs.Add(settingsGUI); var excludedDescAttrs = new ConfigurationManagerAttributes { HideDefaultButton = true, CustomDrawer = settingsGUI.DrawSettingsGUI, Order = order }; var excludedDesc = new ConfigDescription($"{part} uncensors to exclude from random selection for {sex.ToLower()}s", null, excludedDescAttrs); var mainExcludeConf = excludeConf = Config.Bind("Random Excluded", $"{sex} {part}", string.Empty, excludedDesc); #if !EC if (SeparateStudioExclusions.Value) { var studioSettingsGUI = new SettingsGUI <T>(uncensorDictionary, sexValue, part, true); SettingsGUIs.Add(studioSettingsGUI); var studioExcludedDescAttrs = new ConfigurationManagerAttributes { HideDefaultButton = true, CustomDrawer = studioSettingsGUI.DrawSettingsGUI, Order = order }; var studioExcludedDesc = new ConfigDescription($"{part} uncensors to exclude from random selection for {sex.ToLower()}s in studio", null, studioExcludedDescAttrs); // default value is main config so the first time it's enabled current settings are copied over var studioExcludeConf = Config.Bind("Random Excluded for Studio", $"{sex} {part}", mainExcludeConf.Value, studioExcludedDesc); // set excludeConf to the active config excludeConf = KKAPI.Studio.StudioAPI.InsideStudio ? studioExcludeConf : mainExcludeConf; } #endif //Apply initial config file value now UpdateGuidSet(excludeConf.Value, partExcluded); //Update on change excludeConf.SettingChanged += (s, a) => { if (!(a is SettingChangedEventArgs args) || !(args.ChangedSetting is ConfigEntry <string> conf)) { Logger.LogError("Unexpected error, unable to handle changed settings."); return; } UpdateGuidSet(conf.Value, partExcluded); }; }