Beispiel #1
0
 public static void Error(string str, params object[] args)
 {
     if (IsGlobalEnabled && IsErrorEnabled)
     {
         LogInstance?.LogError(string.Format(str, args));
     }
 }
Beispiel #2
0
        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);
            }
        }
Beispiel #3
0
 public void LogError(string message)
 {
     _logger.LogError(message);
 }
Beispiel #4
0
 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));
 }
Beispiel #5
0
 public static void LogE(object data) => logger.LogError(data);
Beispiel #6
0
 public static void LogError(string message) => Logger?.LogError($"{message}");
Beispiel #7
0
        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--");
            }
        }
Beispiel #8
0
        //按鈕邏輯
        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);
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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);
            }
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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;
                    }
                }
            }
        }
Beispiel #14
0
        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");
            }
        }
Beispiel #15
0
 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);
        }
Beispiel #18
0
 private static void ReportException(Exception ex)
 {
     DetourLogger.LogError(ex.ToString());
 }
Beispiel #19
0
 public static void LogError(object data)
 {
     logger.LogError(data);
 }
Beispiel #20
0
        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");
        }
Beispiel #22
0
        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);
                    }
                }
            }
        }
Beispiel #23
0
 public static void LogError(string message, Exception e = null) => Logger?.LogError($"{message}\n{e?.Message ?? ""}\n{e?.StackTrace ?? ""}");
Beispiel #24
0
 internal static void LogError(object data) => _logSource.LogError(data);
Beispiel #25
0
        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));
        }
Beispiel #26
0
        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);
            };
        }