public void Update() { // Keypress handlers if (Input.GetKeyDown(KeyCode.F10)) { try { TextTranslator.Reload(); Logger.Log(LogLevel.Info, "Reloaded translations"); } catch (TranslatorException) { Logger.Log(LogLevel.Error, "Failed to reload translations"); } } else if (Input.GetKeyDown(KeyCode.F9)) { TextTranslator.DumpMissingTranslations(); Logger.Log(LogLevel.Info, "Dumped missing translations"); } }
public static bool LoadAssetAsyncPreHook(ref AssetBundleLoadAssetOperation __result, string assetBundleName, string assetName, Type type, string manifestAssetBundleName) { __result = ResourceRedirector.HandleAsset(assetBundleName, assetName, type, manifestAssetBundleName, ref __result); if (__result == null) { if (!File.Exists($"{Application.dataPath}/../abdata/{assetBundleName}")) { //An asset that does not exist is being requested from from an asset bundle that does not exist //Redirect to an asset bundle the does exist so that the game does not attempt to open a non-existant file and cause errors Logger.Log(LogLevel.Warning, $"Asset {assetName} does not exist in asset bundle {assetBundleName}."); assetBundleName = "chara/mt_ramp_00.unity3d"; assetName = "dummy"; } return(true); } else { return(false); } }
/// <summary> /// Called by GUI button click. Unlinks the selected node. /// </summary> private void UnlinkCharacterToObject() { TreeNodeObject[] selectNodes = Singleton <Studio.Studio> .Instance.treeNodeCtrl.selectNodes; bool DidUnlink = false; for (int i = 0; i < selectNodes.Length; i++) { if (Singleton <Studio.Studio> .Instance.dicInfo.TryGetValue(selectNodes[i], out ObjectCtrlInfo objectCtrlInfo)) { if (objectCtrlInfo is OCIChar ociChar) { GetController(ociChar).RemoveLink(IKGuideObjects[SelectedGuideObject]); DidUnlink = true; } } } if (!DidUnlink) { Logger.Log(LogLevel.Info | LogLevel.Message, "Select a character."); } }
public static bool HandleAsset(string assetBundleName, string assetName, Type type, string manifestAssetBundleName, out AssetBundleLoadAssetOperation result) { if (assetName.StartsWith("bgm") && assetName.Length > 4) { int bgmTrack = int.Parse(assetName.Remove(0, 4)); var path = Utility.CombinePaths(Utility.PluginsDirectory, "bgm", $"BGM{bgmTrack:00}.ogg"); if (File.Exists(path)) { Logger.Log(LogLevel.Info, $"Loading BGM track \"{(BGM)bgmTrack}\" from {path}"); result = new AssetBundleLoadAssetOperationSimulation(AudioLoader.LoadVorbis(path)); return(true); } } result = null; return(false); }
private void OnEarlyMakerFinishedLoading(object sender, RegisterCustomControlsEvent e) { _boneController = FindObjectOfType <BoneController>(); if (_boneController == null) { Logger.Log(LogLevel.Error, "[KKABMX_GUI] Failed to find a BoneController or there are no bone modifiers"); return; } _boneController.NewDataLoaded += (s, args) => { foreach (var action in _updateActionList) { action(); } }; gameObject.AddComponent <KKABMX_AdvancedGUI>().enabled = false; RegisterCustomControls(e); }
protected void LoadTranslations() { string translationDir = Path.Combine(Paths.PluginPath, "translation"); if (!Directory.Exists(translationDir)) { Logger.Log(LogLevel.Debug, "Creating translation directory"); Directory.CreateDirectory(translationDir); } else { try { TextTranslator.Initialize(translationDir); } catch (TranslatorException) { Logger.Log(LogLevel.Error, "Unable to initialize translator"); } } }
private IEnumerator StudioLoadCoroutine(List <KeyValuePair <string, PngType> > scenes, List <KeyValuePair <string, PngType> > cards) { if (scenes.Count > 0) { if (scenes.Count > 1) { Logger.Log(LogLevel.Message, "Warning: Only the first scene will be loaded."); } var scene = scenes[0]; try { LoadScene(scene.Key); } catch (Exception ex) { PrintError(ex); } yield return(new WaitForEndOfFrame()); } foreach (var card in cards) { try { LoadSceneCharacter(card.Key); } catch (Exception ex) { PrintError(ex); } yield return(new WaitForEndOfFrame()); } PlaySound(SystemSE.ok_s); }
void ReloadPlugins() { Destroy(scriptManager); scriptManager = new GameObject($"ScriptEngine_{DateTime.Now.Ticks}"); DontDestroyOnLoad(scriptManager); var files = Directory.GetFiles(ScriptDirectory, "*.dll"); if (files.Length > 0) { foreach (string path in Directory.GetFiles(ScriptDirectory, "*.dll")) { LoadDLL(path, scriptManager); } Logger.Log(LogLevel.Message, "Reloaded script plugins!"); } else { Logger.Log(LogLevel.Message, "No plugins to reload"); } }
private static void UntranslateTextAll() { TextHooks.TranslationHooksEnabled = false; var aliveCount = 0; foreach (var kv in OriginalTranslations) { if (!kv.Key.IsAlive) { continue; } aliveCount++; try { switch (kv.Key.Target) { case TMP_Text tmtext: tmtext.text = kv.Value; break; case UnityEngine.UI.Text tmtext: tmtext.text = kv.Value; break; } } catch { // Bloody APIs crashing up my house } } Logger.Log(LogLevel.Message, $"{aliveCount} translations reloaded."); TextHooks.TranslationHooksEnabled = true; }
private void Awake() { ReplacingEnabled = new ConfigWrapper <bool>("ReplacingEnabled", this, true); DumpingEnabled = new ConfigWrapper <bool>("DumpingEnabled", this, true); _assetsPath = Path.Combine(Paths.PluginPath, "Assets"); if (!Directory.Exists(_assetsPath)) { try { Logger.Log(LogLevel.Info, "No Assets directory, creating..."); Directory.CreateDirectory(_assetsPath); } catch (Exception) { Logger.Log(LogLevel.Error, "Error creating Assets directory " + _assetsPath); throw; } } Load(); }
void UntranslateAll() { Hooks.TranslationHooksEnabled = false; int i = 0; foreach (var kv in originalTranslations) { if (kv.Key.IsAlive) { i++; if (kv.Key.Target is TMP_Text) { TMP_Text tmtext = (TMP_Text)kv.Key.Target; tmtext.text = kv.Value; } else if (kv.Key.Target is TextMeshProUGUI) { TextMeshProUGUI tmtext = (TextMeshProUGUI)kv.Key.Target; tmtext.text = kv.Value; } else if (kv.Key.Target is UnityEngine.UI.Text) { UnityEngine.UI.Text tmtext = (UnityEngine.UI.Text)kv.Key.Target; tmtext.text = kv.Value; } } } Logger.Log(LogLevel.Message, $"{i} translations reloaded."); Hooks.TranslationHooksEnabled = true; }
private static IEnumerable <ReadonlyCacheEntry> GetInstanceClassScanner() { Logger.Log(LogLevel.Debug, "[CheatTools] Looking for class instances..."); var query = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(x => { try { return(x.GetTypes()); } catch (SystemException) { return(Enumerable.Empty <Type>()); } }) .Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters); foreach (var type in query) { object obj = null; try { obj = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) ?.GetValue(null, null); } catch (Exception ex) { Logger.Log(LogLevel.Debug, ex.ToString()); } if (obj != null) { yield return(new ReadonlyCacheEntry(type.Name + ".Instance", obj)); } } }
protected void LoadAllLists(ZipFile arc, Manifest manifest) { foreach (ZipEntry entry in arc) { if (entry.Name.StartsWith("abdata/list/characustom", StringComparison.OrdinalIgnoreCase) && entry.Name.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)) { try { var stream = arc.GetInputStream(entry); var chaListData = ListLoader.LoadCSV(stream); SetPossessNew(chaListData); UniversalAutoResolver.GenerateResolutionInfo(manifest, chaListData); IndexList(manifest, chaListData); ListLoader.ExternalDataList.Add(chaListData); if (LoadedData.TryGetValue(manifest, out lists)) { lists.Add(chaListData); } else { LoadedData[manifest] = new List <ChaListData> { chaListData }; } } catch (SystemException ex) { Logger.Log(LogLevel.Error, $"[SIDELOADER] Failed to load list file \"{entry.Name}\" from archive \"{arc.Name}\" with error: {ex.Message}"); Logger.Log(LogLevel.Debug, $"[SIDELOADER] Error details: {ex}"); } } } }
private void Update() { if (_bytesToLoad != null) { try { var tex = Util.TextureFromBytes(_bytesToLoad); var origTex = GetOverlayController().GetApplicableRenderers(_typeToLoad).First().material.mainTexture; if (tex.width != origTex.width || tex.height != origTex.height) { Logger.Log(LogLevel.Message | LogLevel.Warning, $"[KCOX] WARNING - Wrong texture resolution! It's recommended to use {origTex.width}x{origTex.height} instead."); } else { Logger.Log(LogLevel.Message, "[KCOX] Texture imported successfully"); } SetTexAndUpdate(new ClothesTexData { Override = _hideMainToLoad, Texture = tex }, _typeToLoad); } catch (Exception ex) { _lastError = ex; } _bytesToLoad = null; } if (_lastError != null) { Logger.Log(LogLevel.Error | LogLevel.Message, "[KCOX] Failed to load texture from file - " + _lastError.Message); Logger.Log(LogLevel.Debug, _lastError); _lastError = null; } }
private void ReplaceImages() { UnityEngine.Object[] textures = Resources.FindObjectsOfTypeAll(typeof(Texture2D)); foreach (UnityEngine.Object t in textures) { try { Texture2D tex = (Texture2D)t; if (images.ContainsKey(t.name)) { Logger.Log(LogLevel.Debug, "Replacing Image: " + tex.name); byte[] fileData = File.ReadAllBytes(images[t.name]); tex.LoadImage(fileData); } } catch (Exception ex) { Logger.Log(LogLevel.Error, ex.ToString()); } } Resources.UnloadUnusedAssets(); }
private void Update() { if ((Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) && Input.GetKey(TranslationSyncHotkey.Value.MainKey)) { SyncTLs(TLType.Scenario, true); SyncTLs(TLType.Communication, true); SyncTLs(TLType.H, true); Logger.Log(LogLevel.Info, "Sync complete."); } else if (TranslationSyncHotkey.IsDown()) { //CountText(); SyncTLs(TLType.Scenario); SyncTLs(TLType.Communication); SyncTLs(TLType.H); Logger.Log(LogLevel.Info, "Sync complete."); } if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && Input.GetKey(TranslationSyncHotkey.Value.MainKey)) { for (int i = 0; i <= 37; i++) { Personality.Value = "c" + i.ToString("00"); SyncTLs(TLType.Scenario); SyncTLs(TLType.Communication); SyncTLs(TLType.H); } for (int i = 0; i <= 10; i++) { Personality.Value = "c-" + i.ToString("00"); SyncTLs(TLType.Scenario); SyncTLs(TLType.Communication); SyncTLs(TLType.H); } Logger.Log(LogLevel.Info, "Sync complete."); } }
public static void LoadTextTranslations(string dirTranslation) { Logger.Log(LogLevel.Debug, "Loading all translations"); //TODO: load .bin files here Logger.Log(LogLevel.Debug, $"Loaded {TLArchives.Count} archives"); TLArchives.Clear(); var dirTranslationText = Path.Combine(dirTranslation, "Text"); if (!Directory.Exists(dirTranslationText)) { Directory.CreateDirectory(dirTranslationText); } try { TLArchives.Add(new MarkupCompiler().CompileArchive(dirTranslationText)); Logger.Log(LogLevel.Debug, $"Loaded {TLArchives.Last().Sections.Sum(x => x.Lines.Count)} lines from text"); } catch (Exception ex) { Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to load translations from text!"); Logger.Log(LogLevel.Error, ex); } }
private static void LoadTranslationsFromFolder(string FolderPath) { Logger.Log(LogLevel.Debug, $"Loading translations from {FolderPath}"); FolderTranslations.Clear(); regexFolderTranslations.Clear(); if (Directory.Exists(FolderPath)) { try { var arc = new MarkupCompiler().CompileArchive(FolderPath); foreach (Section section in arc.Sections) { foreach (var line in section.Lines) { if (line.Flags.IsOriginalRegex) { regexFolderTranslations[new Regex(line.OriginalLine)] = line; } else { FolderTranslations[line.OriginalLine] = line; } } } Logger.Log(LogLevel.Debug, $"Loaded {FolderTranslations.Count} lines from text"); } catch (Exception ex) { Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to load translations from text!"); Logger.Log(LogLevel.Error, ex); } } }
public Sideloader() { //ilmerge AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { if (args.Name == "I18N, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" || args.Name == "I18N.West, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") { return(Assembly.GetExecutingAssembly()); } return(null); }; //install hooks Hooks.InstallHooks(); AutoResolver.Hooks.InstallHooks(); ResourceRedirector.ResourceRedirector.AssetResolvers.Add(RedirectHook); ResourceRedirector.ResourceRedirector.AssetBundleResolvers.Add(AssetBundleRedirectHook); MissingModWarning = new ConfigWrapper <bool>("MissingModWarning", this, true); DebugLogging = new ConfigWrapper <bool>("DebugLogging", this, false); DebugResolveInfoLogging = new ConfigWrapper <bool>("DebugResolveInfoLogging", this, false); KeepMissingAccessories = new ConfigWrapper <bool>("KeepMissingAccessories", this, false); //check mods directory var modDirectory = Path.Combine(Paths.GameRootPath, "mods"); if (!Directory.Exists(modDirectory)) { Logger.Log(LogLevel.Warning, "[SIDELOADER] Could not find the \"mods\" directory"); return; } LoadModsFromDirectory(modDirectory); }
private void GetImages() { if (!Directory.Exists(ImagesPath)) { return; } try { Logger.Log(LogLevel.Debug, "Fetching Images..."); foreach (string file in Directory.GetFiles(ImagesPath)) { if (Path.GetExtension(file).Equals(".png", StringComparison.OrdinalIgnoreCase)) { images.Add(Path.GetFileNameWithoutExtension(file), file); } } } catch (Exception ex) { Logger.Log(LogLevel.Error, ex.ToString()); } }
private void Start() { if (KoikatuAPI.CheckIncompatiblePlugin(this, "koikatsu.cartoonuncensor", LogLevel.Error)) { Logger.Log(LogLevel.Error | LogLevel.Message, "CartoonUncensor.dll is incompatible with KK_UncensorSelector! Please remove it and restart the game."); return; } if (KoikatuAPI.CheckIncompatiblePlugin(this, "koikatsu.alexaebubblegum", LogLevel.Error)) { Logger.Log(LogLevel.Error | LogLevel.Message, "AlexaeBubbleGum.dll is incompatible with KK_UncensorSelector! Please remove it and restart the game."); return; } var harmony = HarmonyInstance.Create(GUID); harmony.PatchAll(typeof(Hooks)); Type loadAsyncIterator = typeof(ChaControl).GetNestedTypes(AccessTools.all).First(x => x.Name.StartsWith("<LoadAsync>c__Iterator")); MethodInfo loadAsyncIteratorMoveNext = loadAsyncIterator.GetMethod("MoveNext"); harmony.Patch(loadAsyncIteratorMoveNext, null, null, new HarmonyMethod(typeof(Hooks).GetMethod(nameof(Hooks.LoadAsyncTranspiler), BindingFlags.Static | BindingFlags.Public))); PopulateUncensorLists(); MakerAPI.RegisterCustomSubCategories += MakerAPI_RegisterCustomSubCategories; MakerAPI.MakerFinishedLoading += MakerAPI_MakerFinishedLoading; CharacterApi.RegisterExtraBehaviour <UncensorSelectorController>(GUID); GenderBender = new ConfigWrapper <bool>(nameof(GenderBender), PluginNameInternal, true); DefaultMaleBody = new ConfigWrapper <string>(nameof(DefaultMaleBody), PluginNameInternal, BodyGuidToDisplayName, DisplayNameToBodyGuid, MaleBodyDefaultValue); DefaultMalePenis = new ConfigWrapper <string>(nameof(DefaultMalePenis), PluginNameInternal, MalePenisGuidToDisplayName, DisplayNameToPenisGuid, MalePenisDefaultValue); DefaultMaleBalls = new ConfigWrapper <string>(nameof(DefaultMaleBalls), PluginNameInternal, MaleBallsGuidToDisplayName, DisplayNameToBallsGuid, MaleBallsDefaultValue); DefaultFemaleBody = new ConfigWrapper <string>(nameof(DefaultFemaleBody), PluginNameInternal, BodyGuidToDisplayName, DisplayNameToBodyGuid, FemaleBodyDefaultValue); DefaultFemalePenis = new ConfigWrapper <string>(nameof(DefaultFemalePenis), PluginNameInternal, FemalePenisGuidToDisplayName, DisplayNameToPenisGuid, FemalePenisDefaultValue); DefaultFemaleBalls = new ConfigWrapper <string>(nameof(DefaultFemaleBalls), PluginNameInternal, FemaleBallsGuidToDisplayName, DisplayNameToBallsGuid, FemaleBallsDefaultValue); }
public IEnumerator DownloadSubs() { string cache = Path.Combine(Paths.PluginPath, "hsubs.msgpack"); if (File.Exists(cache)) { subtitlesDict = LZ4MessagePackSerializer.Deserialize <Dictionary <string, string> >(File.ReadAllBytes(cache)); Logger.Log(LogLevel.Info, "Found cached hsubs"); } Logger.Log(LogLevel.Info, "Downloading subs from " + SHEET_KEY); var dl = new WWW($"https://docs.google.com/spreadsheets/d/{SHEET_KEY}/export?format=csv"); while (!dl.isDone) { yield return(dl); } if (dl.error != null) { Logger.Log(LogLevel.Warning, "Failed to fetch latest subtitles. Going to use cached ones."); yield break; } Logger.Log(LogLevel.Info, $"Downloaded {dl.bytesDownloaded} bytes. Parsing..."); int cnt = 0; foreach (IEnumerable <string> row in ParseCSV(dl.text)) { int idx = 0; string sound = null; string tl = null; foreach (string cell in row) { if (idx == 0) { sound = cell.ToLower(); } if (idx == 2) { tl = cell; } idx++; } if (sound != null && tl != null && sound.Length < 64) { cnt++; subtitlesDict[sound] = tl; } } Logger.Log(LogLevel.Info, $"Done parsing subtitles: {cnt} lines found."); if (cnt > 60000) { File.WriteAllBytes(cache, LZ4MessagePackSerializer.Serialize(subtitlesDict)); } else { Logger.Log(LogLevel.Warning, "The amount of lines is suspiciously low (defaced sheet?); not caching."); } }
/// <summary> /// Called by the scene controller, loads animations from the loaded or imported scene /// </summary> internal void LoadAnimations(int characterDicKey, ReadOnlyDictionary <int, ObjectCtrlInfo> loadedItems) { try { PluginData ExtendedData = ExtendedSave.GetSceneExtendedDataById(PluginNameInternal); //Version 1 save data if (ExtendedData?.data != null && ExtendedData.data.ContainsKey("AnimationInfo")) { List <AnimationControllerInfo> AnimationControllerInfoList = ((object[])ExtendedData.data["AnimationInfo"]).Select(x => AnimationControllerInfo.Unserialize((byte[])x)).ToList(); foreach (var AnimInfo in AnimationControllerInfoList) { //See if this is the right character if (AnimInfo.CharDicKey != characterDicKey) { continue; } ObjectCtrlInfo linkedItem = loadedItems[AnimInfo.ItemDicKey]; if (AnimInfo.Version.IsNullOrEmpty()) { AddLinkV1(AnimInfo.IKPart, linkedItem); } else { AddLink(AnimInfo.IKPart, linkedItem); } } } //Version 2 save data else { var data = GetExtendedData(); if (data?.data != null) { if (data.data.TryGetValue("LinksV1", out var loadedLinksV1) && loadedLinksV1 != null) { foreach (var link in (Dictionary <object, object>)loadedLinksV1) { AddLinkV1((string)link.Key, loadedItems[(int)link.Value]); } } if (data.data.TryGetValue("Links", out var loadedLinks) && loadedLinks != null) { foreach (var link in (Dictionary <object, object>)loadedLinks) { AddLink((string)link.Key, loadedItems[(int)link.Value]); } } if (data.data.TryGetValue("Eyes", out var loadedEyeLink) && loadedEyeLink != null) { AddEyeLink(loadedItems[(int)loadedEyeLink]); } if (data.data.TryGetValue("Neck", out var loadedNeckLink) && loadedNeckLink != null) { AddNeckLink(loadedItems[(int)loadedNeckLink]); } } } Logger.Log(LogLevel.Debug, $"Loaded KK_AnimationController animations for character {ChaControl.chaFile.parameter.fullname.Trim()}"); } catch (Exception ex) { Logger.Log(LogLevel.Error | LogLevel.Message, "Could not load KK_AnimationController animations."); Logger.Log(LogLevel.Error, ex.ToString()); } }
void Start() { string managedDir = Path.Combine(Utility.ExecutingDirectory, $@"{System.Diagnostics.Process.GetCurrentProcess().ProcessName}_Data\Managed"); if (File.Exists(Path.Combine(managedDir, "IllusionPlugin.dll"))) { Logger.Log(LogLevel.Error | LogLevel.Message, "IPA has been detected to be installed! IPALoader may not function correctly!"); } if (!Directory.Exists(IPAPluginDir)) { Logger.Log(LogLevel.Message, "No IPA plugin directory, skipping load"); return; } if (IPAManagerObject != null) { Destroy(IPAManagerObject); } IPAManagerObject = new GameObject("IPA_Manager"); DontDestroyOnLoad(IPAManagerObject); IPAManagerObject.SetActive(false); Logger.Log(LogLevel.Info, "Loading IPA plugins"); foreach (string path in Directory.GetFiles(IPAPluginDir, "*.dll")) { try { var assembly = Assembly.LoadFile(path); foreach (Type t in assembly.GetTypes()) { if (t.IsAbstract || t.IsInterface) { continue; } if (t.Name == "CompositePlugin") { continue; } if (typeof(IPlugin).IsAssignableFrom(t)) { pluginToLoad = (IPlugin)Activator.CreateInstance(t); Component c = Chainloader.ManagerObject.AddComponent <IPAPlugin>(); Logger.Log(LogLevel.Info, $"Loaded IPA plugin [{pluginToLoad.Name}]"); pluginToLoad = null; } } } catch (Exception ex) { Logger.Log(LogLevel.Error | LogLevel.Message, $"Error loading IPA plugin {Path.GetFileName(path)}"); Logger.Log(LogLevel.Error, ex.ToString()); } } IPAManagerObject.SetActive(true); }
/// <summary> /// Look through all the GUIDs, compare it to the MigrationInfoList, and do migration when necessary /// </summary> private static IEnumerable <ResolveInfo> MigrateGUID(IEnumerable <ResolveInfo> extInfo, string characterName) { List <ResolveInfo> extInfoNew = new List <ResolveInfo>(); bool DidBlankGUIDMessage = false; try { if (extInfo == null) { return(extInfo); } foreach (ResolveInfo resolveInfo in extInfo) { if (resolveInfo.GUID.IsNullOrEmpty()) { //Don't add empty GUID to the new list, this way CompatibilityResolve will treat it as a hard mod and attempt to find a match if (!DidBlankGUIDMessage) //No need to spam it for every single thing { Logger.Log(LogLevel.Warning | LogLevel.Message, $"[{characterName}] Blank GUID detected, attempting Compatibility Resolve"); DidBlankGUIDMessage = true; } } else { string propertyWithoutPrefix = resolveInfo.Property; //Remove outfit and accessory prefixes for searching purposes if (propertyWithoutPrefix.StartsWith("outfit")) { propertyWithoutPrefix = propertyWithoutPrefix.Remove(0, propertyWithoutPrefix.IndexOf('.') + 1); } if (propertyWithoutPrefix.StartsWith("accessory")) { propertyWithoutPrefix = propertyWithoutPrefix.Remove(0, propertyWithoutPrefix.IndexOf('.') + 1); } MigrationInfo info = MigrationInfoList.Where(x => (x.Property == propertyWithoutPrefix && x.OldID == resolveInfo.Slot && x.OldGUID == resolveInfo.GUID) || (x.Property == "*" && x.OldGUID == resolveInfo.GUID) || (x.Property == "-" && x.OldGUID == resolveInfo.GUID)).FirstOrDefault(); if (info == null) { //This item does not need to be migrated extInfoNew.Add(resolveInfo); } else if (info.Property == "*") //* assumes only the GUID changed while the IDs stayed the same { ResolveInfo GUIDCheckOld = UniversalAutoResolver.LoadedResolutionInfo.FirstOrDefault(x => x.GUID == resolveInfo.GUID); if (GUIDCheckOld == null) { //We do not have the old mod installed, do migration. Whether we have the new mod is irrelevant. //If we don't have the new mod the user will get a missing mod warning for the new mod since they should be using that instead. //If we do it will load correctly. Logger.Log(LogLevel.Info, $"Migrating GUID {info.OldGUID} -> {info.NewGUID}"); ResolveInfo resolveInfoNew = new ResolveInfo(); resolveInfoNew = resolveInfo; resolveInfoNew.GUID = info.NewGUID; extInfoNew.Add(resolveInfoNew); } else { ResolveInfo GUIDCheckNew = UniversalAutoResolver.LoadedResolutionInfo.FirstOrDefault(x => x.GUID == info.NewGUID); if (GUIDCheckNew == null) { //We have the old mod but not the new, do not do migration extInfoNew.Add(resolveInfo); } else { //We have the old mod and the new, do migration so characters save with the new stuff Logger.Log(LogLevel.Info, $"Migrating GUID {info.OldGUID} -> {info.NewGUID}"); ResolveInfo resolveInfoNew = new ResolveInfo(); resolveInfoNew = resolveInfo; resolveInfoNew.GUID = info.NewGUID; extInfoNew.Add(resolveInfoNew); } } } else if (info.Property == "-") //- indicates the entry needs to be stripped of its extended data and loaded as a hard mod { continue; } else { ResolveInfo intResolveOld = UniversalAutoResolver.LoadedResolutionInfo.FirstOrDefault(x => x.Property == propertyWithoutPrefix && x.Slot == resolveInfo.Slot && x.GUID == resolveInfo.GUID); if (intResolveOld == null) { //We do not have the old mod installed, do migration. Whether we have the new mod is irrelevant. //If we don't have the new mod the user will get a missing mod warning for the new mod since they should be using that instead. //If we do it will load correctly. Logger.Log(LogLevel.Info, $"Migrating {info.OldGUID}:{info.OldID} -> {info.NewGUID}:{info.NewID}"); ResolveInfo resolveInfoNew = new ResolveInfo(); resolveInfoNew = resolveInfo; resolveInfoNew.GUID = info.NewGUID; resolveInfoNew.Slot = info.NewID; extInfoNew.Add(resolveInfoNew); } else { ResolveInfo intResolveNew = UniversalAutoResolver.LoadedResolutionInfo.FirstOrDefault(x => x.Property == propertyWithoutPrefix && x.Slot == info.NewID && x.GUID == info.NewGUID); if (intResolveNew == null) { //We have the old mod but not the new, do not do migration extInfoNew.Add(resolveInfo); } else { //We have the old mod and the new, do migration so characters save with the new stuff Logger.Log(LogLevel.Warning, $"Migrating {info.OldGUID}:{info.OldID} -> {info.NewGUID}:{info.NewID}"); ResolveInfo b = new ResolveInfo(); b = resolveInfo; b.GUID = info.NewGUID; b.Slot = info.NewID; extInfoNew.Add(b); } } } } } extInfo = extInfoNew; } catch (Exception ex) { //If something goes horribly wrong, return the original extInfo Logger.Log(LogLevel.Error, $"GUID migration cancelled due to error: {ex}"); return(extInfo); } return(extInfoNew); }
/// <summary> /// Read all the manifest.xml files and generate a dictionary of uncensors to be used in config manager dropdown /// </summary> private static void PopulateUncensorLists() { BodyDictionary.Clear(); BodyConfigListFull.Clear(); PenisDictionary.Clear(); PenisConfigListFull.Clear(); BallsDictionary.Clear(); BallsConfigListFull.Clear(); //Add the default body options BodyConfigListFull.Add("None (censored)", UncensorKeyNone); BodyConfigListFull.Add("Random", UncensorKeyRandom); BodyData DefaultMale = new BodyData(0, "Default.Body.Male", "Default Body M"); BodyDictionary.Add(DefaultMale.BodyGUID, DefaultMale); BodyConfigListFull.Add($"[{(DefaultMale.Sex == 0 ? "Male" : "Female")}] {DefaultMale.DisplayName}", DefaultMale.BodyGUID); BodyData DefaultFemale = new BodyData(1, "Default.Body.Female", "Default Body F"); BodyDictionary.Add(DefaultFemale.BodyGUID, DefaultFemale); BodyConfigListFull.Add($"[{(DefaultFemale.Sex == 0 ? "Male" : "Female")}] {DefaultFemale.DisplayName}", DefaultFemale.BodyGUID); //Add the default penis options PenisConfigListFull.Add("None", UncensorKeyNone); PenisConfigListFull.Add("Random", UncensorKeyRandom); PenisData DefaultPenis = new PenisData("Default.Penis", "Mosaic Penis"); PenisDictionary.Add(DefaultPenis.PenisGUID, DefaultPenis); PenisConfigListFull.Add(DefaultPenis.DisplayName, DefaultPenis.PenisGUID); //Add the default balls options BallsConfigListFull.Add("None", UncensorKeyNone); BallsConfigListFull.Add("Random", UncensorKeyRandom); BallsData DefaultBalls = new BallsData("Default.Balls", "Mosaic Balls"); BallsDictionary.Add(DefaultBalls.BallsGUID, DefaultBalls); BallsConfigListFull.Add(DefaultBalls.DisplayName, DefaultBalls.BallsGUID); foreach (var manifest in Sideloader.Sideloader.LoadedManifests) { XDocument manifestDocument = manifest.manifestDocument; XElement uncensorSelectorElement = manifestDocument?.Root?.Element(PluginNameInternal); if (uncensorSelectorElement != null && uncensorSelectorElement.HasElements) { foreach (XElement uncensorElement in uncensorSelectorElement.Elements("body")) { BodyData bodyData = new BodyData(uncensorElement); if (bodyData.BodyGUID == null) { Logger.Log(LogLevel.Warning, "Body failed to load due to missing GUID."); continue; } if (bodyData.DisplayName == null) { Logger.Log(LogLevel.Warning, "Body failed to load due to missing display name."); continue; } if (bodyData.OOBase == Defaults.OOBase) { Logger.Log(LogLevel.Warning, "Body was not loaded because oo_base is the default."); continue; } BodyDictionary.Add(bodyData.BodyGUID, bodyData); BodyConfigListFull.Add($"[{(bodyData.Sex == 0 ? "Male" : "Female")}] {bodyData.DisplayName}", bodyData.BodyGUID); foreach (var part in bodyData.AdditionalParts) { AllAdditionalParts.Add(part); } } foreach (XElement uncensorElement in uncensorSelectorElement.Elements("penis")) { PenisData penisData = new PenisData(uncensorElement); if (penisData.PenisGUID == null) { Logger.Log(LogLevel.Warning, "Penis failed to load due to missing GUID."); continue; } if (penisData.DisplayName == null) { Logger.Log(LogLevel.Warning, "Penis failed to load due to missing display name."); continue; } if (penisData.File == null) { Logger.Log(LogLevel.Warning, "Penis failed to load due to missing file."); continue; } if (penisData.Asset == null) { Logger.Log(LogLevel.Warning, "Penis failed to load due to missing asset."); continue; } PenisDictionary.Add(penisData.PenisGUID, penisData); PenisConfigListFull.Add(penisData.DisplayName, penisData.PenisGUID); } foreach (XElement uncensorElement in uncensorSelectorElement.Elements("balls")) { BallsData ballsData = new BallsData(uncensorElement); if (ballsData.BallsGUID == null) { Logger.Log(LogLevel.Warning, "Balls failed to load due to missing GUID."); continue; } if (ballsData.DisplayName == null) { Logger.Log(LogLevel.Warning, "Balls failed to load due to missing display name."); continue; } if (ballsData.File == null) { Logger.Log(LogLevel.Warning, "Balls failed to load due to missing file."); continue; } if (ballsData.Asset == null) { Logger.Log(LogLevel.Warning, "Balls failed to load due to missing asset."); continue; } BallsDictionary.Add(ballsData.BallsGUID, ballsData); BallsConfigListFull.Add(ballsData.DisplayName, ballsData.BallsGUID); } foreach (XElement uncensorElement in uncensorSelectorElement.Elements("migration")) { MigrationData migrationData = new MigrationData(uncensorElement); if (migrationData.UncensorGUID == null) { Logger.Log(LogLevel.Warning, "Migration data failed to load due to missing Uncensor GUID."); continue; } if (migrationData.BodyGUID == null) { Logger.Log(LogLevel.Warning, "Migration data failed to load due to missing Body GUID."); continue; } MigrationDictionary.Add(migrationData.UncensorGUID, migrationData); } } } }
private static object DoReplace(TextAsset asset, ReplaceType replaceType) { object obj = null; var text = ""; if (replaceType != ReplaceType.Text) { if (replaceType == ReplaceType.Bytes) { try { RestoreMethodStart(_originalBytesLocation, _originalBytesCode); obj = asset.bytes; Memory.WriteJump(_originalBytesLocation, _replacementBytesLocation); text = "bytes"; } catch (Exception ex) { Logger.Log(LogLevel.Error, "Error getting bytes"); LogException(ex); throw; } } } else { try { RestoreMethodStart(_originalTextLocation, _originalTextCode); obj = asset.text; Memory.WriteJump(_originalTextLocation, _replacementTextLocation); text = "text"; } catch (Exception ex2) { Logger.Log(LogLevel.Error, "Error getting text"); LogException(ex2); throw; } } var name = Path.Combine(_assetsPath, asset.name); var replacementFilePath = name + ReplacementFileExtension; if (File.Exists(replacementFilePath)) { if (ReplacingEnabled.Value) { Logger.Log(LogLevel.Info, "Replacing " + text + " in " + name); try { if (replaceType != ReplaceType.Text) { if (replaceType == ReplaceType.Bytes) { obj = File.ReadAllBytes(replacementFilePath); } } else { obj = File.ReadAllText(replacementFilePath, Encoding.UTF8); } } catch (Exception e) { Logger.Log(LogLevel.Error, string.Concat("Error replacing ", text, " in ", name, ", using original")); LogException(e); } } } else { if (DumpingEnabled.Value) { var dumpFilePath = name + DumpFileExtension; if (!File.Exists(dumpFilePath)) { Logger.Log(LogLevel.Info, "File " + replacementFilePath + " not found, creating 'found' file"); try { if (replaceType != ReplaceType.Text) { if (replaceType == ReplaceType.Bytes) { File.WriteAllBytes(dumpFilePath, obj as byte[] ?? throw new InvalidOperationException()); } } else { File.WriteAllText(dumpFilePath, obj as string, Encoding.UTF8); } } catch (Exception e2) { Logger.Log(LogLevel.Error, "Error creating file: " + dumpFilePath); LogException(e2); } } if (File.Exists(replacementFilePath)) { obj = DoReplace(asset, replaceType); } } } return(obj); }
private void LoadModsFromDirectory(string modDirectory) { string GetRelativeArchiveDir(string archiveDir) { return(archiveDir.Length < modDirectory.Length ? archiveDir : archiveDir.Substring(modDirectory.Length).Trim(' ', '/', '\\')); } Logger.Log(LogLevel.Info, "[SIDELOADER] Scanning the \"mods\" directory..."); // Look for mods, load their manifests var allMods = Directory.GetFiles(modDirectory, "*", SearchOption.AllDirectories) .Where(x => x.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".zipmod", StringComparison.OrdinalIgnoreCase)); 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)) { archives.Add(archive, manifest); } } catch (Exception ex) { Logger.Log(LogLevel.Error, $"[SIDELOADER] Failed to load archive \"{GetRelativeArchiveDir(archivePath)}\" with error: {ex.Message}"); Logger.Log(LogLevel.Debug, $"[SIDELOADER] Error details: {ex}"); archive?.Close(); } } // Handlie 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.Log(LogLevel.Warning, $"[SIDELOADER] Archives with identical GUIDs detected! Archives: {modList}"); Logger.Log(LogLevel.Warning, $"[SIDELOADER] 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); LoadedManifests.Add(manifest); LoadAllUnityArchives(archive, archive.Name); LoadAllLists(archive, manifest); BuildPngFolderList(archive); var trimmedName = manifest.Name?.Trim(); var displayName = !string.IsNullOrEmpty(trimmedName) ? trimmedName : Path.GetFileName(archive.Name); Logger.Log(LogLevel.Info, $"[SIDELOADER] Loaded {displayName} {manifest.Version ?? ""}"); } catch (Exception ex) { Logger.Log(LogLevel.Error, $"[SIDELOADER] Failed to load archive \"{GetRelativeArchiveDir(archive.Name)}\" with error: {ex.Message}"); Logger.Log(LogLevel.Debug, $"[SIDELOADER] Error details: {ex}"); } } BuildPngOnlyFolderList(); }
private void LoadTranslations() { translations.Clear(); var dirTranslation = Path.Combine(Utility.PluginsDirectory, "translation"); var dirTranslationText = Path.Combine(dirTranslation, "Text"); if (!Directory.Exists(dirTranslationText)) { Directory.CreateDirectory(dirTranslationText); } var translation = Directory.GetFiles(dirTranslationText, "*.txt", SearchOption.AllDirectories) .SelectMany(File.ReadAllLines) .ToArray(); foreach (var line in translation) { if (!line.Contains('=')) { continue; } var split = line.Split('='); if (split.Length != 2) { Logger.Log(LogLevel.Warning, "Invalid text translation entry: " + line); continue; } translations[split[0].Trim()] = split[1]; } //ITL var di_tl = new DirectoryInfo(Path.Combine(dirTranslation, "Images")); TL_DIR_ROOT = $"{di_tl.FullName}/{Application.productName}"; TL_DIR_SCENE = $"{TL_DIR_ROOT}/Scenes"; var di = new DirectoryInfo(TL_DIR_SCENE); if (!di.Exists) { di.Create(); } foreach (var t in new DirectoryInfo(TL_DIR_ROOT).GetFiles("*.txt")) { var sceneName = Path.GetFileNameWithoutExtension(t.Name); textureLoadTargets[sceneName] = new Dictionary <string, byte[]>(); foreach (var tl in File.ReadAllLines(t.FullName)) { var tp = tl.Split('='); if (tp.Length != 2) { Logger.Log(LogLevel.Warning, "Invalid entry in " + t.Name + " - " + tl); continue; } var path = $"{TL_DIR_SCENE}/{tp[1]}"; if (!File.Exists(path)) { Logger.Log(LogLevel.Warning, "Missing TL image: " + path); continue; } textureLoadTargets[sceneName][tp[0]] = File.ReadAllBytes(path); } } GlobalTextureTargetExists = textureLoadTargets.ContainsKey(GlobalTextureTargetName); SceneManager.sceneUnloaded += s => { if (IsDumpingEnabled.Value) { var sn = DumpingAllToGlobal.Value ? GlobalTextureTargetName : s.name; if (sw_textureNameDump.TryGetValue(sn, out var sw)) { sw.Flush(); } } }; }
/// <summary> /// Sets the visibility state of a character. If no optional parameters are set the character's visiblity state will be read from the character file and set from that. /// </summary> /// <param name="chaControl">Character for which to set visible state.</param> /// <param name="toggleVisible">Toggles the character from visible to invisible and vice versa. Not used if forceVisible is set.</param> /// <param name="forceVisible">Forces the character to the state set in forceVisibleState. Overrides default visibility state and toggleVisible.</param> /// <param name="forceVisibleState">The visibility state to set a character. Only used if forceVisible is set.</param> /// <param name="saveVisibleState">Whether or not the visible state should be saved to the card.</param> private static void SetVisibleState(ChaControl chaControl, bool toggleVisible = false, bool forceVisible = false, bool forceVisibleState = false, bool saveVisibleState = true) { bool Visible; PluginData ExtendedData = ExtendedSave.GetExtendedDataById(chaControl.chaFile, "KK_InvisibleBody"); GameObject CharacterObject = GameObject.Find(chaControl.name); if (ExtendedData == null) { Logger.Log(LogLevel.Debug, "No KK_InvisibleBody marker found"); Visible = true; //character has no extended data, create some so it will save and load with the scene ExtendedData = new PluginData(); Dictionary <string, object> dic = new Dictionary <string, object> { { "Visible", Visible } }; ExtendedData.data = dic; } else { Logger.Log(LogLevel.Debug, $"KK_InvisibleBody marker found, Visible was {ExtendedData.data["Visible"]}"); Visible = (bool)ExtendedData.data["Visible"]; } if (forceVisible) { Visible = forceVisibleState; } else if (toggleVisible) { Visible = !Visible; } if (saveVisibleState) { ExtendedData.data["Visible"] = Visible; ExtendedSave.SetExtendedDataById(chaControl.chaFile, "KK_InvisibleBody", ExtendedData); } //No need to IterateVisible for visible characters that haven't changed if (!(Visible == true && toggleVisible == false && forceVisible == false)) { Transform cf_j_root = CharacterObject.transform.Find("BodyTop/p_cf_body_bone/cf_j_root"); if (cf_j_root != null) { IterateVisible(cf_j_root.gameObject, Visible); } //female Transform cf_o_rootf = CharacterObject.transform.Find("BodyTop/p_cf_body_00/cf_o_root/"); if (cf_o_rootf != null) { IterateVisible(cf_o_rootf.gameObject, Visible); } //male Transform cf_o_rootm = CharacterObject.transform.Find("BodyTop/p_cm_body_00/cf_o_root/"); if (cf_o_rootm != null) { IterateVisible(cf_o_rootm.gameObject, Visible); } } }