Ver código fonte

Reformat part 10 Style Cop

This shouldn't break anything.

Nothing more to reformat so this is the end.

Next up is refactoring 😬.
habeebweeb 2 anos atrás
pai
commit
4989d83885
167 arquivos alterados com 3412 adições e 2636 exclusões
  1. 12 0
      Directory.Build.props
  2. 31 0
      StyleCop.ruleset
  3. 15 16
      src/MeidoPhotoStudio.Converter/Converters/MMConverter.cs
  4. 16 12
      src/MeidoPhotoStudio.Converter/Converters/MMPngConverter.cs
  5. 1 0
      src/MeidoPhotoStudio.Converter/MPSSceneSerializer.cs
  6. 4 4
      src/MeidoPhotoStudio.Converter/MultipleMaids/ConversionUtility.cs
  7. 2 2
      src/MeidoPhotoStudio.Converter/MultipleMaids/MMConstants.cs
  8. 1 0
      src/MeidoPhotoStudio.Converter/MultipleMaids/MMScene.cs
  9. 75 55
      src/MeidoPhotoStudio.Converter/MultipleMaids/MMSceneConverter.cs
  10. 5 4
      src/MeidoPhotoStudio.Converter/Plugin.cs
  11. 4 3
      src/MeidoPhotoStudio.Converter/PluginCore.cs
  12. 2 2
      src/MeidoPhotoStudio.Converter/UI.cs
  13. 1 0
      src/MeidoPhotoStudio.Converter/Utility/LZMA.cs
  14. 2 1
      src/MeidoPhotoStudio.Converter/Utility/PngUtility.cs
  15. 92 0
      src/MeidoPhotoStudio.Plugin/BinaryExtensions.cs
  16. 42 0
      src/MeidoPhotoStudio.Plugin/CameraUtility.cs
  17. 3 3
      src/MeidoPhotoStudio.Plugin/Configuration.cs
  18. 166 156
      src/MeidoPhotoStudio.Plugin/Constants.cs
  19. 35 17
      src/MeidoPhotoStudio.Plugin/DragPoint/CustomGizmo.cs
  20. 55 31
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPoint.cs
  21. 19 0
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointBG.cs
  22. 0 18
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointOther.cs
  23. 35 16
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointGeneral.cs
  24. 3 3
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointGravity.cs
  25. 31 22
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointLight.cs
  26. 9 8
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointMeido.cs
  27. 6 37
      src/MeidoPhotoStudio.Plugin/DragPoint/DragPointProp.cs
  28. 21 0
      src/MeidoPhotoStudio.Plugin/DragPoint/LightProperty.cs
  29. 47 0
      src/MeidoPhotoStudio.Plugin/DragPoint/PropInfo.cs
  30. 4 1
      src/MeidoPhotoStudio.Plugin/GUI/Controls/BaseControl.cs
  31. 3 2
      src/MeidoPhotoStudio.Plugin/GUI/Controls/Button.cs
  32. 8 8
      src/MeidoPhotoStudio.Plugin/GUI/Controls/ComboBox.cs
  33. 32 245
      src/MeidoPhotoStudio.Plugin/GUI/Controls/DropDown.cs
  34. 230 0
      src/MeidoPhotoStudio.Plugin/GUI/Controls/DropdownHelper.cs
  35. 8 7
      src/MeidoPhotoStudio.Plugin/GUI/Controls/KeyRebindButton.cs
  36. 1 1
      src/MeidoPhotoStudio.Plugin/GUI/Controls/Modal.cs
  37. 25 21
      src/MeidoPhotoStudio.Plugin/GUI/Controls/SelectionGrid.cs
  38. 21 30
      src/MeidoPhotoStudio.Plugin/GUI/Controls/Slider.cs
  39. 20 0
      src/MeidoPhotoStudio.Plugin/GUI/Controls/SliderProp.cs
  40. 4 3
      src/MeidoPhotoStudio.Plugin/GUI/Controls/TextField.cs
  41. 7 6
      src/MeidoPhotoStudio.Plugin/GUI/Controls/Toggle.cs
  42. 16 15
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/AttachPropPane.cs
  43. 13 8
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/ModPropsPane.cs
  44. 5 4
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/MyRoomPropsPane.cs
  45. 3 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/PropManagerPane.cs
  46. 10 9
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/PropsPane.cs
  47. 2 1
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/BackgroundSelectorPane.cs
  48. 1 0
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/CameraPane.cs
  49. 8 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/DragPointPane.cs
  50. 4 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/BloomPane.cs
  51. 4 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/DepthOfFieldPane.cs
  52. 21 20
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/EffectPane.cs
  53. 7 7
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/EffectsPane.cs
  54. 4 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/FogPane.cs
  55. 8 8
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/OtherEffectsPane.cs
  56. 4 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/VignettePane.cs
  57. 18 11
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/LightsPane.cs
  58. 19 8
      src/MeidoPhotoStudio.Plugin/GUI/Panes/BasePane.cs
  59. 2 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/CallWindowPanes/MaidSelectorPane.cs
  60. 27 28
      src/MeidoPhotoStudio.Plugin/GUI/Panes/FaceWindowPanes/MaidFaceBlendPane.cs
  61. 80 97
      src/MeidoPhotoStudio.Plugin/GUI/Panes/FaceWindowPanes/MaidFaceSliderPane.cs
  62. 3 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/BG2WindowPane.cs
  63. 5 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/BGWindowPane.cs
  64. 3 4
      src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/CallWindowPane.cs
  65. 2 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/PoseWindowPane.cs
  66. 22 21
      src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/SettingsWindowPane.cs
  67. 17 15
      src/MeidoPhotoStudio.Plugin/GUI/Panes/OtherPanes/TabsPane.cs
  68. 11 8
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/CopyPosePane.cs
  69. 13 11
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/HandPresetPane.cs
  70. 11 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidDressingPane.cs
  71. 2 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidFreeLookPane.cs
  72. 7 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidIKPane.cs
  73. 35 29
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidPoseSelectorPane.cs
  74. 2 2
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MpnAttachPropPane.cs
  75. 1 0
      src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/SavePosePane.cs
  76. 3 3
      src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerDirectoryPane.cs
  77. 6 4
      src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerScenePane.cs
  78. 6 6
      src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerTitleBar.cs
  79. 14 12
      src/MeidoPhotoStudio.Plugin/GUI/Windows/BaseWindow.cs
  80. 29 24
      src/MeidoPhotoStudio.Plugin/GUI/Windows/MainWindow.cs
  81. 11 10
      src/MeidoPhotoStudio.Plugin/GUI/Windows/MessageWindow.cs
  82. 34 32
      src/MeidoPhotoStudio.Plugin/GUI/Windows/SceneModalWindow.cs
  83. 18 19
      src/MeidoPhotoStudio.Plugin/GUI/Windows/SceneWindow.cs
  84. 12 0
      src/MeidoPhotoStudio.Plugin/KeyValuePairExtensions.cs
  85. 19 15
      src/MeidoPhotoStudio.Plugin/MPSScene.cs
  86. 11 10
      src/MeidoPhotoStudio.Plugin/MaidPlacementUtility.cs
  87. 50 0
      src/MeidoPhotoStudio.Plugin/Managers/CameraInfo.cs
  88. 50 93
      src/MeidoPhotoStudio.Plugin/Managers/CameraManager.cs
  89. 14 10
      src/MeidoPhotoStudio.Plugin/Managers/EffectManager.cs
  90. 26 14
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/BloomEffectManager.cs
  91. 5 2
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/BlurEffectManager.cs
  92. 7 2
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/DepthOfFieldManager.cs
  93. 7 2
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/FogEffectManager.cs
  94. 2 0
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/IEffectManager.cs
  95. 9 4
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/SepiaToneEffectManager.cs
  96. 7 2
      src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/VignetteEffectManager.cs
  97. 36 29
      src/MeidoPhotoStudio.Plugin/Managers/EnvironmentManager.cs
  98. 2 0
      src/MeidoPhotoStudio.Plugin/Managers/IManager.cs
  99. 45 24
      src/MeidoPhotoStudio.Plugin/Managers/InputManager.cs
  100. 21 14
      src/MeidoPhotoStudio.Plugin/Managers/LightManager.cs
  101. 139 153
      src/MeidoPhotoStudio.Plugin/Managers/MeidoManager.cs
  102. 24 0
      src/MeidoPhotoStudio.Plugin/Managers/MeidoUpdateEventArgs.cs
  103. 23 21
      src/MeidoPhotoStudio.Plugin/Managers/MessageWindowManager.cs
  104. 56 48
      src/MeidoPhotoStudio.Plugin/Managers/PropManager.cs
  105. 48 37
      src/MeidoPhotoStudio.Plugin/Managers/SceneManager.cs
  106. 13 12
      src/MeidoPhotoStudio.Plugin/Managers/WindowManager.cs
  107. 27 0
      src/MeidoPhotoStudio.Plugin/Meido/AttachPoint.cs
  108. 29 0
      src/MeidoPhotoStudio.Plugin/Meido/AttachPointInfo.cs
  109. 18 0
      src/MeidoPhotoStudio.Plugin/Meido/GravityEventArgs.cs
  110. 13 13
      src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointFinger.cs
  111. 13 2
      src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointHead.cs
  112. 1 0
      src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointPelvis.cs
  113. 14 0
      src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointSpine.cs
  114. 7 5
      src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointTorso.cs
  115. 1 1
      src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointChain.cs
  116. 17 7
      src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointLimb.cs
  117. 8 6
      src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointMune.cs
  118. 78 69
      src/MeidoPhotoStudio.Plugin/Meido/Meido.cs
  119. 276 245
      src/MeidoPhotoStudio.Plugin/Meido/MeidoDragPointManager.cs
  120. 23 0
      src/MeidoPhotoStudio.Plugin/Meido/PoseInfo.cs
  121. 40 42
      src/MeidoPhotoStudio.Plugin/MeidoPhotoStudio.cs
  122. 14 14
      src/MeidoPhotoStudio.Plugin/MenuFileCache.cs
  123. 20 12
      src/MeidoPhotoStudio.Plugin/MenuFileUtility.cs
  124. 18 0
      src/MeidoPhotoStudio.Plugin/MenuFilesEventArgs.cs
  125. 1 74
      src/MeidoPhotoStudio.Plugin/MenuItem.cs
  126. 75 0
      src/MeidoPhotoStudio.Plugin/ModItem.cs
  127. 41 20
      src/MeidoPhotoStudio.Plugin/ModelUtility.cs
  128. 31 0
      src/MeidoPhotoStudio.Plugin/MousePosition.cs
  129. 14 0
      src/MeidoPhotoStudio.Plugin/MpnAttachProp.cs
  130. 16 16
      src/MeidoPhotoStudio.Plugin/MyGui.cs
  131. 11 0
      src/MeidoPhotoStudio.Plugin/MyRoomItem.cs
  132. 4 9
      src/MeidoPhotoStudio.Plugin/Patchers/AllProcPropSeqStartPatcher.cs
  133. 2 0
      src/MeidoPhotoStudio.Plugin/Patchers/BgMgrPatcher.cs
  134. 11 0
      src/MeidoPhotoStudio.Plugin/Patchers/ProcStartEventArgs.cs
  135. 18 0
      src/MeidoPhotoStudio.Plugin/PresetChangeEventArgs.cs
  136. 18 0
      src/MeidoPhotoStudio.Plugin/ScreenshotEventArgs.cs
  137. 1 0
      src/MeidoPhotoStudio.Plugin/Serialization/ISerializer.cs
  138. 1 0
      src/MeidoPhotoStudio.Plugin/Serialization/ISimpleSerializer.cs
  139. 4 1
      src/MeidoPhotoStudio.Plugin/Serialization/SceneMetadata.cs
  140. 2 2
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/AttachPointInfoSerializer.cs
  141. 2 2
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/CameraInfoSerializer.cs
  142. 5 5
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/DragPointLightSerializer.cs
  143. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/BloomEffectSerializer.cs
  144. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/BlurEffectSerializer.cs
  145. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/DepthOfFieldEffectSerializer.cs
  146. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/FogEffectSerializer.cs
  147. 6 6
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/SepiaToneEffectSerializer.cs
  148. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/VignetteEffectSerializer.cs
  149. 2 2
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/LightPropertySerializer.cs
  150. 8 8
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/CameraManagerSerializer.cs
  151. 9 10
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/EffectManagerSerializer.cs
  152. 10 7
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/EnvironmentManagerSerializer.cs
  153. 6 6
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/LightManagerSerializer.cs
  154. 4 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/MeidoManagerSerializer.cs
  155. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/MessageWindowManagerSerializer.cs
  156. 27 27
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/PropManagerSerializer.cs
  157. 21 14
      src/MeidoPhotoStudio.Plugin/Serialization/Serializers/MeidoSerializer.cs
  158. 32 0
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/DragPointPropDTO.cs
  159. 3 29
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/DragPointPropDTOSerializer.cs
  160. 2 2
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/PoseInfoSerializer.cs
  161. 3 3
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/PropInfoSerializer.cs
  162. 29 0
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/TransformDTO.cs
  163. 3 24
      src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/TransformDTOSerializer.cs
  164. 35 0
      src/MeidoPhotoStudio.Plugin/StreamExtensions.cs
  165. 32 36
      src/MeidoPhotoStudio.Plugin/Translation.cs
  166. 37 220
      src/MeidoPhotoStudio.Plugin/Utility.cs
  167. 16 0
      stylecop.json

+ 12 - 0
Directory.Build.props

@@ -4,6 +4,7 @@
     <TargetFramework>net35</TargetFramework>
     <LangVersion>10</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)StyleCop.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
 
   <ItemGroup>
@@ -14,10 +15,21 @@
   </ItemGroup>
 
   <ItemGroup>
+
     <PackageReference Include="Microsoft.Unity.Analyzers" Version="1.13.0">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
     </PackageReference>
+
+    <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+    </PackageReference>
+
+  </ItemGroup>
+
+  <ItemGroup>
+    <AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" />
   </ItemGroup>
 
 </Project>

+ 31 - 0
StyleCop.ruleset

@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<RuleSet Name="StyleCop.Analyzers rules" Description="My style rule preferences" ToolsVersion="14.0">
+
+  <!-- <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers."> -->
+  <!--   <Rule Id="" Action="None" /> -->
+  <!-- </Rules> -->
+
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.ReadabilityRules">
+    <Rule Id="SA1101" Action="None" Description="Prefix local calls with this" />
+  </Rules>
+
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.MaintainabilityRules">
+    <!-- NOTE: SA1401 is suppressed temporarily and will be addressed in the future -->
+    <Rule Id="SA1401" Action="None" Description="Fields should be private" />
+    <Rule Id="SA1407" Action="None" Description="Arithmetic expressions should declare precedence" />
+    <Rule Id="SA1408" Action="None" Description="Conditional expressions should declare precedence" />
+  </Rules>
+
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.LayoutRules">
+    <Rule Id="SA1503" Action="None" Description="Braces should not be omitted" />
+    <Rule Id="SA1519" Action="None" Description="Braces should not be omitted from multi-line child statement" />
+  </Rules>
+
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.DocumentationRules">
+    <!-- NOTE: Documentation rules should only be temporary as I might document things in the future. -->
+    <Rule Id="SA1600" Action="None" Description="Elements should be documented" />
+    <Rule Id="SA1602" Action="None" Description="Enumeration items should be documented" />
+    <Rule Id="SA1633" Action="None" Description="File should have header" />
+  </Rules>
+
+</RuleSet>

+ 15 - 16
src/MeidoPhotoStudio.Converter/Converters/MMConverter.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Linq;
+
 using ExIni;
 using MeidoPhotoStudio.Converter.MultipleMaids;
 
@@ -12,6 +13,15 @@ public class MMConverter : IConverter
 
     private const string InputDirectoryName = "Input";
 
+    public void Convert(string workingDirectory)
+    {
+        var baseDirectory = Path.Combine(workingDirectory, ConverterName);
+        var baseInputDirectory = Path.Combine(baseDirectory, InputDirectoryName);
+        var baseOutputDirectory = Path.Combine(baseDirectory, MPSSceneSerializer.FormatDate(DateTime.Now));
+
+        Convert(baseInputDirectory, baseOutputDirectory);
+    }
+
     private static void Convert(string workingDirectory, string destination)
     {
         var directory = new DirectoryInfo(workingDirectory);
@@ -61,7 +71,7 @@ public class MMConverter : IConverter
         }
         catch (Exception e)
         {
-            if (!Plugin.Instance)
+            if (!Plugin.Instance || Plugin.Instance!.Logger is null)
                 return;
 
             Plugin.Instance.Logger.LogError($"Could not convert {Path.GetFileName(filePath)} scene because {e}");
@@ -100,10 +110,9 @@ public class MMConverter : IConverter
         }
         catch (Exception e)
         {
-            if (Plugin.Instance)
+            if (Plugin.Instance && Plugin.Instance!.Logger is not null)
                 Plugin.Instance.Logger.LogWarning(
-                    $"Could not {(e is IOException ? "read" : "parse")} ini file {filePath}"
-                );
+                    $"Could not {(e is IOException ? "read" : "parse")} ini file {filePath}");
 
             return null;
         }
@@ -111,20 +120,10 @@ public class MMConverter : IConverter
         if (iniFile.HasSection("scene"))
             return iniFile.GetSection("scene");
 
-        if (Plugin.Instance)
+        if (Plugin.Instance && Plugin.Instance!.Logger is not null)
             Plugin.Instance.Logger.LogWarning(
-                $"{filePath} is not a valid MM config because '[scene]' section is missing"
-            );
+                $"{filePath} is not a valid MM config because '[scene]' section is missing");
 
         return null;
     }
-
-    public void Convert(string workingDirectory)
-    {
-        var baseDirectory = Path.Combine(workingDirectory, ConverterName);
-        var baseInputDirectory = Path.Combine(baseDirectory, InputDirectoryName);
-        var baseOutputDirectory = Path.Combine(baseDirectory, MPSSceneSerializer.FormatDate(DateTime.Now));
-
-        Convert(baseInputDirectory, baseOutputDirectory);
-    }
 }

+ 16 - 12
src/MeidoPhotoStudio.Converter/Converters/MMPngConverter.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Text;
+
 using MeidoPhotoStudio.Converter.MultipleMaids;
 using MeidoPhotoStudio.Converter.Utility;
 
@@ -8,11 +9,20 @@ namespace MeidoPhotoStudio.Converter.Converters;
 
 public class MMPngConverter : IConverter
 {
-    private const string InputDirectoryName = "Input";
     public const string ConverterName = "ModifiedMM PNG";
+    private const string InputDirectoryName = "Input";
 
     private static readonly byte[] KankyoHeader = Encoding.ASCII.GetBytes("KANKYO");
 
+    public void Convert(string workingDirectory)
+    {
+        var baseDirectory = Path.Combine(workingDirectory, ConverterName);
+        var baseInputDirectory = Path.Combine(baseDirectory, InputDirectoryName);
+        var baseOutputDirectory = Path.Combine(baseDirectory, MPSSceneSerializer.FormatDate(DateTime.Now));
+
+        Convert(baseInputDirectory, baseOutputDirectory);
+    }
+
     private static void Convert(string workingDirectory, string destination)
     {
         var directory = new DirectoryInfo(workingDirectory);
@@ -43,7 +53,7 @@ public class MMPngConverter : IConverter
         var background = false;
 
         // ModifiedMM habeebweeb fork scene data uses 'KANKYO' as a header to identify saved environments.
-        // Regular scenes will lack a 'KANKYO' header so the filestream position has to be pulled back. 
+        // Regular scenes will lack a 'KANKYO' header so the filestream position has to be pulled back.
         if (MeidoPhotoStudio.Plugin.Utility.BytesEqual(kankyo, KankyoHeader))
             background = true;
         else
@@ -62,6 +72,9 @@ public class MMPngConverter : IConverter
             if (!Plugin.Instance)
                 return;
 
+            if (Plugin.Instance!.Logger is null)
+                return;
+
             Plugin.Instance.Logger.LogWarning($"Could not decompress scene data from {pngFile} because {e}");
 
             return;
@@ -80,7 +93,7 @@ public class MMPngConverter : IConverter
         }
         catch (Exception e)
         {
-            if (!Plugin.Instance)
+            if (!Plugin.Instance || Plugin.Instance!.Logger is null)
                 return;
 
             Plugin.Instance.Logger.LogError($"Could not convert {pngFile} because {e}");
@@ -90,13 +103,4 @@ public class MMPngConverter : IConverter
 
         MPSSceneSerializer.SaveToFile(outputFilename, sceneMetadata, convertedData, thumbnailData);
     }
-
-    public void Convert(string workingDirectory)
-    {
-        var baseDirectory = Path.Combine(workingDirectory, ConverterName);
-        var baseInputDirectory = Path.Combine(baseDirectory, InputDirectoryName);
-        var baseOutputDirectory = Path.Combine(baseDirectory, MPSSceneSerializer.FormatDate(DateTime.Now));
-
-        Convert(baseInputDirectory, baseOutputDirectory);
-    }
 }

+ 1 - 0
src/MeidoPhotoStudio.Converter/MPSSceneSerializer.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Text;
+
 using Ionic.Zlib;
 using MeidoPhotoStudio.Plugin;
 

+ 4 - 4
src/MeidoPhotoStudio.Converter/MultipleMaids/ConversionUtility.cs

@@ -19,11 +19,11 @@ internal static class ConversionUtility
     }
 
     /// <summary>
-    /// Checks if the string has 3 euler angle components delimited by commas before parsing
+    /// Checks if the string has 3 euler angle components delimited by commas before parsing.
     /// </summary>
-    /// <param name="euler">Euler angle string in the form "x,y,z"</param>
-    /// <param name="result">Resulting angle as a <c>Quaternion</c></param>
-    /// <returns>Whether or not the euler string can be safely parsed</returns>
+    /// <param name="euler">Euler angle string in the form "x,y,z".</param>
+    /// <param name="result">Resulting angle as a <c>Quaternion</c>.</param>
+    /// <returns>Whether or not the euler string can be safely parsed.</returns>
     public static bool TryParseEulerAngle(string euler, out Quaternion result)
     {
         result = Quaternion.identity;

+ 2 - 2
src/MeidoPhotoStudio.Converter/MultipleMaids/MMConstants.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using MyRoomCustom;
 using UnityEngine;
 
@@ -34,6 +35,5 @@ public static class MMConstants
             .ToDictionary(
                 data => string.IsNullOrEmpty(data.assetName) ? data.resourceName : data.assetName,
                 data => data,
-                StringComparer.InvariantCultureIgnoreCase
-            );
+                StringComparer.InvariantCultureIgnoreCase);
 }

+ 1 - 0
src/MeidoPhotoStudio.Converter/MultipleMaids/MMScene.cs

@@ -1,5 +1,6 @@
 namespace MeidoPhotoStudio.Converter.MultipleMaids;
 
+// TODO: This class is unused so delete it.
 public class MMScene
 {
     public readonly string Data;

+ 75 - 55
src/MeidoPhotoStudio.Converter/MultipleMaids/MMSceneConverter.cs

@@ -2,6 +2,7 @@ using System;
 using System.IO;
 using System.Linq;
 using System.Text;
+
 using MeidoPhotoStudio.Plugin;
 using MyRoomCustom;
 using UnityEngine;
@@ -75,7 +76,7 @@ public static class MMSceneConverter
     {
         var dataSegments = data.Split('_');
         var strArray2 = dataSegments[1].Split(';');
-        var meidoCount = environment ? MeidoPhotoStudio.Plugin.MeidoPhotoStudio.kankyoMagic : strArray2.Length;
+        var meidoCount = environment ? MeidoPhotoStudio.Plugin.MeidoPhotoStudio.KankyoMagic : strArray2.Length;
 
         return new()
         {
@@ -90,7 +91,8 @@ public static class MMSceneConverter
     {
         var strArray2 = data[1].Split(';');
 
-        writer.Write(MeidoManager.header);
+        writer.Write(MeidoManager.Header);
+
         // MeidoManagerSerializer version
         writer.WriteVersion(1);
 
@@ -115,8 +117,8 @@ public static class MMSceneConverter
                     Position = ConversionUtility.ParseVector3(maidData[59]),
                     Rotation = ConversionUtility.ParseEulerAngle(maidData[58]),
                     LocalScale = ConversionUtility.ParseVector3(maidData[60]),
-                }, tempWriter
-            );
+                },
+                tempWriter);
 
             ConvertHead(maidData, tempWriter);
             ConvertBody(maidData, tempWriter);
@@ -204,10 +206,12 @@ public static class MMSceneConverter
                 writer.Write(ConversionUtility.ParseEulerAngle(maidData[i]));
 
             if (!sixtyFourFlag)
+            {
                 // toe rotations
                 for (var i = 0; i < 2; i++)
                     for (var j = 72 + i; j < 90; j += 2)
                         writer.Write(ConversionUtility.ParseEulerAngle(maidData[j]));
+            }
 
             var rotationIndices = sixtyFourFlag ? BodyRotationIndices64 : BodyRotationIndices;
 
@@ -219,19 +223,23 @@ public static class MMSceneConverter
 
                 // check special case for ClavicleL
                 if (index is ClavicleLIndex)
+                {
                     // NOTE: Versions of MM possibly serialized ClavicleL improperly.
                     // At least I think that's what happened otherwise why would they make this check at all.
                     // https://git.coder.horse/meidomustard/modifiedMM/src/master/MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs#L4355
-                    // 
+                    //
                     // Look at the way MM serializes rotations.
                     // https://git.coder.horse/meidomustard/modifiedMM/src/master/MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs#L2364
                     // It is most definitely possible MM dev missed a component.
-                    // 
+                    //
                     // Also why is strArray9.Length == 2 acceptable? If the length were only 2,
                     // float.Parse(strArray9[2]) would throw an index out of range exception???
                     writer.Write(ConversionUtility.TryParseEulerAngle(data, out rotation));
+                }
                 else
+                {
                     rotation = ConversionUtility.ParseEulerAngle(data);
+                }
 
                 writer.Write(rotation);
             }
@@ -318,8 +326,10 @@ public static class MMSceneConverter
             var hairGravityActive = softG != MMConstants.DefaultSoftG;
 
             writer.Write(hairGravityActive);
+
             // an approximation for hair gravity position
             writer.Write(softG * 90f);
+
             // MM does not serialize skirt gravity
             writer.Write(Vector3.zero);
         }
@@ -329,7 +339,8 @@ public static class MMSceneConverter
     {
         const string newLine = "&kaigyo";
 
-        writer.Write(MessageWindowManager.header);
+        writer.Write(MessageWindowManager.Header);
+
         // MessageWindowManagerSerializer version
         writer.WriteVersion(1);
 
@@ -343,6 +354,7 @@ public static class MMSceneConverter
             showingMessage = int.Parse(strArray3[34]) is 1;
             name = strArray3[35];
             message = strArray3[36].Replace(newLine, "\n");
+
             // MM does not serialize message font size
         }
 
@@ -354,13 +366,15 @@ public static class MMSceneConverter
 
     private static void ConvertCamera(string[] data, BinaryWriter writer)
     {
-        writer.Write(CameraManager.header);
+        writer.Write(CameraManager.Header);
+
         // CameraManagerSerializer version
         writer.WriteVersion(1);
 
         // MM only has one camera
         // current camera index
         writer.Write(0);
+
         // number of camera slots
         writer.Write(1);
 
@@ -385,13 +399,14 @@ public static class MMSceneConverter
                 TargetPos = cameraTargetPos,
                 Angle = cameraRotation,
                 Distance = cameraDistance,
-            }, writer
-        );
+            },
+            writer);
     }
 
     private static void ConvertLight(string[] data, BinaryWriter writer)
     {
-        writer.Write(LightManager.header);
+        writer.Write(LightManager.Header);
+
         // LightManagerSerializer version
         writer.WriteVersion(1);
 
@@ -422,9 +437,9 @@ public static class MMSceneConverter
             var lightProperty = new LightProperty
             {
                 Rotation = Quaternion.Euler(
-                    float.Parse(strArray3[21]), float.Parse(strArray3[22]), float.Parse(strArray3[23])
-                ),
+                    float.Parse(strArray3[21]), float.Parse(strArray3[22]), float.Parse(strArray3[23])),
                 Intensity = float.Parse(strArray3[24]),
+
                 // MM uses spotAngle for both range and spotAngle based on which light type is used
                 SpotAngle = spotAngle,
                 Range = spotAngle / 5f,
@@ -449,10 +464,13 @@ public static class MMSceneConverter
                 : ConversionUtility.ParseVector3(strArray7[0]);
 
             writer.Write(lightPosition);
+
             // light type. 3 is colour mode which uses directional light type.
             writer.Write(lightType is 3 ? 0 : lightType);
+
             // colour mode
             writer.Write(lightType is 3);
+
             // MM lights cannot be disabled
             writer.Write(false);
         }
@@ -485,12 +503,14 @@ public static class MMSceneConverter
                 Intensity = float.Parse(lightProperties[6]),
                 SpotAngle = spotAngle,
                 Range = spotAngle / 5f,
-                // MM does not save shadow strength for other lights 
+
+                // MM does not save shadow strength for other lights
                 ShadowStrength = 0.098f,
                 LightColour = new(
-                    float.Parse(lightProperties[1]), float.Parse(lightProperties[2]), float.Parse(lightProperties[3]),
-                    1f
-                ),
+                    float.Parse(lightProperties[1]),
+                    float.Parse(lightProperties[2]),
+                    float.Parse(lightProperties[3]),
+                    1f),
             };
 
             var lightType = int.Parse(lightProperties[0]);
@@ -506,10 +526,13 @@ public static class MMSceneConverter
                 : ConversionUtility.ParseVector3(strArray7[i + 1]);
 
             writer.Write(lightPosition);
+
             // light type. 3 is colour mode which uses directional light type.
             writer.Write(lightType is 3 ? 0 : lightType);
+
             // colour mode only applies to the main light
             writer.Write(false);
+
             // MM lights cannot be disabled
             writer.Write(false);
         }
@@ -520,28 +543,29 @@ public static class MMSceneConverter
         if (data.Length < 5)
             return;
 
-        writer.Write(EffectManager.header);
+        writer.Write(EffectManager.Header);
+
         // EffectManagerSerializer version
         writer.WriteVersion(1);
 
         var effectData = data[2].Split(',');
 
         // bloom
-        writer.Write(BloomEffectManager.header);
+        writer.Write(BloomEffectManager.Header);
         writer.WriteVersion(1);
 
         writer.Write(int.Parse(effectData[1]) is 1); // active
         writer.Write(float.Parse(effectData[2]) / 5.7f * 100f); // intensity
         writer.Write((int)float.Parse(effectData[3])); // blur iterations
 
+        // bloom threshold colour
         writer.WriteColour(
-            new(1f - float.Parse(effectData[4]), 1f - float.Parse(effectData[5]), 1f - float.Parse(effectData[6]), 1f)
-        ); // bloom threshold colour
+            new(1f - float.Parse(effectData[4]), 1f - float.Parse(effectData[5]), 1f - float.Parse(effectData[6]), 1f));
 
         writer.Write(int.Parse(effectData[7]) is 1); // hdr
 
         // vignetting
-        writer.Write(VignetteEffectManager.header);
+        writer.Write(VignetteEffectManager.Header);
         writer.WriteVersion(1);
 
         writer.Write(int.Parse(effectData[8]) is 1); // active
@@ -550,8 +574,8 @@ public static class MMSceneConverter
         writer.Write(float.Parse(effectData[11])); // blur spread
         writer.Write(float.Parse(effectData[12])); // chromatic aberration
 
-        // blur 
-        writer.Write(BlurEffectManager.header);
+        // blur
+        writer.Write(BlurEffectManager.Header);
         writer.WriteVersion(1);
 
         var blurSize = float.Parse(effectData[13]);
@@ -560,7 +584,7 @@ public static class MMSceneConverter
         writer.Write(blurSize); // blur size
 
         // Sepia Tone
-        writer.Write(SepiaToneEffectManger.header);
+        writer.Write(SepiaToneEffectManager.Header);
         writer.WriteVersion(1);
 
         writer.Write(int.Parse(effectData[29]) is 1);
@@ -568,7 +592,7 @@ public static class MMSceneConverter
         if (effectData.Length > 15)
         {
             // depth of field
-            writer.Write(DepthOfFieldEffectManager.header);
+            writer.Write(DepthOfFieldEffectManager.Header);
             writer.WriteVersion(1);
 
             writer.Write(int.Parse(effectData[15]) is 1); // active
@@ -579,7 +603,7 @@ public static class MMSceneConverter
             writer.Write(int.Parse(effectData[20]) is 1); // visualize focus
 
             // fog
-            writer.Write(FogEffectManager.header);
+            writer.Write(FogEffectManager.Header);
             writer.WriteVersion(1);
 
             writer.Write(int.Parse(effectData[21]) is 1); // active
@@ -590,21 +614,21 @@ public static class MMSceneConverter
 
             // fog colour
             writer.WriteColour(
-                new(float.Parse(effectData[26]), float.Parse(effectData[27]), float.Parse(effectData[28]), 1f)
-            );
+                new(float.Parse(effectData[26]), float.Parse(effectData[27]), float.Parse(effectData[28]), 1f));
         }
 
-        writer.Write(EffectManager.footer);
+        writer.Write(EffectManager.Footer);
     }
 
     private static void ConvertEnvironment(string[] data, BinaryWriter writer)
     {
-        writer.Write(EnvironmentManager.header);
+        writer.Write(EnvironmentManager.Header);
+
         // EnvironmentManagerSerializer version
         writer.WriteVersion(1);
 
         var environmentData = data[0].Split(',');
-        var bgAsset = EnvironmentManager.defaultBg;
+        var bgAsset = EnvironmentManager.DefaultBg;
 
         if (!int.TryParse(environmentData[2], out _))
             bgAsset = environmentData[2].Replace(' ', '_');
@@ -616,19 +640,19 @@ public static class MMSceneConverter
                 new()
                 {
                     Position = new(
-                        float.Parse(environmentData[6]), float.Parse(environmentData[7]),
-                        float.Parse(environmentData[8])
-                    ),
+                        float.Parse(environmentData[6]),
+                        float.Parse(environmentData[7]),
+                        float.Parse(environmentData[8])),
                     Rotation = Quaternion.Euler(
-                        float.Parse(environmentData[3]), float.Parse(environmentData[4]),
-                        float.Parse(environmentData[5])
-                    ),
+                        float.Parse(environmentData[3]),
+                        float.Parse(environmentData[4]),
+                        float.Parse(environmentData[5])),
                     LocalScale = new(
-                        float.Parse(environmentData[9]), float.Parse(environmentData[10]),
-                        float.Parse(environmentData[11])
-                    ),
-                }, writer
-            );
+                        float.Parse(environmentData[9]),
+                        float.Parse(environmentData[10]),
+                        float.Parse(environmentData[11])),
+                },
+                writer);
     }
 
     private static void ConvertProps(string[] data, BinaryWriter writer)
@@ -640,7 +664,7 @@ public static class MMSceneConverter
 
         propCount += hasWProp ? 1 : 0;
 
-        writer.Write(PropManager.header);
+        writer.Write(PropManager.Header);
 
         // PropManagerSerializer version
         writer.WriteVersion(1);
@@ -661,8 +685,7 @@ public static class MMSceneConverter
                     Position =
                         new(float.Parse(strArray3[41]), float.Parse(strArray3[42]), float.Parse(strArray3[43])),
                     Rotation = Quaternion.Euler(
-                        float.Parse(strArray3[38]), float.Parse(strArray3[39]), float.Parse(strArray3[40])
-                    ),
+                        float.Parse(strArray3[38]), float.Parse(strArray3[39]), float.Parse(strArray3[40])),
                     LocalScale =
                         new(float.Parse(strArray3[44]), float.Parse(strArray3[45]), float.Parse(strArray3[46])),
                 },
@@ -691,8 +714,7 @@ public static class MMSceneConverter
                     Position =
                         new(float.Parse(assetParts[4]), float.Parse(assetParts[5]), float.Parse(assetParts[6])),
                     Rotation = Quaternion.Euler(
-                        float.Parse(assetParts[1]), float.Parse(assetParts[2]), float.Parse(assetParts[3])
-                    ),
+                        float.Parse(assetParts[1]), float.Parse(assetParts[2]), float.Parse(assetParts[3])),
                     LocalScale =
                         new(float.Parse(assetParts[7]), float.Parse(assetParts[8]), float.Parse(assetParts[9])),
                 },
@@ -717,7 +739,6 @@ public static class MMSceneConverter
                 // modifiedMM my room creative prop
                 // modifiedMM serializes the prefabName rather than the ID.
                 // Kinda dumb tbh who's idea was this anyway?
-
                 asset = asset.Replace(mmMyRoomPrefix, string.Empty);
 
                 return new(PropInfo.PropType.MyRoom)
@@ -774,13 +795,12 @@ public static class MMSceneConverter
 
                 // hand items are treated as game props (Odogu) in MPS
                 if (asset.StartsWith("handitem", StringComparison.OrdinalIgnoreCase)
-                    || asset.StartsWith("kousoku", StringComparison.OrdinalIgnoreCase)
-                )
+                    || asset.StartsWith("kousoku", StringComparison.OrdinalIgnoreCase))
                     propType = PropInfo.PropType.Odogu;
 
                 return new(propType)
                 {
-                    Filename = asset
+                    Filename = asset,
                 };
             }
 
@@ -789,7 +809,7 @@ public static class MMSceneConverter
                 // MM prepends BG to certain prop asset names. Don't know why.
                 return new(PropInfo.PropType.Odogu)
                 {
-                    Filename = asset.Substring(2)
+                    Filename = asset.Substring(2),
                 };
             }
 
@@ -798,13 +818,13 @@ public static class MMSceneConverter
                 // game bg as prop
                 return new(PropInfo.PropType.Bg)
                 {
-                    Filename = asset.Substring(3)
+                    Filename = asset.Substring(3),
                 };
             }
 
             return new(PropInfo.PropType.Odogu)
             {
-                Filename = asset
+                Filename = asset,
             };
         }
 

+ 5 - 4
src/MeidoPhotoStudio.Converter/Plugin.cs

@@ -1,4 +1,5 @@
 using System.IO;
+
 using BepInEx;
 using BepInEx.Logging;
 using MeidoPhotoStudio.Converter.Converters;
@@ -15,13 +16,13 @@ public class Plugin : BaseUnityPlugin
 
     private const string PluginGuid = "com.habeebweeb.com3d2.meidophotostudio.converter";
 
+    private PluginCore? pluginCore;
+    private UI? ui;
+
     public static Plugin? Instance { get; private set; }
 
     public new ManualLogSource? Logger { get; private set; }
 
-    private PluginCore? pluginCore;
-    private UI? ui;
-
     private void Awake()
     {
         DontDestroyOnLoad(this);
@@ -42,5 +43,5 @@ public class Plugin : BaseUnityPlugin
     }
 
     private void OnGUI() =>
-        ui.Draw();
+        ui!.Draw();
 }

+ 4 - 3
src/MeidoPhotoStudio.Converter/PluginCore.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+
 using MeidoPhotoStudio.Converter.Converters;
 
 namespace MeidoPhotoStudio.Converter;
@@ -8,8 +9,6 @@ public class PluginCore
 {
     private readonly IConverter[] converters;
 
-    public string WorkingDirectory { get; set; }
-
     public PluginCore(string workingDirectory, params IConverter[] converters)
     {
         WorkingDirectory = workingDirectory;
@@ -17,6 +16,8 @@ public class PluginCore
         this.converters = converters;
     }
 
+    public string WorkingDirectory { get; set; }
+
     public void Convert()
     {
         Directory.CreateDirectory(WorkingDirectory);
@@ -32,7 +33,7 @@ public class PluginCore
                 if (!Plugin.Instance)
                     continue;
 
-                Plugin.Instance.Logger.LogError($"Could not convert data because {e}");
+                Plugin.Instance!.Logger!.LogError($"Could not convert data because {e}");
             }
         }
     }

+ 2 - 2
src/MeidoPhotoStudio.Converter/UI.cs

@@ -4,13 +4,13 @@ namespace MeidoPhotoStudio.Converter;
 
 public class UI
 {
+    public bool Visible;
+
     private const int WindowID = 0xEA4040;
     private const string WindowTitle = Plugin.PluginName + " " + Plugin.PluginVersion;
 
     private readonly PluginCore core;
 
-    public bool Visible;
-
     private Rect windowRect;
 
     public UI(PluginCore pluginCore) =>

+ 1 - 0
src/MeidoPhotoStudio.Converter/Utility/LZMA.cs

@@ -1,4 +1,5 @@
 using System.IO;
+
 using SevenZip.Compression.LZMA;
 
 namespace MeidoPhotoStudio.Converter.Utility;

+ 2 - 1
src/MeidoPhotoStudio.Converter/Utility/PngUtility.cs

@@ -47,7 +47,8 @@ internal static class PngUtility
                 // chunk data + CRC
                 read = stream.Read(chunkBuffer, 0, (int)(length + 4L));
                 memoryStream.Write(chunkBuffer, 0, read);
-            } while (!MeidoPhotoStudio.Plugin.Utility.BytesEqual(fourByteBuffer, PngEnd));
+            }
+            while (!MeidoPhotoStudio.Plugin.Utility.BytesEqual(fourByteBuffer, PngEnd));
         }
         catch
         {

+ 92 - 0
src/MeidoPhotoStudio.Plugin/BinaryExtensions.cs

@@ -0,0 +1,92 @@
+using System.IO;
+
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public static class BinaryExtensions
+{
+    public static string ReadNullableString(this BinaryReader binaryReader) =>
+        binaryReader.ReadBoolean()
+            ? binaryReader.ReadString()
+            : null;
+
+    public static void WriteNullableString(this BinaryWriter binaryWriter, string str)
+    {
+        binaryWriter.Write(str is not null);
+
+        if (str is not null)
+            binaryWriter.Write(str);
+    }
+
+    public static void Write(this BinaryWriter binaryWriter, Vector3 vector3)
+    {
+        binaryWriter.Write(vector3.x);
+        binaryWriter.Write(vector3.y);
+        binaryWriter.Write(vector3.z);
+    }
+
+    public static void WriteVector3(this BinaryWriter binaryWriter, Vector3 vector3)
+    {
+        binaryWriter.Write(vector3.x);
+        binaryWriter.Write(vector3.y);
+        binaryWriter.Write(vector3.z);
+    }
+
+    public static Vector2 ReadVector2(this BinaryReader binaryReader) =>
+        new(binaryReader.ReadSingle(), binaryReader.ReadSingle());
+
+    public static Vector3 ReadVector3(this BinaryReader binaryReader) =>
+        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
+
+    public static Vector4 ReadVector4(this BinaryReader binaryReader) =>
+        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
+
+    public static void Write(this BinaryWriter binaryWriter, Quaternion quaternion)
+    {
+        binaryWriter.Write(quaternion.x);
+        binaryWriter.Write(quaternion.y);
+        binaryWriter.Write(quaternion.z);
+        binaryWriter.Write(quaternion.w);
+    }
+
+    public static void WriteQuaternion(this BinaryWriter binaryWriter, Quaternion quaternion)
+    {
+        binaryWriter.Write(quaternion.x);
+        binaryWriter.Write(quaternion.y);
+        binaryWriter.Write(quaternion.z);
+        binaryWriter.Write(quaternion.w);
+    }
+
+    public static Quaternion ReadQuaternion(this BinaryReader binaryReader) =>
+        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
+
+    public static void Write(this BinaryWriter binaryWriter, Color colour)
+    {
+        binaryWriter.Write(colour.r);
+        binaryWriter.Write(colour.g);
+        binaryWriter.Write(colour.b);
+        binaryWriter.Write(colour.a);
+    }
+
+    public static void WriteColour(this BinaryWriter binaryWriter, Color colour)
+    {
+        binaryWriter.Write(colour.r);
+        binaryWriter.Write(colour.g);
+        binaryWriter.Write(colour.b);
+        binaryWriter.Write(colour.a);
+    }
+
+    public static Color ReadColour(this BinaryReader binaryReader) =>
+        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
+
+    public static Matrix4x4 ReadMatrix4x4(this BinaryReader binaryReader)
+    {
+        Matrix4x4 matrix = default;
+
+        for (var i = 0; i < 16; i++)
+            matrix[i] = binaryReader.ReadSingle();
+
+        return matrix;
+    }
+}

+ 42 - 0
src/MeidoPhotoStudio.Plugin/CameraUtility.cs

@@ -0,0 +1,42 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public static class CameraUtility
+{
+    public static CameraMain MainCamera =>
+        GameMain.Instance.MainCamera;
+
+    public static UltimateOrbitCamera UOCamera { get; } =
+        GameMain.Instance.MainCamera.GetComponent<UltimateOrbitCamera>();
+
+    public static void StopSpin()
+    {
+        Utility.SetFieldValue(UOCamera, "xVelocity", 0f);
+        Utility.SetFieldValue(UOCamera, "yVelocity", 0f);
+    }
+
+    public static void StopMovement() =>
+        MainCamera.SetTargetPos(MainCamera.GetTargetPos());
+
+    public static void StopAll()
+    {
+        StopSpin();
+        StopMovement();
+    }
+
+    public static void ForceCalcNearClip(this CameraMain camera)
+    {
+        camera.StopAllCoroutines();
+        camera.m_bCalcNearClip = false;
+        camera.camera.nearClipPlane = 0.01f;
+    }
+
+    public static void ResetCalcNearClip(this CameraMain camera)
+    {
+        if (camera.m_bCalcNearClip)
+            return;
+
+        camera.StopAllCoroutines();
+        camera.m_bCalcNearClip = true;
+        camera.Start();
+    }
+}

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Configuration.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public static class Configuration
 {
-    public static ConfigFile Config { get; }
-
     static Configuration()
     {
-        var configPath = System.IO.Path.Combine(Constants.configPath, $"{MeidoPhotoStudio.pluginName}.cfg");
+        var configPath = System.IO.Path.Combine(Constants.ConfigPath, $"{MeidoPhotoStudio.PluginName}.cfg");
 
         Config = new(configPath, false);
     }
+
+    public static ConfigFile Config { get; }
 }

+ 166 - 156
src/MeidoPhotoStudio.Plugin/Constants.cs

@@ -3,41 +3,42 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Xml.Linq;
+
 using MyRoomCustom;
 using Newtonsoft.Json;
 using UnityEngine;
 using wf;
+
 using static MeidoPhotoStudio.Plugin.MenuFileUtility;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public static class Constants
 {
-    public enum Window { Call, Pose, Face, BG, BG2, Main, Message, Save, SaveModal, Settings }
-    public enum Scene { Daily = 3, Edit = 5 }
-    public enum DoguCategory { Other, Mob, Desk, HandItem, BGSmall }
-
-    public const string customPoseDirectory = "Custom Poses";
-    public const string customHandDirectory = "Hand Presets";
-    public const string customFaceDirectory = "Face Presets";
-    public const string sceneDirectory = "Scenes";
-    public const string kankyoDirectory = "Environments";
-    public const string configDirectory = "MeidoPhotoStudio";
-    public const string translationDirectory = "Translations";
-    public const string databaseDirectory = "Database";
-
-    public static readonly string customPosePath;
-    public static readonly string customHandPath;
-    public static readonly string customFacePath;
-    public static readonly string scenesPath;
-    public static readonly string kankyoPath;
-    public static readonly string configPath;
-    public static readonly string databasePath;
-    public static readonly int mainWindowID = 765;
-    public static readonly int messageWindowID = 961;
-    public static readonly int sceneManagerWindowID = 876;
-    public static readonly int sceneManagerModalID = 283;
-    public static readonly int dropdownWindowID = 777;
+    public const string CustomPoseDirectory = "Custom Poses";
+    public const string CustomHandDirectory = "Hand Presets";
+    public const string CustomFaceDirectory = "Face Presets";
+    public const string SceneDirectory = "Scenes";
+    public const string KankyoDirectory = "Environments";
+    public const string ConfigDirectory = "MeidoPhotoStudio";
+    public const string TranslationDirectory = "Translations";
+    public const string DatabaseDirectory = "Database";
+
+    public static readonly string CustomPosePath;
+    public static readonly string CustomHandPath;
+    public static readonly string CustomFacePath;
+    public static readonly string ScenesPath;
+    public static readonly string KankyoPath;
+    public static readonly string ConfigPath;
+    public static readonly string DatabasePath;
+
+    // TODO: Some of these IDs aren't used to use them or drop them.
+    public static readonly int MainWindowID = 765;
+    public static readonly int MessageWindowID = 961;
+    public static readonly int SceneManagerWindowID = 876;
+    public static readonly int SceneManagerModalID = 283;
+    public static readonly int DropdownWindowID = 777;
+
     public static readonly List<string> PoseGroupList = new();
     public static readonly Dictionary<string, List<string>> PoseDict = new();
     public static readonly List<string> CustomPoseGroupList = new();
@@ -56,52 +57,89 @@ public static class Constants
     public static readonly Dictionary<string, List<MyRoomItem>> MyRoomPropDict = new();
     public static readonly Dictionary<string, List<ModItem>> ModPropDict =
         new(StringComparer.InvariantCultureIgnoreCase);
+
     public static readonly List<string> SceneDirectoryList = new();
     public static readonly List<string> KankyoDirectoryList = new();
     public static readonly List<MpnAttachProp> MpnAttachPropList = new();
-    public static readonly Dictionary<DoguCategory, string> customDoguCategories =
+    public static readonly Dictionary<DoguCategory, string> CustomDoguCategories =
         new()
         {
             [DoguCategory.Other] = "other",
             [DoguCategory.Mob] = "mob",
             [DoguCategory.Desk] = "desk",
             [DoguCategory.HandItem] = "handItem",
-            [DoguCategory.BGSmall] = "bgSmall"
+            [DoguCategory.BGSmall] = "bgSmall",
         };
 
-    public static int MyRoomCustomBGIndex { get; private set; } = -1;
-    public static bool HandItemsInitialized { get; private set; }
-    public static bool MpnAttachInitialized { get; private set; }
-    public static bool MenuFilesInitialized { get; private set; }
-    public static event EventHandler<MenuFilesEventArgs> MenuFilesChange;
-    public static event EventHandler<PresetChangeEventArgs> CustomPoseChange;
-    public static event EventHandler<PresetChangeEventArgs> CustomHandChange;
-    public static event EventHandler<PresetChangeEventArgs> CustomFaceChange;
-
     private static bool beginHandItemInit;
     private static bool beginMpnAttachInit;
 
     static Constants()
     {
-        configPath = Path.Combine(BepInEx.Paths.ConfigPath, configDirectory);
+        ConfigPath = Path.Combine(BepInEx.Paths.ConfigPath, ConfigDirectory);
 
-        var presetPath = Path.Combine(configPath, "Presets");
+        var presetPath = Path.Combine(ConfigPath, "Presets");
 
-        customPosePath = Path.Combine(presetPath, customPoseDirectory);
-        customHandPath = Path.Combine(presetPath, customHandDirectory);
-        customFacePath = Path.Combine(presetPath, customFaceDirectory);
-        scenesPath = Path.Combine(configPath, sceneDirectory);
-        kankyoPath = Path.Combine(configPath, kankyoDirectory);
-        databasePath = Path.Combine(configPath, databaseDirectory);
+        CustomPosePath = Path.Combine(presetPath, CustomPoseDirectory);
+        CustomHandPath = Path.Combine(presetPath, CustomHandDirectory);
+        CustomFacePath = Path.Combine(presetPath, CustomFaceDirectory);
+        ScenesPath = Path.Combine(ConfigPath, SceneDirectory);
+        KankyoPath = Path.Combine(ConfigPath, KankyoDirectory);
+        DatabasePath = Path.Combine(ConfigPath, DatabaseDirectory);
 
         var directories =
-            new[] { customPosePath, customHandPath, scenesPath, kankyoPath, configPath, customFacePath, databasePath };
+            new[] { CustomPosePath, CustomHandPath, ScenesPath, KankyoPath, ConfigPath, CustomFacePath, DatabasePath };
 
         foreach (var directory in directories)
             if (!Directory.Exists(directory))
                 Directory.CreateDirectory(directory);
     }
 
+    public static event EventHandler<MenuFilesEventArgs> MenuFilesChange;
+
+    public static event EventHandler<PresetChangeEventArgs> CustomPoseChange;
+
+    public static event EventHandler<PresetChangeEventArgs> CustomHandChange;
+
+    public static event EventHandler<PresetChangeEventArgs> CustomFaceChange;
+
+    public enum Window
+    {
+        Call,
+        Pose,
+        Face,
+        BG,
+        BG2,
+        Main,
+        Message,
+        Save,
+        SaveModal,
+        Settings,
+    }
+
+    public enum Scene
+    {
+        Daily = 3,
+        Edit = 5,
+    }
+
+    public enum DoguCategory
+    {
+        Other,
+        Mob,
+        Desk,
+        HandItem,
+        BGSmall,
+    }
+
+    public static int MyRoomCustomBGIndex { get; private set; } = -1;
+
+    public static bool HandItemsInitialized { get; private set; }
+
+    public static bool MpnAttachInitialized { get; private set; }
+
+    public static bool MenuFilesInitialized { get; private set; }
+
     public static void Initialize()
     {
         InitializeSceneDirectories();
@@ -123,10 +161,10 @@ public static class Constants
         if (string.IsNullOrEmpty(filename))
             filename = "face_preset";
 
-        if (directory.Equals(customFaceDirectory, StringComparison.InvariantCultureIgnoreCase))
+        if (directory.Equals(CustomFaceDirectory, StringComparison.InvariantCultureIgnoreCase))
             directory = string.Empty;
 
-        directory = Path.Combine(customFacePath, directory);
+        directory = Path.Combine(CustomFacePath, directory);
 
         if (!Directory.Exists(directory))
             Directory.CreateDirectory(directory);
@@ -138,7 +176,7 @@ public static class Constants
 
         fullPath = Path.GetFullPath($"{fullPath}.xml");
 
-        if (!fullPath.StartsWith(customFacePath))
+        if (!fullPath.StartsWith(CustomFacePath))
         {
             Utility.LogError($"Could not save face preset! Path is invalid: '{fullPath}'");
 
@@ -153,25 +191,25 @@ public static class Constants
         var fullDocument = new XDocument(
             new XDeclaration("1.0", "utf-8", "true"),
             new XComment("MeidoPhotoStudio Face Preset"),
-            rootElement
-        );
+            rootElement);
 
         fullDocument.Save(fullPath);
 
         var fileInfo = new FileInfo(fullPath);
         var category = fileInfo.Directory.Name;
         var faceGroup = CustomFaceGroupList.Find(
-            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
-        );
+            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase));
 
         if (string.IsNullOrEmpty(faceGroup))
         {
             CustomFaceGroupList.Add(category);
             CustomFaceDict[category] = new();
-            CustomFaceGroupList.Sort((a, b) => KeepAtTop(a, b, customFaceDirectory));
+            CustomFaceGroupList.Sort((a, b) => KeepAtTop(a, b, CustomFaceDirectory));
         }
         else
+        {
             category = faceGroup;
+        }
 
         CustomFaceDict[category].Add(fullPath);
         CustomFaceDict[category].Sort(WindowsLogicalComparer.StrCmpLogicalW);
@@ -182,17 +220,16 @@ public static class Constants
     public static void AddPose(byte[] anmBinary, string filename, string directory)
     {
         // TODO: Consider writing a file system monitor
-
         filename = Utility.SanitizePathPortion(filename);
         directory = Utility.SanitizePathPortion(directory);
 
         if (string.IsNullOrEmpty(filename))
             filename = "custom_pose";
 
-        if (directory.Equals(customPoseDirectory, StringComparison.InvariantCultureIgnoreCase))
+        if (directory.Equals(CustomPoseDirectory, StringComparison.InvariantCultureIgnoreCase))
             directory = string.Empty;
 
-        directory = Path.Combine(customPosePath, directory);
+        directory = Path.Combine(CustomPosePath, directory);
 
         if (!Directory.Exists(directory))
             Directory.CreateDirectory(directory);
@@ -204,7 +241,7 @@ public static class Constants
 
         fullPath = Path.GetFullPath($"{fullPath}.anm");
 
-        if (!fullPath.StartsWith(customPosePath))
+        if (!fullPath.StartsWith(CustomPosePath))
         {
             Utility.LogError($"Could not save pose! Path is invalid: '{fullPath}'");
 
@@ -217,17 +254,18 @@ public static class Constants
 
         var category = fileInfo.Directory.Name;
         var poseGroup = CustomPoseGroupList.Find(
-            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
-        );
+            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase));
 
         if (string.IsNullOrEmpty(poseGroup))
         {
             CustomPoseGroupList.Add(category);
             CustomPoseDict[category] = new();
-            CustomPoseGroupList.Sort((a, b) => KeepAtTop(a, b, customPoseDirectory));
+            CustomPoseGroupList.Sort((a, b) => KeepAtTop(a, b, CustomPoseDirectory));
         }
         else
+        {
             category = poseGroup;
+        }
 
         CustomPoseDict[category].Add(fullPath);
         CustomPoseDict[category].Sort(WindowsLogicalComparer.StrCmpLogicalW);
@@ -243,10 +281,10 @@ public static class Constants
         if (string.IsNullOrEmpty(filename))
             filename = "custom_hand";
 
-        if (directory.Equals(customHandDirectory, StringComparison.InvariantCultureIgnoreCase))
+        if (directory.Equals(CustomHandDirectory, StringComparison.InvariantCultureIgnoreCase))
             directory = string.Empty;
 
-        directory = Path.Combine(customHandPath, directory);
+        directory = Path.Combine(CustomHandPath, directory);
 
         if (!Directory.Exists(directory))
             Directory.CreateDirectory(directory);
@@ -258,7 +296,7 @@ public static class Constants
 
         fullPath = Path.GetFullPath($"{fullPath}.xml");
 
-        if (!fullPath.StartsWith(customHandPath))
+        if (!fullPath.StartsWith(CustomHandPath))
         {
             Utility.LogError($"Could not save hand! Path is invalid: '{fullPath}'");
 
@@ -274,26 +312,25 @@ public static class Constants
                 "FingerData",
                 new XElement("GameVersion", gameVersion),
                 new XElement("RightData", right),
-                new XElement("BinaryData", Convert.ToBase64String(handBinary))
-            )
-        );
+                new XElement("BinaryData", Convert.ToBase64String(handBinary))));
 
         finalXml.Save(fullPath);
 
         var fileInfo = new FileInfo(fullPath);
         var category = fileInfo.Directory.Name;
         var handGroup = CustomHandGroupList.Find(
-            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
-        );
+            group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase));
 
         if (string.IsNullOrEmpty(handGroup))
         {
             CustomHandGroupList.Add(category);
             CustomHandDict[category] = new();
-            CustomHandGroupList.Sort((a, b) => KeepAtTop(a, b, customHandDirectory));
+            CustomHandGroupList.Sort((a, b) => KeepAtTop(a, b, CustomHandDirectory));
         }
         else
+        {
             category = handGroup;
+        }
 
         CustomHandDict[category].Add(fullPath);
         CustomHandDict[category].Sort(WindowsLogicalComparer.StrCmpLogicalW);
@@ -304,29 +341,29 @@ public static class Constants
     public static void InitializeSceneDirectories()
     {
         SceneDirectoryList.Clear();
-        SceneDirectoryList.Add(sceneDirectory);
+        SceneDirectoryList.Add(SceneDirectory);
 
-        foreach (var directory in Directory.GetDirectories(scenesPath))
+        foreach (var directory in Directory.GetDirectories(ScenesPath))
             SceneDirectoryList.Add(new DirectoryInfo(directory).Name);
 
-        SceneDirectoryList.Sort((a, b) => KeepAtTop(a, b, sceneDirectory));
+        SceneDirectoryList.Sort((a, b) => KeepAtTop(a, b, SceneDirectory));
     }
 
     public static void InitializeKankyoDirectories()
     {
         KankyoDirectoryList.Clear();
-        KankyoDirectoryList.Add(kankyoDirectory);
+        KankyoDirectoryList.Add(KankyoDirectory);
 
-        foreach (var directory in Directory.GetDirectories(kankyoPath))
+        foreach (var directory in Directory.GetDirectories(KankyoPath))
             KankyoDirectoryList.Add(new DirectoryInfo(directory).Name);
 
-        KankyoDirectoryList.Sort((a, b) => KeepAtTop(a, b, kankyoDirectory));
+        KankyoDirectoryList.Sort((a, b) => KeepAtTop(a, b, KankyoDirectory));
     }
 
     public static void InitializePoses()
     {
         // Load Poses
-        var poseListPath = Path.Combine(databasePath, "mm_pose_list.json");
+        var poseListPath = Path.Combine(DatabasePath, "mm_pose_list.json");
 
         try
         {
@@ -378,15 +415,16 @@ public static class Constants
                     continue;
 
                 if (file.StartsWith("edit_"))
+                {
                     PoseDict["normal"].Add(file);
+                }
                 else if (file is not ("dance_cm3d2_001_zoukin" or "dance_cm3d2_001_mop" or "aruki_1_idougo_f"
                     or "sleep2" or "stand_akire2") && !file.EndsWith("_3_") && !file.EndsWith("_5_")
                     && !file.StartsWith("vr_") && !file.StartsWith("dance_mc") && !file.Contains("_kubi_")
                     && !file.Contains("a01_") && !file.Contains("b01_") && !file.Contains("b02_")
                     && !file.EndsWith("_m2") && !file.EndsWith("_m2_once_") && !file.StartsWith("h_")
                     && !file.StartsWith("event_") && !file.StartsWith("man_") && !file.EndsWith("_m")
-                    && !file.Contains("_m_") && !file.Contains("_man_")
-                )
+                    && !file.Contains("_m_") && !file.Contains("_man_"))
                 {
                     if (path.Contains(@"\sex\"))
                         PoseDict["ero2"].Add(file);
@@ -403,7 +441,9 @@ public static class Constants
                         PoseGroupList.Add(category);
                 }
                 else
+                {
                     PoseDict.Remove(category);
+                }
             }
         }
 
@@ -427,15 +467,15 @@ public static class Constants
         CustomPoseGroupList.Clear();
         CustomPoseDict.Clear();
 
-        CustomPoseGroupList.Add(customPoseDirectory);
-        CustomPoseDict[customPoseDirectory] = new();
+        CustomPoseGroupList.Add(CustomPoseDirectory);
+        CustomPoseDict[CustomPoseDirectory] = new();
 
-        GetPoses(customPosePath);
+        GetPoses(CustomPosePath);
 
-        foreach (var directory in Directory.GetDirectories(customPosePath))
+        foreach (var directory in Directory.GetDirectories(CustomPosePath))
             GetPoses(directory);
 
-        CustomPoseGroupList.Sort((a, b) => KeepAtTop(a, b, customPoseDirectory));
+        CustomPoseGroupList.Sort((a, b) => KeepAtTop(a, b, CustomPoseDirectory));
 
         CustomPoseChange?.Invoke(null, PresetChangeEventArgs.Empty);
 
@@ -449,7 +489,7 @@ public static class Constants
 
             var poseGroupName = new DirectoryInfo(directory).Name;
 
-            if (poseGroupName is not customPoseDirectory)
+            if (poseGroupName is not CustomPoseDirectory)
                 CustomPoseGroupList.Add(poseGroupName);
 
             CustomPoseDict[poseGroupName] = poseList.ToList();
@@ -462,15 +502,15 @@ public static class Constants
         CustomHandGroupList.Clear();
         CustomHandDict.Clear();
 
-        CustomHandGroupList.Add(customHandDirectory);
-        CustomHandDict[customHandDirectory] = new();
+        CustomHandGroupList.Add(CustomHandDirectory);
+        CustomHandDict[CustomHandDirectory] = new();
 
-        GetPresets(customHandPath);
+        GetPresets(CustomHandPath);
 
-        foreach (var directory in Directory.GetDirectories(customHandPath))
+        foreach (var directory in Directory.GetDirectories(CustomHandPath))
             GetPresets(directory);
 
-        CustomHandGroupList.Sort((a, b) => KeepAtTop(a, b, customHandDirectory));
+        CustomHandGroupList.Sort((a, b) => KeepAtTop(a, b, CustomHandDirectory));
 
         CustomHandChange?.Invoke(null, PresetChangeEventArgs.Empty);
 
@@ -484,7 +524,7 @@ public static class Constants
 
             var presetCategory = new DirectoryInfo(directory).Name;
 
-            if (presetCategory is not customHandDirectory)
+            if (presetCategory is not CustomHandDirectory)
                 CustomHandGroupList.Add(presetCategory);
 
             CustomHandDict[presetCategory] = presetList.ToList();
@@ -509,15 +549,15 @@ public static class Constants
         CustomFaceGroupList.Clear();
         CustomFaceDict.Clear();
 
-        CustomFaceGroupList.Add(customFaceDirectory);
-        CustomFaceDict[customFaceDirectory] = new();
+        CustomFaceGroupList.Add(CustomFaceDirectory);
+        CustomFaceDict[CustomFaceDirectory] = new();
 
-        GetFacePresets(customFacePath);
+        GetFacePresets(CustomFacePath);
 
-        foreach (var directory in Directory.GetDirectories(customFacePath))
+        foreach (var directory in Directory.GetDirectories(CustomFacePath))
             GetFacePresets(directory);
 
-        CustomFaceGroupList.Sort((a, b) => KeepAtTop(a, b, customFaceDirectory));
+        CustomFaceGroupList.Sort((a, b) => KeepAtTop(a, b, CustomFaceDirectory));
 
         CustomFaceChange?.Invoke(null, PresetChangeEventArgs.Empty);
 
@@ -531,7 +571,7 @@ public static class Constants
 
             var faceGroupName = new DirectoryInfo(directory).Name;
 
-            if (faceGroupName is not customFaceDirectory)
+            if (faceGroupName is not CustomFaceDirectory)
                 CustomFaceGroupList.Add(faceGroupName);
 
             CustomFaceDict[faceGroupName] = presetList.ToList();
@@ -582,7 +622,7 @@ public static class Constants
 
     public static void InitializeDogu()
     {
-        foreach (var customCategory in customDoguCategories.Values)
+        foreach (var customCategory in CustomDoguCategories.Values)
             DoguDict[customCategory] = new();
 
         InitializeDeskItems();
@@ -599,7 +639,7 @@ public static class Constants
         }
 
         foreach (DoguCategory category in Enum.GetValues(typeof(DoguCategory)))
-            DoguCategories.Add(customDoguCategories[category]);
+            DoguCategories.Add(CustomDoguCategories[category]);
     }
 
     public static List<ModItem> GetModPropList(string category)
@@ -678,17 +718,16 @@ public static class Constants
 
     private static void InitializeOtherDogu()
     {
-        DoguDict[customDoguCategories[DoguCategory.BGSmall]] = BGList;
-        DoguDict[customDoguCategories[DoguCategory.Mob]].AddRange(
+        DoguDict[CustomDoguCategories[DoguCategory.BGSmall]] = BGList;
+        DoguDict[CustomDoguCategories[DoguCategory.Mob]].AddRange(
             new[]
             {
                 "Mob_Man_Stand001", "Mob_Man_Stand002", "Mob_Man_Stand003", "Mob_Man_Sit001", "Mob_Man_Sit002",
                 "Mob_Man_Sit003", "Mob_Girl_Stand001", "Mob_Girl_Stand002", "Mob_Girl_Stand003", "Mob_Girl_Sit001",
-                "Mob_Girl_Sit002", "Mob_Girl_Sit003"
-            }
-        );
+                "Mob_Girl_Sit002", "Mob_Girl_Sit003",
+            });
 
-        var DoguList = DoguDict[customDoguCategories[DoguCategory.Other]];
+        var otherDoguList = DoguDict[CustomDoguCategories[DoguCategory.Other]];
 
         // bg object extend
         var doguHashSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
@@ -697,7 +736,7 @@ public static class Constants
 
         try
         {
-            var ignoreListPath = Path.Combine(databasePath, "bg_ignore_list.json");
+            var ignoreListPath = Path.Combine(DatabasePath, "bg_ignore_list.json");
             var ignoreListJson = File.ReadAllText(ignoreListPath);
             var ignoreList = JsonConvert.DeserializeObject<string[]>(ignoreListJson);
 
@@ -707,7 +746,10 @@ public static class Constants
         {
             Utility.LogWarning($"Could not open ignored BG database because {e.Message}");
         }
-        catch { }
+        catch
+        {
+            // Ignored.
+        }
 
         foreach (var doguList in DoguDict.Values)
             doguHashSet.UnionWith(doguList);
@@ -722,17 +764,17 @@ public static class Constants
             if (doguHashSet.Contains(file) || file.EndsWith("_hit"))
                 continue;
 
-            DoguList.Add(file);
+            otherDoguList.Add(file);
             doguHashSet.Add(file);
         }
 
         // Get cherry picked dogu that I can't find in the game files
         try
         {
-            var doguExtendPath = Path.Combine(databasePath, "extra_dogu.json");
+            var doguExtendPath = Path.Combine(DatabasePath, "extra_dogu.json");
             var doguExtendJson = File.ReadAllText(doguExtendPath);
 
-            DoguList.AddRange(JsonConvert.DeserializeObject<IEnumerable<string>>(doguExtendJson));
+            otherDoguList.AddRange(JsonConvert.DeserializeObject<IEnumerable<string>>(doguExtendJson));
         }
         catch (IOException e)
         {
@@ -751,7 +793,7 @@ public static class Constants
             var file = Path.GetFileNameWithoutExtension(path);
 
             if (!doguHashSet.Contains(file) && !file.EndsWith("_not_optimisation"))
-                DoguList.Add(file);
+                otherDoguList.Add(file);
         }
     }
 
@@ -760,14 +802,12 @@ public static class Constants
         var enabledIDs = new HashSet<int>();
 
         CsvCommonIdManager.ReadEnabledIdList(
-            CsvCommonIdManager.FileSystemType.Normal, true, "desk_item_enabled_id", ref enabledIDs
-        );
+            CsvCommonIdManager.FileSystemType.Normal, true, "desk_item_enabled_id", ref enabledIDs);
 
         CsvCommonIdManager.ReadEnabledIdList(
-            CsvCommonIdManager.FileSystemType.Old, true, "desk_item_enabled_id", ref enabledIDs
-        );
+            CsvCommonIdManager.FileSystemType.Old, true, "desk_item_enabled_id", ref enabledIDs);
 
-        var com3d2DeskDogu = DoguDict[customDoguCategories[DoguCategory.Desk]];
+        var com3d2DeskDogu = DoguDict[CustomDoguCategories[DoguCategory.Desk]];
 
         GetDeskItems(GameUty.FileSystem);
 
@@ -837,9 +877,8 @@ public static class Constants
             new[]
             {
                 "Particle/pLineY", "Particle/pLineP02", "Particle/pHeart01",
-                "Particle/pLine_act2", "Particle/pstarY_act2"
-            }
-        );
+                "Particle/pLine_act2", "Particle/pstarY_act3",
+            });
     }
 
     private static void InitializeHandItems()
@@ -865,7 +904,7 @@ public static class Constants
 
         try
         {
-            var ignoreListPath = Path.Combine(databasePath, "bg_ignore_list.json");
+            var ignoreListPath = Path.Combine(DatabasePath, "bg_ignore_list.json");
             var ignoreListJson = File.ReadAllText(ignoreListPath);
             var ignoreList = JsonConvert.DeserializeObject<string[]>(ignoreListJson);
 
@@ -883,7 +922,7 @@ public static class Constants
         foreach (var doguList in DoguDict.Values)
             doguHashSet.UnionWith(doguList);
 
-        var category = customDoguCategories[DoguCategory.HandItem];
+        var category = CustomDoguCategories[DoguCategory.HandItem];
 
         for (var i = 0; i < menuDataBase.GetDataSize(); i++)
         {
@@ -1024,7 +1063,9 @@ public static class Constants
             ModItem modItem;
 
             if (cache.Has(modMenuFile))
+            {
                 modItem = cache[modMenuFile];
+            }
             else
             {
                 modItem = ModItem.Mod(modMenuFile);
@@ -1067,7 +1108,10 @@ public static class Constants
 
             return null;
         }
-        catch { }
+        catch
+        {
+            // Ignored.
+        }
 
         return null;
     }
@@ -1092,41 +1136,7 @@ public static class Constants
     private class SerializePoseList
     {
         public string UIName { get; set; }
-        public List<string> PoseList { get; set; }
-    }
-}
-
-public class MenuFilesEventArgs : EventArgs
-{
-    public enum EventType { HandItems, MenuFiles, MpnAttach }
-
-    public EventType Type { get; }
 
-    public MenuFilesEventArgs(EventType type) =>
-        Type = type;
-}
-
-public class PresetChangeEventArgs : EventArgs
-{
-    public static new PresetChangeEventArgs Empty { get; } = new(string.Empty, string.Empty);
-    public string Category { get; }
-    public string Path { get; }
-
-    public PresetChangeEventArgs(string path, string category)
-    {
-        Path = path;
-        Category = category;
-    }
-}
-
-public readonly struct MpnAttachProp
-{
-    public MPN Tag { get; }
-    public string MenuFile { get; }
-
-    public MpnAttachProp(MPN tag, string menuFile)
-    {
-        Tag = tag;
-        MenuFile = menuFile;
+        public List<string> PoseList { get; set; }
     }
 }

+ 35 - 17
src/MeidoPhotoStudio.Plugin/DragPoint/CustomGizmo.cs

@@ -1,27 +1,23 @@
 using System;
 using System.Reflection;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class CustomGizmo : GizmoRender
 {
-    public enum GizmoType { Rotate, Move, Scale }
-    public enum GizmoMode { Local, World, Global }
+    public GizmoMode Mode;
 
-    private static readonly Camera camera = GameMain.Instance.MainCamera.camera;
-    private static new readonly FieldInfo is_drag_ = Utility.GetFieldInfo<GizmoRender>("is_drag_");
+    private static readonly Camera Camera = GameMain.Instance.MainCamera.camera;
 
-    private new readonly FieldInfo beSelectedType = Utility.GetFieldInfo<GizmoRender>("beSelectedType");
+#pragma warning disable SA1310, SA1311
 
-    public static bool IsDrag
-    {
-        get => (bool)is_drag_.GetValue(null);
-        private set => is_drag_.SetValue(null, value);
-    }
+    // TODO: Refactor reflection to using private members directly.
+    private static new readonly FieldInfo is_drag_ = Utility.GetFieldInfo<GizmoRender>("is_drag_");
+#pragma warning restore SA1310, SA1311
 
-    public event EventHandler GizmoDrag;
-    public GizmoMode gizmoMode;
+    private new readonly FieldInfo beSelectedType = Utility.GetFieldInfo<GizmoRender>("beSelectedType");
 
     private GizmoType gizmoType;
     private Transform target;
@@ -37,6 +33,28 @@ public class CustomGizmo : GizmoRender
     private Vector3 scaleOld = Vector3.one;
     private GizmoType gizmoTypeOld;
 
+    public event EventHandler GizmoDrag;
+
+    public enum GizmoType
+    {
+        Rotate,
+        Move,
+        Scale,
+    }
+
+    public enum GizmoMode
+    {
+        Local,
+        World,
+        Global,
+    }
+
+    public static bool IsDrag
+    {
+        get => (bool)is_drag_.GetValue(null);
+        private set => is_drag_.SetValue(null, value);
+    }
+
     public GizmoType CurrentGizmoType
     {
         get => gizmoType;
@@ -83,7 +101,7 @@ public class CustomGizmo : GizmoRender
         gizmo.target = target;
         gizmo.lineRSelectedThick = 0.25f;
         gizmo.offsetScale = scale;
-        gizmo.gizmoMode = mode;
+        gizmo.Mode = mode;
         gizmo.CurrentGizmoType = GizmoType.Rotate;
 
         return gizmo;
@@ -133,7 +151,7 @@ public class CustomGizmo : GizmoRender
     {
         bool dragged;
 
-        switch (gizmoMode)
+        switch (Mode)
         {
             case GizmoMode.Local:
                 target.position += target.transform.TransformVector(deltaLocalPosition).normalized
@@ -165,12 +183,12 @@ public class CustomGizmo : GizmoRender
 
         transform.position = (hasAlternateTarget ? positionTransform : target).position;
         transform.localScale = Vector3.one;
-        transform.rotation = gizmoMode switch
+        transform.rotation = Mode switch
         {
             GizmoMode.Local => target.rotation,
             GizmoMode.World => Quaternion.identity,
-            GizmoMode.Global => Quaternion.LookRotation(transform.position - camera.transform.position),
-            _ => target.rotation
+            GizmoMode.Global => Quaternion.LookRotation(transform.position - Camera.transform.position),
+            _ => target.rotation,
         };
     }
 

+ 55 - 31
src/MeidoPhotoStudio.Plugin/DragPoint/DragPoint.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.CustomGizmo;
@@ -7,19 +8,18 @@ namespace MeidoPhotoStudio.Plugin;
 
 public abstract class DragPoint : MonoBehaviour
 {
-    public enum DragType { None, Ignore, Select, Delete, MoveXZ, MoveY, RotLocalXZ, RotY, RotLocalY, Scale }
-
-    public const float defaultAlpha = 0.75f;
+    public const float DefaultAlpha = 0.75f;
 
-    private const float doubleClickSensitivity = 0.3f;
+    public static readonly Color DefaultColour = new(0f, 0f, 0f, 0.4f);
 
-    public static Material dragPointMaterial = new(Shader.Find("CM3D2/Trans_AbsoluteFront"));
-    public static readonly Color defaultColour = new(0f, 0f, 0f, 0.4f);
+    public static Material DragPointMaterial = new(Shader.Find("CM3D2/Trans_AbsoluteFront"));
 
     protected static Camera camera = GameMain.Instance.MainCamera.camera;
 
+    private const float DoubleClickSensitivity = 0.3f;
+
     // TODO: Use this value or just throw it away.
-    private static readonly int layer = (int)Mathf.Log(LayerMask.GetMask("AbsolutFront"), 2);
+    private static readonly int DragPointLayer = (int)Mathf.Log(LayerMask.GetMask("AbsolutFront"), 2);
     private static GameObject dragPointParent;
 
     private Func<Vector3> position;
@@ -39,9 +39,36 @@ public abstract class DragPoint : MonoBehaviour
     private float dragPointScale = 1f;
     private bool gizmoEnabled = true;
 
+    static DragPoint()
+    {
+        InputManager.Register(MpsKey.DragSelect, KeyCode.A, "Select handle mode");
+        InputManager.Register(MpsKey.DragDelete, KeyCode.D, "Delete handle mode");
+        InputManager.Register(MpsKey.DragMove, KeyCode.Z, "Move handle mode");
+        InputManager.Register(MpsKey.DragRotate, KeyCode.X, "Rotate handle mode");
+        InputManager.Register(MpsKey.DragScale, KeyCode.C, "Scale handle mode");
+        InputManager.Register(MpsKey.DragFinger, KeyCode.Space, "Show finger handles");
+    }
+
+    public enum DragType
+    {
+        None,
+        Ignore,
+        Select,
+        Delete,
+        MoveXZ,
+        MoveY,
+        RotLocalXZ,
+        RotY,
+        RotLocalY,
+        Scale,
+    }
+
     public Vector3 OriginalScale { get; private set; }
+
     public Transform MyObject { get; protected set; }
+
     public GameObject GizmoGo { get; protected set; }
+
     public CustomGizmo Gizmo { get; protected set; }
 
     public GameObject MyGameObject =>
@@ -129,17 +156,8 @@ public abstract class DragPoint : MonoBehaviour
     protected bool Deleting =>
         CurrentDragType is DragType.Delete;
 
-    static DragPoint()
-    {
-        InputManager.Register(MpsKey.DragSelect, KeyCode.A, "Select handle mode");
-        InputManager.Register(MpsKey.DragDelete, KeyCode.D, "Delete handle mode");
-        InputManager.Register(MpsKey.DragMove, KeyCode.Z, "Move handle mode");
-        InputManager.Register(MpsKey.DragRotate, KeyCode.X, "Rotate handle mode");
-        InputManager.Register(MpsKey.DragScale, KeyCode.C, "Scale handle mode");
-        InputManager.Register(MpsKey.DragFinger, KeyCode.Space, "Show finger handles");
-    }
-
-    public static T Make<T>(PrimitiveType primitiveType, Vector3 scale) where T : DragPoint
+    public static T Make<T>(PrimitiveType primitiveType, Vector3 scale)
+        where T : DragPoint
     {
         var dragPointGo = GameObject.CreatePrimitive(primitiveType);
 
@@ -149,19 +167,12 @@ public abstract class DragPoint : MonoBehaviour
 
         var dragPoint = dragPointGo.AddComponent<T>();
 
-        dragPoint.renderer.material = dragPointMaterial;
-        dragPoint.renderer.material.color = defaultColour;
+        dragPoint.renderer.material = DragPointMaterial;
+        dragPoint.renderer.material.color = DefaultColour;
 
         return dragPoint;
     }
 
-    private static GameObject DragPointParent() =>
-        dragPointParent ? dragPointParent : (dragPointParent = new("[MPS DragPoint Parent]"));
-
-    protected abstract void UpdateDragType();
-
-    protected abstract void Drag();
-
     public virtual void Initialize(Func<Vector3> position, Func<Vector3> rotation)
     {
         this.position = position;
@@ -191,7 +202,13 @@ public abstract class DragPoint : MonoBehaviour
             Gizmo.GizmoVisible = gizmo;
     }
 
-    protected virtual void ApplyDragType() { }
+    protected abstract void UpdateDragType();
+
+    protected abstract void Drag();
+
+    protected virtual void ApplyDragType()
+    {
+    }
 
     protected virtual void Update()
     {
@@ -224,16 +241,20 @@ public abstract class DragPoint : MonoBehaviour
 
     protected virtual void OnMouseUp()
     {
-        if (Time.time - startDoubleClick < doubleClickSensitivity)
+        if (Time.time - startDoubleClick < DoubleClickSensitivity)
         {
             startDoubleClick = -1f;
             OnDoubleClick();
         }
         else
+        {
             startDoubleClick = Time.time;
+        }
     }
 
-    protected virtual void OnDoubleClick() { }
+    protected virtual void OnDoubleClick()
+    {
+    }
 
     protected virtual void OnDestroy() =>
         Destroy(GizmoGo);
@@ -241,7 +262,7 @@ public abstract class DragPoint : MonoBehaviour
     protected void ApplyColour(Color colour) =>
         renderer.material.color = colour;
 
-    protected void ApplyColour(float r, float g, float b, float a = defaultAlpha) =>
+    protected void ApplyColour(float r, float g, float b, float a = DefaultAlpha) =>
         ApplyColour(new(r, g, b, a));
 
     protected Vector3 MouseDelta() =>
@@ -260,6 +281,9 @@ public abstract class DragPoint : MonoBehaviour
             - newOffset;
     }
 
+    private static GameObject DragPointParent() =>
+        dragPointParent ? dragPointParent : (dragPointParent = new("[MPS DragPoint Parent]"));
+
     private void Awake()
     {
         BaseScale = OriginalScale = transform.localScale;

+ 19 - 0
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointBG.cs

@@ -0,0 +1,19 @@
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class DragPointBG : DragPointGeneral
+{
+    public override void Set(Transform myObject)
+    {
+        base.Set(myObject);
+
+        DefaultPosition = myObject.position;
+    }
+
+    protected override void ApplyDragType()
+    {
+        ApplyProperties(Transforming, Transforming, Rotating);
+        ApplyColours();
+    }
+}

+ 0 - 18
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointOther.cs

@@ -1,5 +1,3 @@
-using UnityEngine;
-
 namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointBody : DragPointGeneral
@@ -32,19 +30,3 @@ public class DragPointBody : DragPointGeneral
             ApplyColours();
     }
 }
-
-public class DragPointBG : DragPointGeneral
-{
-    public override void Set(Transform myObject)
-    {
-        base.Set(myObject);
-
-        DefaultPosition = myObject.position;
-    }
-
-    protected override void ApplyDragType()
-    {
-        ApplyProperties(Transforming, Transforming, Rotating);
-        ApplyColours();
-    }
-}

+ 35 - 16
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointGeneral.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.CustomGizmo;
@@ -9,29 +10,38 @@ namespace MeidoPhotoStudio.Plugin;
 
 public abstract class DragPointGeneral : DragPoint
 {
-    public const float smallCube = 0.5f;
+    public const float SmallCube = 0.5f;
+
+    public static readonly Color MoveColour = new(0.2f, 0.5f, 0.95f, DefaultAlpha);
+    public static readonly Color RotateColour = new(0.2f, 0.75f, 0.3f, DefaultAlpha);
+    public static readonly Color ScaleColour = new(0.8f, 0.7f, 0.3f, DefaultAlpha);
+    public static readonly Color SelectColour = new(0.9f, 0.5f, 1f, DefaultAlpha);
+    public static readonly Color DeleteColour = new(1f, 0.1f, 0.1f, DefaultAlpha);
 
-    public static readonly Color moveColour = new(0.2f, 0.5f, 0.95f, defaultAlpha);
-    public static readonly Color rotateColour = new(0.2f, 0.75f, 0.3f, defaultAlpha);
-    public static readonly Color scaleColour = new(0.8f, 0.7f, 0.3f, defaultAlpha);
-    public static readonly Color selectColour = new(0.9f, 0.5f, 1f, defaultAlpha);
-    public static readonly Color deleteColour = new(1f, 0.1f, 0.1f, defaultAlpha);
+    private float currentScale;
+    private bool scaling;
+    private Quaternion currentRotation;
 
     public event EventHandler Move;
+
     public event EventHandler Rotate;
+
     public event EventHandler Scale;
+
     public event EventHandler EndScale;
+
     public event EventHandler Delete;
-    public event EventHandler Select;
 
-    private float currentScale;
-    private bool scaling;
-    private Quaternion currentRotation;
+    public event EventHandler Select;
 
     public Quaternion DefaultRotation { get; set; } = Quaternion.identity;
+
     public Vector3 DefaultPosition { get; set; } = Vector3.zero;
+
     public Vector3 DefaultScale { get; set; } = Vector3.one;
+
     public float ScaleFactor { get; set; } = 1f;
+
     public bool ConstantScale { get; set; }
 
     public override void AddGizmo(float scale = 0.35f, GizmoMode mode = GizmoMode.Local)
@@ -62,9 +72,13 @@ public abstract class DragPointGeneral : DragPoint
         var shift = Input.Shift;
 
         if (Input.GetKey(MpsKey.DragSelect))
+        {
             CurrentDragType = DragType.Select;
+        }
         else if (Input.GetKey(MpsKey.DragDelete))
+        {
             CurrentDragType = DragType.Delete;
+        }
         else if (Input.GetKey(MpsKey.DragMove))
         {
             if (Input.Control)
@@ -73,11 +87,17 @@ public abstract class DragPointGeneral : DragPoint
                 CurrentDragType = shift ? DragType.RotY : DragType.MoveXZ;
         }
         else if (Input.GetKey(MpsKey.DragRotate))
+        {
             CurrentDragType = shift ? DragType.RotLocalY : DragType.RotLocalXZ;
+        }
         else if (Input.GetKey(MpsKey.DragScale))
+        {
             CurrentDragType = DragType.Scale;
+        }
         else
+        {
             CurrentDragType = DragType.None;
+        }
     }
 
     protected override void OnMouseDown()
@@ -145,7 +165,6 @@ public abstract class DragPointGeneral : DragPoint
         var mouseDelta = MouseDelta();
 
         // CurrentDragType can only be one thing at a time afaik so maybe refactor to else if chain
-
         if (CurrentDragType is DragType.MoveXZ)
         {
             MyObject.position = new(cursorPosition.x, MyObject.position.y, cursorPosition.z);
@@ -207,16 +226,16 @@ public abstract class DragPointGeneral : DragPoint
 
     protected virtual void ApplyColours()
     {
-        var colour = moveColour;
+        var colour = MoveColour;
 
         if (Rotating)
-            colour = rotateColour;
+            colour = RotateColour;
         else if (Scaling)
-            colour = scaleColour;
+            colour = ScaleColour;
         else if (Selecting)
-            colour = selectColour;
+            colour = SelectColour;
         else if (Deleting)
-            colour = deleteColour;
+            colour = DeleteColour;
 
         ApplyColour(colour);
     }

+ 3 - 3
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointGravity.cs

@@ -6,8 +6,8 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointGravity : DragPointGeneral
 {
-    private static readonly SlotID[] skirtSlots = { SlotID.skirt, SlotID.onepiece, SlotID.mizugi, SlotID.panz };
-    private static readonly SlotID[] hairSlots = { SlotID.hairF, SlotID.hairR, SlotID.hairS, SlotID.hairT };
+    private static readonly SlotID[] SkirtSlots = { SlotID.skirt, SlotID.onepiece, SlotID.mizugi, SlotID.panz };
+    private static readonly SlotID[] HairSlots = { SlotID.hairF, SlotID.hairR, SlotID.hairS, SlotID.hairT };
 
     public GravityTransformControl Control { get; private set; }
 
@@ -49,7 +49,7 @@ public class DragPointGravity : DragPointGeneral
         }
 
         var gravityControl = gravityTransform.gameObject.AddComponent<GravityTransformControl>();
-        var slots = skirt ? skirtSlots : hairSlots;
+        var slots = skirt ? SkirtSlots : HairSlots;
 
         gravityControl.SetTargetSlods(slots);
         gravityControl.forceRate = 0.1f;

+ 31 - 22
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointLight.cs

@@ -1,31 +1,53 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointLight : DragPointGeneral
 {
-    public enum MPSLightType { Normal, Spot, Point, Disabled }
-    public enum LightProp { LightRotX, LightRotY, Intensity, ShadowStrength, SpotAngle, Range, Red, Green, Blue }
-
-    public static EnvironmentManager EnvironmentManager { private get; set; }
-
-    private readonly LightProperty[] LightProperties = new LightProperty[]
+    private readonly LightProperty[] lightProperties = new LightProperty[]
     {
-        new(), new(), new()
+        new(), new(), new(),
     };
 
     private Light light;
     private bool isDisabled;
     private bool isColourMode;
 
+    public enum MPSLightType
+    {
+        Normal,
+        Spot,
+        Point,
+        Disabled,
+    }
+
+    public enum LightProp
+    {
+        LightRotX,
+        LightRotY,
+        Intensity,
+        ShadowStrength,
+        SpotAngle,
+        Range,
+        Red,
+        Green,
+        Blue,
+    }
+
+    public static EnvironmentManager EnvironmentManager { private get; set; }
+
     public bool IsActiveLight { get; set; }
+
     public string Name { get; private set; } = string.Empty;
+
     public bool IsMain { get; set; }
+
     public MPSLightType SelectedLightType { get; private set; }
 
     public LightProperty CurrentLightProperty =>
-        LightProperties[(int)SelectedLightType];
+        lightProperties[(int)SelectedLightType];
 
     public bool IsDisabled
     {
@@ -245,7 +267,7 @@ public class DragPointLight : DragPointGeneral
 
     public void ResetLightProps()
     {
-        LightProperties[(int)SelectedLightType] = new();
+        lightProperties[(int)SelectedLightType] = new();
         SetProps();
     }
 
@@ -306,16 +328,3 @@ public class DragPointLight : DragPointGeneral
         camera.backgroundColor = CurrentLightProperty.LightColour;
     }
 }
-
-public class LightProperty
-{
-    public static readonly Vector3 DefaultPosition = new(0f, 1.9f, 0.4f);
-    public static readonly Quaternion DefaultRotation = Quaternion.Euler(40f, 180f, 0f);
-
-    public Quaternion Rotation { get; set; } = DefaultRotation;
-    public float Intensity { get; set; } = 0.95f;
-    public float Range { get; set; } = GameMain.Instance.MainLight.GetComponent<Light>().range;
-    public float SpotAngle { get; set; } = 50f;
-    public float ShadowStrength { get; set; } = 0.10f;
-    public Color LightColour { get; set; } = Color.white;
-}

+ 9 - 8
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointMeido.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.CustomGizmo;
@@ -7,20 +8,17 @@ namespace MeidoPhotoStudio.Plugin;
 
 public abstract class DragPointMeido : DragPoint
 {
-    protected const int jointUpper = 0;
-    protected const int jointMiddle = 1;
-    protected const int jointLower = 2;
+    public static readonly Vector3 BoneScale = Vector3.one * 0.04f;
 
-    public static readonly Vector3 boneScale = Vector3.one * 0.04f;
+    protected const int JointUpper = 0;
+    protected const int JointMiddle = 1;
+    protected const int JointLower = 2;
 
     protected Meido meido;
     protected Maid maid;
     protected bool isPlaying;
     protected bool isBone;
 
-    protected IKCtrlData IkCtrlData =>
-        meido.Body.IKCtrl.GetIKData("左手");
-
     public virtual bool IsBone
     {
         get => isBone;
@@ -34,9 +32,12 @@ public abstract class DragPointMeido : DragPoint
         }
     }
 
+    protected IKCtrlData IkCtrlData =>
+        meido.Body.IKCtrl.GetIKData("左手");
+
     public virtual void Initialize(Meido meido, Func<Vector3> position, Func<Vector3> rotation)
     {
-        base.Initialize(position, rotation);
+        Initialize(position, rotation);
 
         this.meido = meido;
         maid = meido.Maid;

+ 6 - 37
src/MeidoPhotoStudio.Plugin/DragPoint/DragPointProp.cs

@@ -1,6 +1,6 @@
 using System.Collections.Generic;
-using System.IO;
 using System.Linq;
+
 using UnityEngine;
 using UnityEngine.Rendering;
 
@@ -8,11 +8,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointProp : DragPointGeneral
 {
-    public AttachPointInfo AttachPointInfo { get; private set; } = AttachPointInfo.Empty;
-    public string assetName = string.Empty;
+    public string AssetName = string.Empty;
 
     private List<Renderer> renderers;
 
+    public AttachPointInfo AttachPointInfo { get; private set; } = AttachPointInfo.Empty;
+
     public PropInfo Info { get; set; }
 
     public string Name =>
@@ -52,7 +53,9 @@ public class DragPointProp : DragPointGeneral
         MyObject.transform.SetParent(attachPoint, keepWorldPosition);
 
         if (keepWorldPosition)
+        {
             MyObject.rotation = rotation;
+        }
         else
         {
             MyObject.localPosition = Vector3.zero;
@@ -89,37 +92,3 @@ public class DragPointProp : DragPointGeneral
         base.OnDestroy();
     }
 }
-
-public class PropInfo
-{
-    public enum PropType { Mod, MyRoom, Bg, Odogu }
-
-    public PropType Type { get; }
-    public string IconFile { get; set; }
-    public string Filename { get; set; }
-    public string SubFilename { get; set; }
-    public int MyRoomID { get; set; }
-
-    public static PropInfo FromModItem(ModItem modItem) =>
-        new(PropType.Mod)
-        {
-            Filename = modItem.IsOfficialMod ? Path.GetFileName(modItem.MenuFile) : modItem.MenuFile,
-            SubFilename = modItem.BaseMenuFile,
-        };
-
-    public static PropInfo FromMyRoom(MyRoomItem myRoomItem) =>
-        new(PropType.MyRoom)
-        {
-            MyRoomID = myRoomItem.ID,
-            Filename = myRoomItem.PrefabName,
-        };
-
-    public static PropInfo FromBg(string name) =>
-        new(PropType.Bg) { Filename = name };
-
-    public static PropInfo FromGameProp(string name) =>
-        new(PropType.Odogu) { Filename = name };
-
-    public PropInfo(PropType type) =>
-        Type = type;
-}

+ 21 - 0
src/MeidoPhotoStudio.Plugin/DragPoint/LightProperty.cs

@@ -0,0 +1,21 @@
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class LightProperty
+{
+    public static readonly Vector3 DefaultPosition = new(0f, 1.9f, 0.4f);
+    public static readonly Quaternion DefaultRotation = Quaternion.Euler(40f, 180f, 0f);
+
+    public Quaternion Rotation { get; set; } = DefaultRotation;
+
+    public float Intensity { get; set; } = 0.95f;
+
+    public float Range { get; set; } = GameMain.Instance.MainLight.GetComponent<Light>().range;
+
+    public float SpotAngle { get; set; } = 50f;
+
+    public float ShadowStrength { get; set; } = 0.10f;
+
+    public Color LightColour { get; set; } = Color.white;
+}

+ 47 - 0
src/MeidoPhotoStudio.Plugin/DragPoint/PropInfo.cs

@@ -0,0 +1,47 @@
+using System.IO;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class PropInfo
+{
+    public PropInfo(PropType type) =>
+        Type = type;
+
+    public enum PropType
+    {
+        Mod,
+        MyRoom,
+        Bg,
+        Odogu,
+    }
+
+    public PropType Type { get; }
+
+    public string IconFile { get; set; }
+
+    public string Filename { get; set; }
+
+    public string SubFilename { get; set; }
+
+    public int MyRoomID { get; set; }
+
+    public static PropInfo FromModItem(ModItem modItem) =>
+        new(PropType.Mod)
+        {
+            Filename = modItem.IsOfficialMod ? Path.GetFileName(modItem.MenuFile) : modItem.MenuFile,
+            SubFilename = modItem.BaseMenuFile,
+        };
+
+    public static PropInfo FromMyRoom(MyRoomItem myRoomItem) =>
+        new(PropType.MyRoom)
+        {
+            MyRoomID = myRoomItem.ID,
+            Filename = myRoomItem.PrefabName,
+        };
+
+    public static PropInfo FromBg(string name) =>
+        new(PropType.Bg) { Filename = name };
+
+    public static PropInfo FromGameProp(string name) =>
+        new(PropType.Odogu) { Filename = name };
+}

+ 4 - 1
src/MeidoPhotoStudio.Plugin/GUI/Controls/BaseControl.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -7,7 +8,9 @@ public abstract class BaseControl
 {
     public event EventHandler ControlEvent;
 
-    public virtual void Draw(params GUILayoutOption[] layoutOptions) { }
+    public virtual void Draw(params GUILayoutOption[] layoutOptions)
+    {
+    }
 
     public virtual void OnControlEvent(EventArgs args) =>
         ControlEvent?.Invoke(this, args);

+ 3 - 2
src/MeidoPhotoStudio.Plugin/GUI/Controls/Button.cs

@@ -1,15 +1,16 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class Button : BaseControl
 {
-    public string Label { get; set; }
-
     public Button(string label) =>
         Label = label;
 
+    public string Label { get; set; }
+
     public override void Draw(params GUILayoutOption[] layoutOptions)
     {
         var buttonStyle = new GUIStyle(GUI.skin.button);

+ 8 - 8
src/MeidoPhotoStudio.Plugin/GUI/Controls/ComboBox.cs

@@ -6,14 +6,6 @@ public class ComboBox : BaseControl
 {
     private readonly TextField textField = new();
 
-    public Dropdown BaseDropDown { get; }
-
-    public string Value
-    {
-        get => textField.Value;
-        set => textField.Value = value;
-    }
-
     public ComboBox(string[] itemList)
     {
         BaseDropDown = new("▾", itemList);
@@ -23,6 +15,14 @@ public class ComboBox : BaseControl
         Value = itemList[0];
     }
 
+    public Dropdown BaseDropDown { get; }
+
+    public string Value
+    {
+        get => textField.Value;
+        set => textField.Value = value;
+    }
+
     public override void Draw(params GUILayoutOption[] layoutOptions)
     {
         var buttonStyle = new GUIStyle(GUI.skin.button)

+ 32 - 245
src/MeidoPhotoStudio.Plugin/GUI/Controls/DropDown.cs

@@ -1,5 +1,7 @@
 using System;
+
 using UnityEngine;
+
 using DropdownCloseArgs = MeidoPhotoStudio.Plugin.DropdownHelper.DropdownCloseArgs;
 using DropdownSelectArgs = MeidoPhotoStudio.Plugin.DropdownHelper.DropdownSelectArgs;
 
@@ -10,10 +12,6 @@ public class Dropdown : BaseControl
     private readonly string label;
     private readonly bool isMenu;
 
-    public event EventHandler SelectionChange;
-    public event EventHandler DropdownOpen;
-    public event EventHandler DropdownClose;
-
     private Vector2 elementSize;
     private int selectedItemIndex;
     private bool clickedYou;
@@ -21,6 +19,36 @@ public class Dropdown : BaseControl
     private Vector2 scrollPos;
     private Rect buttonRect;
 
+    public Dropdown(string label, string[] itemList, int selectedItemIndex = 0)
+        : this(itemList, selectedItemIndex)
+    {
+        this.label = label;
+
+        isMenu = true;
+    }
+
+    public Dropdown(string[] itemList, int selectedItemIndex = 0)
+    {
+        DropdownID = DropdownHelper.DropdownID;
+        SetDropdownItems(itemList, selectedItemIndex);
+
+        DropdownHelper.SelectionChange += OnChangeSelection;
+        DropdownHelper.DropdownClose += OnCloseDropdown;
+    }
+
+    // TODO: I don't think this works the way I think it does
+    ~Dropdown()
+    {
+        DropdownHelper.SelectionChange -= OnChangeSelection;
+        DropdownHelper.DropdownClose -= OnCloseDropdown;
+    }
+
+    public event EventHandler SelectionChange;
+
+    public event EventHandler DropdownOpen;
+
+    public event EventHandler DropdownClose;
+
     public int DropdownID { get; }
 
     public string[] DropdownList { get; private set; }
@@ -50,30 +78,6 @@ public class Dropdown : BaseControl
         }
     }
 
-    public Dropdown(string label, string[] itemList, int selectedItemIndex = 0)
-        : this(itemList, selectedItemIndex)
-    {
-        this.label = label;
-
-        isMenu = true;
-    }
-
-    public Dropdown(string[] itemList, int selectedItemIndex = 0)
-    {
-        DropdownID = DropdownHelper.DropdownID;
-        SetDropdownItems(itemList, selectedItemIndex);
-
-        DropdownHelper.SelectionChange += OnChangeSelection;
-        DropdownHelper.DropdownClose += OnCloseDropdown;
-    }
-
-    // TODO: I don't think this works the way I think it does
-    ~Dropdown()
-    {
-        DropdownHelper.SelectionChange -= OnChangeSelection;
-        DropdownHelper.DropdownClose -= OnCloseDropdown;
-    }
-
     public void SetDropdownItems(string[] itemList, int selectedItemIndex = -1)
     {
         if (selectedItemIndex < 0)
@@ -180,220 +184,3 @@ public class Dropdown : BaseControl
     private void OnDropdownEvent(EventHandler handler) =>
         handler?.Invoke(this, EventArgs.Empty);
 }
-
-public static class DropdownHelper
-{
-    public static event EventHandler<DropdownSelectArgs> SelectionChange;
-    public static event EventHandler<DropdownCloseArgs> DropdownClose;
-    public static Rect dropdownWindow;
-
-    private static int dropdownID = 100;
-    private static GUIStyle defaultDropdownStyle;
-    private static bool onScrollBar;
-    private static Rect dropdownScrollRect;
-    private static Rect dropdownRect;
-    private static GUIStyle dropdownStyle;
-    private static GUIStyle windowStyle;
-    private static Rect buttonRect;
-    private static string[] dropdownList;
-    private static Vector2 scrollPos;
-    private static int currentDropdownID;
-    private static int selectedItemIndex;
-    private static bool initialized;
-
-    public static int DropdownID =>
-        dropdownID++;
-
-    public static GUIStyle DefaultDropdownStyle
-    {
-        get
-        {
-            if (!initialized)
-                InitializeStyle();
-
-            return defaultDropdownStyle;
-        }
-    }
-
-    public static bool Visible { get; set; }
-    public static bool DropdownOpen { get; private set; }
-
-    public static Vector2 CalculateElementSize(string item, GUIStyle style = null)
-    {
-        if (!initialized)
-            InitializeStyle();
-
-        style ??= DefaultDropdownStyle;
-
-        return style.CalcSize(new(item));
-    }
-
-    public static Vector2 CalculateElementSize(string[] list, GUIStyle style = null)
-    {
-        if (!initialized)
-            InitializeStyle();
-
-        style ??= DefaultDropdownStyle;
-
-        var content = new GUIContent(list[0]);
-        var calculatedSize = style.CalcSize(content);
-
-        for (var i = 1; i < list.Length; i++)
-        {
-            content.text = list[i];
-
-            var calcSize = style.CalcSize(content);
-
-            if (calcSize.x > calculatedSize.x)
-                calculatedSize = calcSize;
-        }
-
-        return calculatedSize;
-    }
-
-    public static void Set(Dropdown dropdown, GUIStyle style = null)
-    {
-        dropdownStyle = style ?? DefaultDropdownStyle;
-        currentDropdownID = dropdown.DropdownID;
-        dropdownList = dropdown.DropdownList;
-        scrollPos = dropdown.ScrollPos;
-        selectedItemIndex = dropdown.SelectedItemIndex;
-        scrollPos = dropdown.ScrollPos;
-        buttonRect = dropdown.ButtonRect;
-
-        var calculatedSize = dropdown.ElementSize;
-        var calculatedListHeight = calculatedSize.y * dropdownList.Length;
-        var heightAbove = buttonRect.y;
-        var heightBelow = Screen.height - heightAbove - buttonRect.height;
-        var rectWidth = Mathf.Max(calculatedSize.x + 5, buttonRect.width);
-        var rectHeight = Mathf.Min(calculatedListHeight, Mathf.Max(heightAbove, heightBelow));
-
-        if (calculatedListHeight > heightBelow && heightAbove > heightBelow)
-            dropdownWindow = new(buttonRect.x, buttonRect.y - rectHeight, rectWidth + 18, rectHeight);
-        else
-        {
-            if (calculatedListHeight > heightBelow)
-                rectHeight -= calculatedSize.y;
-
-            dropdownWindow = new(buttonRect.x, buttonRect.y + buttonRect.height, rectWidth + 18, rectHeight);
-        }
-
-        dropdownWindow.x = Mathf.Clamp(dropdownWindow.x, 0, Screen.width - rectWidth - 18);
-
-        dropdownScrollRect = new(0, 0, dropdownWindow.width, dropdownWindow.height);
-        dropdownRect = new(0, 0, dropdownWindow.width - 18, calculatedListHeight);
-
-        DropdownOpen = true;
-        Visible = true;
-    }
-
-    public static void HandleDropdown()
-    {
-        dropdownWindow = GUI.Window(Constants.dropdownWindowID, dropdownWindow, GUIFunc, "", windowStyle);
-
-        if (Input.mouseScrollDelta.y is not 0f && Visible && dropdownWindow.Contains(Event.current.mousePosition))
-            Input.ResetInputAxes();
-    }
-
-    private static void GUIFunc(int id)
-    {
-        var clicked = false;
-
-        if (Event.current.type is EventType.MouseUp)
-            clicked = true;
-
-        scrollPos = GUI.BeginScrollView(dropdownScrollRect, scrollPos, dropdownRect);
-
-        var selection = GUI.SelectionGrid(dropdownRect, selectedItemIndex, dropdownList, 1, dropdownStyle);
-
-        GUI.EndScrollView();
-
-        var clickedYou = false;
-
-        if (Utility.AnyMouseDown())
-        {
-            var mousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
-            var clickedMe = dropdownWindow.Contains(mousePos);
-
-            onScrollBar = mousePos.x > dropdownWindow.x + dropdownWindow.width - 12f;
-
-            if (buttonRect.Contains(mousePos))
-                clickedYou = true;
-
-            if (!clickedMe)
-                DropdownOpen = false;
-        }
-
-        if (selection != selectedItemIndex || clicked && !onScrollBar)
-        {
-            SelectionChange?.Invoke(null, new(currentDropdownID, selection));
-            DropdownOpen = false;
-        }
-
-        if (!DropdownOpen)
-        {
-            Visible = false;
-            DropdownClose?.Invoke(null, new(currentDropdownID, scrollPos, clickedYou));
-        }
-    }
-
-    private static void InitializeStyle()
-    {
-        defaultDropdownStyle = new(GUI.skin.button)
-        {
-            alignment = TextAnchor.MiddleLeft,
-            margin = new(0, 0, 0, 0),
-        };
-
-        defaultDropdownStyle.padding.top = defaultDropdownStyle.padding.bottom = 2;
-        defaultDropdownStyle.normal.background = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.5f));
-
-        var whiteBackground = new Texture2D(2, 2);
-
-        defaultDropdownStyle.onHover.background
-            = defaultDropdownStyle.hover.background
-            = defaultDropdownStyle.onNormal.background
-            = whiteBackground;
-
-        defaultDropdownStyle.onHover.textColor
-            = defaultDropdownStyle.onNormal.textColor
-            = defaultDropdownStyle.hover.textColor
-            = Color.black;
-
-        windowStyle = new(GUI.skin.box)
-        {
-            padding = new(0, 0, 0, 0),
-            alignment = TextAnchor.UpperRight,
-        };
-
-        initialized = true;
-    }
-
-    public class DropdownEventArgs : EventArgs
-    {
-        public int DropdownID { get; }
-
-        public DropdownEventArgs(int dropdownID) =>
-            DropdownID = dropdownID;
-    }
-
-    public class DropdownSelectArgs : DropdownEventArgs
-    {
-        public int SelectedItemIndex { get; }
-
-        public DropdownSelectArgs(int dropdownID, int selection) : base(dropdownID) =>
-            SelectedItemIndex = selection;
-    }
-
-    public class DropdownCloseArgs : DropdownEventArgs
-    {
-        public Vector2 ScrollPos { get; }
-        public bool ClickedYou { get; }
-
-        public DropdownCloseArgs(int dropdownID, Vector2 scrollPos, bool clickedYou = false) : base(dropdownID)
-        {
-            ScrollPos = scrollPos;
-            ClickedYou = clickedYou;
-        }
-    }
-}

+ 230 - 0
src/MeidoPhotoStudio.Plugin/GUI/Controls/DropdownHelper.cs

@@ -0,0 +1,230 @@
+using System;
+
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public static class DropdownHelper
+{
+    public static Rect DropdownWindow;
+
+    private static int dropdownID = 100;
+    private static GUIStyle defaultDropdownStyle;
+    private static bool onScrollBar;
+    private static Rect dropdownScrollRect;
+    private static Rect dropdownRect;
+    private static GUIStyle dropdownStyle;
+    private static GUIStyle windowStyle;
+    private static Rect buttonRect;
+    private static string[] dropdownList;
+    private static Vector2 scrollPos;
+    private static int currentDropdownID;
+    private static int selectedItemIndex;
+    private static bool initialized;
+
+    public static event EventHandler<DropdownSelectArgs> SelectionChange;
+
+    public static event EventHandler<DropdownCloseArgs> DropdownClose;
+
+    public static int DropdownID =>
+        dropdownID++;
+
+    public static GUIStyle DefaultDropdownStyle
+    {
+        get
+        {
+            if (!initialized)
+                InitializeStyle();
+
+            return defaultDropdownStyle;
+        }
+    }
+
+    public static bool Visible { get; set; }
+
+    public static bool DropdownOpen { get; private set; }
+
+    public static Vector2 CalculateElementSize(string item, GUIStyle style = null)
+    {
+        if (!initialized)
+            InitializeStyle();
+
+        style ??= DefaultDropdownStyle;
+
+        return style.CalcSize(new(item));
+    }
+
+    public static Vector2 CalculateElementSize(string[] list, GUIStyle style = null)
+    {
+        if (!initialized)
+            InitializeStyle();
+
+        style ??= DefaultDropdownStyle;
+
+        var content = new GUIContent(list[0]);
+        var calculatedSize = style.CalcSize(content);
+
+        for (var i = 1; i < list.Length; i++)
+        {
+            content.text = list[i];
+
+            var calcSize = style.CalcSize(content);
+
+            if (calcSize.x > calculatedSize.x)
+                calculatedSize = calcSize;
+        }
+
+        return calculatedSize;
+    }
+
+    public static void Set(Dropdown dropdown, GUIStyle style = null)
+    {
+        dropdownStyle = style ?? DefaultDropdownStyle;
+        currentDropdownID = dropdown.DropdownID;
+        dropdownList = dropdown.DropdownList;
+        scrollPos = dropdown.ScrollPos;
+        selectedItemIndex = dropdown.SelectedItemIndex;
+        scrollPos = dropdown.ScrollPos;
+        buttonRect = dropdown.ButtonRect;
+
+        var calculatedSize = dropdown.ElementSize;
+        var calculatedListHeight = calculatedSize.y * dropdownList.Length;
+        var heightAbove = buttonRect.y;
+        var heightBelow = Screen.height - heightAbove - buttonRect.height;
+        var rectWidth = Mathf.Max(calculatedSize.x + 5, buttonRect.width);
+        var rectHeight = Mathf.Min(calculatedListHeight, Mathf.Max(heightAbove, heightBelow));
+
+        if (calculatedListHeight > heightBelow && heightAbove > heightBelow)
+        {
+            DropdownWindow = new(buttonRect.x, buttonRect.y - rectHeight, rectWidth + 18, rectHeight);
+        }
+        else
+        {
+            if (calculatedListHeight > heightBelow)
+                rectHeight -= calculatedSize.y;
+
+            DropdownWindow = new(buttonRect.x, buttonRect.y + buttonRect.height, rectWidth + 18, rectHeight);
+        }
+
+        DropdownWindow.x = Mathf.Clamp(DropdownWindow.x, 0, Screen.width - rectWidth - 18);
+
+        dropdownScrollRect = new(0, 0, DropdownWindow.width, DropdownWindow.height);
+        dropdownRect = new(0, 0, DropdownWindow.width - 18, calculatedListHeight);
+
+        DropdownOpen = true;
+        Visible = true;
+    }
+
+    public static void HandleDropdown()
+    {
+        DropdownWindow = GUI.Window(Constants.DropdownWindowID, DropdownWindow, GUIFunc, string.Empty, windowStyle);
+
+        if (Input.mouseScrollDelta.y is not 0f && Visible && DropdownWindow.Contains(Event.current.mousePosition))
+            Input.ResetInputAxes();
+    }
+
+    private static void GUIFunc(int id)
+    {
+        var clicked = false;
+
+        if (Event.current.type is EventType.MouseUp)
+            clicked = true;
+
+        scrollPos = GUI.BeginScrollView(dropdownScrollRect, scrollPos, dropdownRect);
+
+        var selection = GUI.SelectionGrid(dropdownRect, selectedItemIndex, dropdownList, 1, dropdownStyle);
+
+        GUI.EndScrollView();
+
+        var clickedYou = false;
+
+        if (Utility.AnyMouseDown())
+        {
+            var mousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
+            var clickedMe = DropdownWindow.Contains(mousePos);
+
+            onScrollBar = mousePos.x > DropdownWindow.x + DropdownWindow.width - 12f;
+
+            if (buttonRect.Contains(mousePos))
+                clickedYou = true;
+
+            if (!clickedMe)
+                DropdownOpen = false;
+        }
+
+        if (selection != selectedItemIndex || clicked && !onScrollBar)
+        {
+            SelectionChange?.Invoke(null, new(currentDropdownID, selection));
+            DropdownOpen = false;
+        }
+
+        if (!DropdownOpen)
+        {
+            Visible = false;
+            DropdownClose?.Invoke(null, new(currentDropdownID, scrollPos, clickedYou));
+        }
+    }
+
+    private static void InitializeStyle()
+    {
+        defaultDropdownStyle = new(GUI.skin.button)
+        {
+            alignment = TextAnchor.MiddleLeft,
+            margin = new(0, 0, 0, 0),
+        };
+
+        defaultDropdownStyle.padding.top = defaultDropdownStyle.padding.bottom = 2;
+        defaultDropdownStyle.normal.background = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.5f));
+
+        var whiteBackground = new Texture2D(2, 2);
+
+        defaultDropdownStyle.onHover.background
+            = defaultDropdownStyle.hover.background
+            = defaultDropdownStyle.onNormal.background
+            = whiteBackground;
+
+        defaultDropdownStyle.onHover.textColor
+            = defaultDropdownStyle.onNormal.textColor
+            = defaultDropdownStyle.hover.textColor
+            = Color.black;
+
+        windowStyle = new(GUI.skin.box)
+        {
+            padding = new(0, 0, 0, 0),
+            alignment = TextAnchor.UpperRight,
+        };
+
+        initialized = true;
+    }
+
+    public class DropdownEventArgs : EventArgs
+    {
+        public DropdownEventArgs(int dropdownID) =>
+            DropdownID = dropdownID;
+
+        public int DropdownID { get; }
+    }
+
+    public class DropdownSelectArgs : DropdownEventArgs
+    {
+        public DropdownSelectArgs(int dropdownID, int selection)
+            : base(dropdownID) =>
+            SelectedItemIndex = selection;
+
+        public int SelectedItemIndex { get; }
+    }
+
+    public class DropdownCloseArgs : DropdownEventArgs
+    {
+        public DropdownCloseArgs(int dropdownID, Vector2 scrollPos, bool clickedYou = false)
+            : base(dropdownID)
+        {
+            ScrollPos = scrollPos;
+            ClickedYou = clickedYou;
+        }
+
+        public Vector2 ScrollPos { get; }
+
+        public bool ClickedYou { get; }
+    }
+}

+ 8 - 7
src/MeidoPhotoStudio.Plugin/GUI/Controls/KeyRebindButton.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -10,6 +11,13 @@ public class KeyRebindButton : BaseControl
     private bool listening;
     private KeyCode keyCode;
 
+    public KeyRebindButton(KeyCode code)
+    {
+        button = new(code.ToString());
+        button.ControlEvent += (_, _) =>
+            StartListening();
+    }
+
     public KeyCode KeyCode
     {
         get => keyCode;
@@ -20,13 +28,6 @@ public class KeyRebindButton : BaseControl
         }
     }
 
-    public KeyRebindButton(KeyCode code)
-    {
-        button = new(code.ToString());
-        button.ControlEvent += (_, _) =>
-            StartListening();
-    }
-
     public override void Draw(params GUILayoutOption[] layoutOptions)
     {
         var buttonStyle = new GUIStyle(GUI.skin.button);

+ 1 - 1
src/MeidoPhotoStudio.Plugin/GUI/Controls/Modal.cs

@@ -38,6 +38,6 @@ public static class Modal
         var windowStyle = new GUIStyle(GUI.skin.box);
 
         currentModal.WindowRect =
-            GUI.ModalWindow(currentModal.windowID, currentModal.WindowRect, currentModal.GUIFunc, "", windowStyle);
+            GUI.ModalWindow(currentModal.WindowID, currentModal.WindowRect, currentModal.GUIFunc, string.Empty, windowStyle);
     }
 }

+ 25 - 21
src/MeidoPhotoStudio.Plugin/GUI/Controls/SelectionGrid.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -8,6 +9,12 @@ public class SelectionGrid : BaseControl
     private SimpleToggle[] toggles;
     private int selectedItemIndex;
 
+    public SelectionGrid(string[] items, int selected = 0)
+    {
+        selectedItemIndex = Mathf.Clamp(selected, 0, items.Length - 1);
+        toggles = MakeToggles(items);
+    }
+
     public int SelectedItemIndex
     {
         get => selectedItemIndex;
@@ -16,18 +23,12 @@ public class SelectionGrid : BaseControl
             selectedItemIndex = Mathf.Clamp(value, 0, toggles.Length - 1);
 
             foreach (var toggle in toggles)
-                toggle.value = toggle.toggleIndex == selectedItemIndex;
+                toggle.Value = toggle.ToggleIndex == selectedItemIndex;
 
             OnControlEvent(EventArgs.Empty);
         }
     }
 
-    public SelectionGrid(string[] items, int selected = 0)
-    {
-        selectedItemIndex = Mathf.Clamp(selected, 0, items.Length - 1);
-        toggles = MakeToggles(items);
-    }
-
     public override void Draw(params GUILayoutOption[] layoutOptions)
     {
         GUILayout.BeginHorizontal();
@@ -50,8 +51,8 @@ public class SelectionGrid : BaseControl
             {
                 var item = items[i];
 
-                toggles[i].value = i == SelectedItemIndex;
-                toggles[i].label = item;
+                toggles[i].Value = i == SelectedItemIndex;
+                toggles[i].Label = item;
             }
 
         SelectedItemIndex = Mathf.Clamp(selectedItemIndex, 0, items.Length - 1);
@@ -65,12 +66,12 @@ public class SelectionGrid : BaseControl
         {
             var toggle = new SimpleToggle(items[i], i == SelectedItemIndex)
             {
-                toggleIndex = i,
+                ToggleIndex = i,
             };
 
             toggle.ControlEvent += (sender, _) =>
             {
-                var value = (sender as SimpleToggle).toggleIndex;
+                var value = (sender as SimpleToggle).ToggleIndex;
 
                 if (value != SelectedItemIndex)
                     SelectedItemIndex = value;
@@ -84,29 +85,32 @@ public class SelectionGrid : BaseControl
 
     private class SimpleToggle
     {
-        public int toggleIndex;
-        public bool value;
-        public string label;
-        public event EventHandler ControlEvent;
+        public int ToggleIndex;
+        public bool Value;
+        public string Label;
 
         public SimpleToggle(string label, bool value = false)
         {
-            this.label = label;
-            this.value = value;
+            Label = label;
+            Value = value;
         }
 
+        public event EventHandler ControlEvent;
+
         public void Draw(params GUILayoutOption[] layoutOptions)
         {
-            var value = GUILayout.Toggle(this.value, label, layoutOptions);
+            var value = GUILayout.Toggle(Value, Label, layoutOptions);
 
-            if (value == this.value)
+            if (value == Value)
                 return;
 
             if (!value)
-                this.value = true;
+            {
+                Value = true;
+            }
             else
             {
-                this.value = value;
+                Value = value;
 
                 ControlEvent?.Invoke(this, EventArgs.Empty);
             }

+ 21 - 30
src/MeidoPhotoStudio.Plugin/GUI/Controls/Slider.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Globalization;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -15,6 +16,26 @@ public class Slider : BaseControl
     private string textFieldValue;
     private bool hasTextField;
 
+    public Slider(string label, float left, float right, float value = 0, float defaultValue = 0)
+    {
+        Label = label;
+        this.left = left;
+        this.right = right;
+        this.value = Utility.Bound(value, left, right);
+        textFieldValue = FormatValue(this.value);
+        DefaultValue = defaultValue;
+    }
+
+    public Slider(string label, SliderProp prop)
+        : this(label, prop.Left, prop.Right, prop.Initial, prop.Default)
+    {
+    }
+
+    public Slider(SliderProp prop)
+        : this(string.Empty, prop.Left, prop.Right, prop.Initial, prop.Default)
+    {
+    }
+
     public bool HasReset { get; set; }
 
     public string Label
@@ -79,20 +100,6 @@ public class Slider : BaseControl
         }
     }
 
-    public Slider(string label, float left, float right, float value = 0, float defaultValue = 0)
-    {
-        Label = label;
-        this.left = left;
-        this.right = right;
-        this.value = Utility.Bound(value, left, right);
-        textFieldValue = FormatValue(this.value);
-        DefaultValue = defaultValue;
-    }
-
-    public Slider(string label, SliderProp prop) : this(label, prop.Left, prop.Right, prop.Initial, prop.Default) { }
-
-    public Slider(SliderProp prop) : this(string.Empty, prop.Left, prop.Right, prop.Initial, prop.Default) { }
-
     public override void Draw(params GUILayoutOption[] layoutOptions)
     {
         var hasUpper = hasLabel || HasTextField || HasReset;
@@ -156,19 +163,3 @@ public class Slider : BaseControl
     private static string FormatValue(float value) =>
         value.ToString("0.####", CultureInfo.InvariantCulture);
 }
-
-public readonly struct SliderProp
-{
-    public float Left { get; }
-    public float Right { get; }
-    public float Initial { get; }
-    public float Default { get; }
-
-    public SliderProp(float left, float right, float initial = 0f, float @default = 0f)
-    {
-        Left = left;
-        Right = right;
-        Initial = Utility.Bound(initial, left, right);
-        Default = Utility.Bound(@default, left, right);
-    }
-}

+ 20 - 0
src/MeidoPhotoStudio.Plugin/GUI/Controls/SliderProp.cs

@@ -0,0 +1,20 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public readonly struct SliderProp
+{
+    public SliderProp(float left, float right, float initial = 0f, float @default = 0f)
+    {
+        Left = left;
+        Right = right;
+        Initial = Utility.Bound(initial, left, right);
+        Default = Utility.Bound(@default, left, right);
+    }
+
+    public float Left { get; }
+
+    public float Right { get; }
+
+    public float Initial { get; }
+
+    public float Default { get; }
+}

+ 4 - 3
src/MeidoPhotoStudio.Plugin/GUI/Controls/TextField.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -7,13 +8,13 @@ public class TextField : BaseControl
 {
     private static int textFieldID = 961;
 
-    private static int ID =>
-        ++textFieldID;
-
     private readonly string controlName = $"textField{ID}";
 
     public string Value { get; set; } = string.Empty;
 
+    private static int ID =>
+        ++textFieldID;
+
     public override void Draw(params GUILayoutOption[] layoutOptions) =>
         Draw(new(GUI.skin.textField), layoutOptions);
 

+ 7 - 6
src/MeidoPhotoStudio.Plugin/GUI/Controls/Toggle.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -7,6 +8,12 @@ public class Toggle : BaseControl
 {
     private bool value;
 
+    public Toggle(string label, bool state = false)
+    {
+        Label = label;
+        value = state;
+    }
+
     public string Label { get; set; }
 
     public bool Value
@@ -20,12 +27,6 @@ public class Toggle : BaseControl
         }
     }
 
-    public Toggle(string label, bool state = false)
-    {
-        Label = label;
-        value = state;
-    }
-
     public override void Draw(params GUILayoutOption[] layoutOptions) =>
         Draw(new(GUI.skin.toggle), layoutOptions);
 

+ 16 - 15
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/AttachPropPane.cs

@@ -1,13 +1,14 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class AttachPropPane : BasePane
 {
-    private static readonly Dictionary<AttachPoint, string> toggleTranslation =
+    private static readonly Dictionary<AttachPoint, string> ToggleTranslation =
         new()
         {
             [AttachPoint.Head] = "head",
@@ -44,18 +45,6 @@ public class AttachPropPane : BasePane
     private bool doguDropdownActive;
     private string header;
 
-    private bool PaneActive =>
-        meidoDropdownActive && doguDropdownActive;
-
-    private Meido SelectedMeido =>
-        meidoManager.ActiveMeidoList[meidoDropdown.SelectedItemIndex];
-
-    private DragPointProp SelectedProp =>
-        propManager.CurrentProp;
-
-    private bool KeepWoldPosition =>
-        keepWorldPositionToggle.Value;
-
     public AttachPropPane(MeidoManager meidoManager, PropManager propManager)
     {
         header = Translation.Get("attachPropPane", "header");
@@ -86,7 +75,7 @@ public class AttachPropPane : BasePane
                 continue;
 
             var point = attachPoint;
-            var toggle = new Toggle(Translation.Get("attachPropPane", toggleTranslation[point]));
+            var toggle = new Toggle(Translation.Get("attachPropPane", ToggleTranslation[point]));
 
             toggle.ControlEvent += (_, _) =>
                 OnToggleChange(point);
@@ -95,6 +84,18 @@ public class AttachPropPane : BasePane
         }
     }
 
+    private bool PaneActive =>
+        meidoDropdownActive && doguDropdownActive;
+
+    private Meido SelectedMeido =>
+        meidoManager.ActiveMeidoList[meidoDropdown.SelectedItemIndex];
+
+    private DragPointProp SelectedProp =>
+        propManager.CurrentProp;
+
+    private bool KeepWoldPosition =>
+        keepWorldPositionToggle.Value;
+
     public override void Draw()
     {
         const float dropdownButtonHeight = 30;
@@ -137,7 +138,7 @@ public class AttachPropPane : BasePane
             if (attachPoint is AttachPoint.None)
                 continue;
 
-            toggles[attachPoint].Label = Translation.Get("attachPropPane", toggleTranslation[attachPoint]);
+            toggles[attachPoint].Label = Translation.Get("attachPropPane", ToggleTranslation[attachPoint]);
         }
     }
 

+ 13 - 8
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/ModPropsPane.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.MenuFileUtility;
@@ -8,8 +9,6 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class ModPropsPane : BasePane
 {
-    private enum FilterType { None, Mod, Base }
-
     private readonly PropManager propManager;
     private readonly Dropdown propCategoryDropdown;
     private readonly Toggle modFilterToggle;
@@ -26,9 +25,6 @@ public class ModPropsPane : BasePane
     private bool baseFilter;
     private int currentListCount;
 
-    private string SelectedCategory =>
-        MenuCategories[propCategoryDropdown.SelectedItemIndex];
-
     public ModPropsPane(PropManager propManager)
     {
         this.propManager = propManager;
@@ -69,6 +65,16 @@ public class ModPropsPane : BasePane
             ChangeFilter(FilterType.Base);
     }
 
+    private enum FilterType
+    {
+        None,
+        Mod,
+        Base,
+    }
+
+    private string SelectedCategory =>
+        MenuCategories[propCategoryDropdown.SelectedItemIndex];
+
     public override void Draw()
     {
         const float dropdownButtonHeight = 30f;
@@ -115,8 +121,7 @@ public class ModPropsPane : BasePane
             var positionRect = new Rect(5f, offsetTop + dropdownButtonHeight, windowWidth - 10f, windowHeight - 145f);
 
             var viewRect = new Rect(
-                0f, 0f, buttonSize * columns, buttonSize * Mathf.Ceil(currentListCount / (float)columns) + 5
-            );
+                0f, 0f, buttonSize * columns, buttonSize * Mathf.Ceil(currentListCount / (float)columns) + 5);
 
             propListScrollPos = GUI.BeginScrollView(positionRect, propListScrollPos, viewRect);
 
@@ -131,7 +136,7 @@ public class ModPropsPane : BasePane
                 var y = modIndex / columns * buttonSize;
                 var iconRect = new Rect(x, y, buttonSize, buttonSize);
 
-                if (GUI.Button(iconRect, ""))
+                if (GUI.Button(iconRect, string.Empty))
                     propManager.AddModProp(modItem);
 
                 GUI.DrawTexture(iconRect, modItem.Icon);

+ 5 - 4
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/MyRoomPropsPane.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -12,9 +13,6 @@ public class MyRoomPropsPane : BasePane
     private List<MyRoomItem> myRoomPropList;
     private string currentCategory;
 
-    private string SelectedCategory =>
-        Constants.MyRoomPropCategories[propCategoryDropdown.SelectedItemIndex];
-
     public MyRoomPropsPane(PropManager propManager)
     {
         this.propManager = propManager;
@@ -26,6 +24,9 @@ public class MyRoomPropsPane : BasePane
         ChangePropCategory(SelectedCategory);
     }
 
+    private string SelectedCategory =>
+        Constants.MyRoomPropCategories[propCategoryDropdown.SelectedItemIndex];
+
     public override void Draw()
     {
         const float dropdownButtonHeight = 30f;
@@ -64,7 +65,7 @@ public class MyRoomPropsPane : BasePane
             var myRoomItem = myRoomPropList[i];
             var iconRect = new Rect(x, y, buttonSize, buttonSize);
 
-            if (GUI.Button(iconRect, ""))
+            if (GUI.Button(iconRect, string.Empty))
                 propManager.AddMyRoomProp(myRoomItem);
 
             GUI.DrawTexture(iconRect, myRoomItem.Icon);

+ 3 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/PropManagerPane.cs

@@ -16,9 +16,6 @@ public class PropManagerPane : BasePane
 
     private string propManagerHeader;
 
-    private int CurrentDoguIndex =>
-        propManager.CurrentPropIndex;
-
     public PropManagerPane(PropManager propManager)
     {
         this.propManager = propManager;
@@ -93,6 +90,9 @@ public class PropManagerPane : BasePane
         propManagerHeader = Translation.Get("propManagerPane", "header");
     }
 
+    private int CurrentDoguIndex =>
+        propManager.CurrentPropIndex;
+
     public override void Draw()
     {
         const float buttonHeight = 30;

+ 10 - 9
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindow2Panes/PropsPane.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -20,9 +21,6 @@ public class PropsPane : BasePane
     private string currentCategory;
     private bool itemSelectorEnabled = true;
 
-    private string SelectedCategory =>
-        Constants.DoguCategories[doguCategoryDropdown.SelectedItemIndex];
-
     public PropsPane(PropManager propManager)
     {
         this.propManager = propManager;
@@ -61,6 +59,9 @@ public class PropsPane : BasePane
         ChangeDoguCategory(SelectedCategory);
     }
 
+    private string SelectedCategory =>
+        Constants.DoguCategories[doguCategoryDropdown.SelectedItemIndex];
+
     public override void Draw()
     {
         const float buttonHeight = 30;
@@ -102,7 +103,7 @@ public class PropsPane : BasePane
         var category = SelectedCategory;
 
         var translationArray =
-            category == Constants.customDoguCategories[Constants.DoguCategory.HandItem] && !handItemsReady
+            category == Constants.CustomDoguCategories[Constants.DoguCategory.HandItem] && !handItemsReady
                 ? new[] { Translation.Get("systemMessage", "initializing") }
                 : GetTranslations(category);
 
@@ -118,7 +119,7 @@ public class PropsPane : BasePane
 
         var selectedCategory = SelectedCategory;
 
-        if (selectedCategory == Constants.customDoguCategories[Constants.DoguCategory.HandItem])
+        if (selectedCategory == Constants.CustomDoguCategories[Constants.DoguCategory.HandItem])
             ChangeDoguCategory(selectedCategory, true);
     }
 
@@ -131,7 +132,7 @@ public class PropsPane : BasePane
 
         string[] translationArray;
 
-        if (category == Constants.customDoguCategories[Constants.DoguCategory.HandItem] && !handItemsReady)
+        if (category == Constants.CustomDoguCategories[Constants.DoguCategory.HandItem] && !handItemsReady)
         {
             translationArray = new[] { Translation.Get("systemMessage", "initializing") };
             itemSelectorEnabled = false;
@@ -149,7 +150,7 @@ public class PropsPane : BasePane
     {
         IEnumerable<string> itemList = Constants.DoguDict[category];
 
-        if (category == Constants.customDoguCategories[Constants.DoguCategory.HandItem])
+        if (category == Constants.CustomDoguCategories[Constants.DoguCategory.HandItem])
         {
             // TODO: itemList should not be reused
             itemList = itemList.Select(item =>
@@ -160,7 +161,7 @@ public class PropsPane : BasePane
             });
         }
 
-        var translationCategory = category == Constants.customDoguCategories[Constants.DoguCategory.BGSmall]
+        var translationCategory = category == Constants.CustomDoguCategories[Constants.DoguCategory.BGSmall]
             ? "bgNames"
             : "propNames";
 
@@ -171,7 +172,7 @@ public class PropsPane : BasePane
     {
         var assetName = Constants.DoguDict[SelectedCategory][doguDropdown.SelectedItemIndex];
 
-        if (SelectedCategory == Constants.customDoguCategories[Constants.DoguCategory.BGSmall])
+        if (SelectedCategory == Constants.CustomDoguCategories[Constants.DoguCategory.BGSmall])
             propManager.AddBgProp(assetName);
         else
             propManager.AddGameProp(assetName);

+ 2 - 1
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/BackgroundSelectorPane.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -15,7 +16,7 @@ public class BackgroundSelectorPane : BasePane
     {
         this.environmentManager = environmentManager;
 
-        var theaterIndex = Constants.BGList.FindIndex(bg => bg == EnvironmentManager.defaultBg);
+        var theaterIndex = Constants.BGList.FindIndex(bg => bg == EnvironmentManager.DefaultBg);
         var bgList = new List<string>(Translation.GetList("bgNames", Constants.BGList));
 
         if (Constants.MyRoomCustomBGIndex >= 0)

+ 1 - 0
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/CameraPane.cs

@@ -1,4 +1,5 @@
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;

+ 8 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/DragPointPane.cs

@@ -4,8 +4,6 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointPane : BasePane
 {
-    private enum Setting { Prop, Maid, Background, Size }
-
     private readonly Toggle propsCubeToggle;
     private readonly Toggle smallCubeToggle;
     private readonly Toggle maidCubeToggle;
@@ -34,6 +32,14 @@ public class DragPointPane : BasePane
             ChangeDragPointSetting(Setting.Background, bgCubeToggle.Value);
     }
 
+    private enum Setting
+    {
+        Prop,
+        Maid,
+        Background,
+        Size,
+    }
+
     public override void Draw()
     {
         MpsGui.Header(header);

+ 4 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/BloomPane.cs

@@ -11,9 +11,8 @@ public class BloomPane : EffectPane<BloomEffectManager>
     private readonly Slider blueSlider;
     private readonly Toggle hdrToggle;
 
-    protected override BloomEffectManager EffectManager { get; set; }
-
-    public BloomPane(EffectManager effectManager) : base(effectManager)
+    public BloomPane(EffectManager effectManager)
+        : base(effectManager)
     {
         intensitySlider = new(Translation.Get("effectBloom", "intensity"), 0f, 100f, EffectManager.BloomValue);
         intensitySlider.ControlEvent += (_, _) =>
@@ -72,6 +71,8 @@ public class BloomPane : EffectPane<BloomEffectManager>
         };
     }
 
+    protected override BloomEffectManager EffectManager { get; set; }
+
     protected override void TranslatePane()
     {
         intensitySlider.Label = Translation.Get("effectBloom", "intensity");

+ 4 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/DepthOfFieldPane.cs

@@ -10,9 +10,8 @@ public class DepthOfFieldPane : EffectPane<DepthOfFieldEffectManager>
     private readonly Slider blurSlider;
     private readonly Toggle thicknessToggle;
 
-    protected override DepthOfFieldEffectManager EffectManager { get; set; }
-
-    public DepthOfFieldPane(EffectManager effectManager) : base(effectManager)
+    public DepthOfFieldPane(EffectManager effectManager)
+        : base(effectManager)
     {
         focalLengthSlider =
             new(Translation.Get("effectDof", "focalLength"), 0f, 10f, EffectManager.FocalLength);
@@ -62,6 +61,8 @@ public class DepthOfFieldPane : EffectPane<DepthOfFieldEffectManager>
         };
     }
 
+    protected override DepthOfFieldEffectManager EffectManager { get; set; }
+
     protected override void TranslatePane()
     {
         focalLengthSlider.Label = Translation.Get("effectDof", "focalLength");

+ 21 - 20
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/EffectPane.cs

@@ -2,29 +2,14 @@ using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
-public abstract class EffectPane<T> : BasePane where T : IEffectManager
+public abstract class EffectPane<T> : BasePane
+    where T : IEffectManager
 {
     protected readonly Toggle effectToggle;
     protected readonly Button resetEffectButton;
 
     private bool enabled;
 
-    public override bool Enabled
-    {
-        get => enabled;
-        set
-        {
-            enabled = value;
-
-            if (updating)
-                return;
-
-            EffectManager.SetEffectActive(enabled);
-        }
-    }
-
-    protected abstract T EffectManager { get; set; }
-
     protected EffectPane(EffectManager effectManager)
     {
         EffectManager = effectManager.Get<T>();
@@ -38,11 +23,21 @@ public abstract class EffectPane<T> : BasePane where T : IEffectManager
             Enabled = effectToggle.Value;
     }
 
-    protected abstract void TranslatePane();
+    public override bool Enabled
+    {
+        get => enabled;
+        set
+        {
+            enabled = value;
 
-    protected abstract void UpdateControls();
+            if (updating)
+                return;
 
-    protected abstract void DrawPane();
+            EffectManager.SetEffectActive(enabled);
+        }
+    }
+
+    protected abstract T EffectManager { get; set; }
 
     public override void UpdatePane()
     {
@@ -76,6 +71,12 @@ public abstract class EffectPane<T> : BasePane where T : IEffectManager
         updating = false;
     }
 
+    protected abstract void TranslatePane();
+
+    protected abstract void UpdateControls();
+
+    protected abstract void DrawPane();
+
     private void ResetEffect()
     {
         EffectManager.Deactivate();

+ 7 - 7
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/EffectsPane.cs

@@ -10,6 +10,13 @@ public class EffectsPane : BasePane
 
     private BasePane currentEffectPane;
 
+    public EffectsPane()
+    {
+        effectToggles = new(new[] { "dummy" /* thicc */ });
+        effectToggles.ControlEvent += (_, _) =>
+            SetEffectPane(effectList[effectToggles.SelectedItemIndex]);
+    }
+
     public BasePane this[string effectUI]
     {
         private get => effectPanes[effectUI];
@@ -21,13 +28,6 @@ public class EffectsPane : BasePane
         }
     }
 
-    public EffectsPane()
-    {
-        effectToggles = new(new[] { "dummy" /* thicc */ });
-        effectToggles.ControlEvent += (_, _) =>
-            SetEffectPane(effectList[effectToggles.SelectedItemIndex]);
-    }
-
     public override void UpdatePane() =>
         currentEffectPane.UpdatePane();
 

+ 4 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/FogPane.cs

@@ -12,9 +12,8 @@ public class FogPane : EffectPane<FogEffectManager>
     private readonly Slider greenSlider;
     private readonly Slider blueSlider;
 
-    protected override FogEffectManager EffectManager { get; set; }
-
-    public FogPane(EffectManager effectManager) : base(effectManager)
+    public FogPane(EffectManager effectManager)
+        : base(effectManager)
     {
         distanceSlider = new(Translation.Get("effectFog", "distance"), 0f, 30f, EffectManager.Distance);
         distanceSlider.ControlEvent += (_, _) =>
@@ -82,6 +81,8 @@ public class FogPane : EffectPane<FogEffectManager>
         };
     }
 
+    protected override FogEffectManager EffectManager { get; set; }
+
     protected override void TranslatePane()
     {
         distanceSlider.Label = Translation.Get("effectFog", "distance");

+ 8 - 8
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/OtherEffectsPane.cs

@@ -5,7 +5,7 @@ namespace MeidoPhotoStudio.Plugin;
 public class OtherEffectsPane : BasePane
 {
     private readonly EffectManager effectManager;
-    private readonly SepiaToneEffectManger sepiaToneEffectManger;
+    private readonly SepiaToneEffectManager sepiaToneEffectManger;
     private readonly BlurEffectManager blurEffectManager;
     private readonly Toggle sepiaToggle;
     private readonly Slider blurSlider;
@@ -14,7 +14,7 @@ public class OtherEffectsPane : BasePane
     {
         this.effectManager = effectManager;
 
-        sepiaToneEffectManger = this.effectManager.Get<SepiaToneEffectManger>();
+        sepiaToneEffectManger = this.effectManager.Get<SepiaToneEffectManager>();
         blurEffectManager = this.effectManager.Get<BlurEffectManager>();
 
         sepiaToggle = new(Translation.Get("otherEffectsPane", "sepiaToggle"));
@@ -43,12 +43,6 @@ public class OtherEffectsPane : BasePane
         };
     }
 
-    protected override void ReloadTranslation()
-    {
-        sepiaToggle.Label = Translation.Get("otherEffectsPane", "sepiaToggle");
-        blurSlider.Label = Translation.Get("otherEffectsPane", "blurSlider");
-    }
-
     public override void Draw()
     {
         GUILayout.BeginHorizontal();
@@ -69,4 +63,10 @@ public class OtherEffectsPane : BasePane
 
         updating = false;
     }
+
+    protected override void ReloadTranslation()
+    {
+        sepiaToggle.Label = Translation.Get("otherEffectsPane", "sepiaToggle");
+        blurSlider.Label = Translation.Get("otherEffectsPane", "blurSlider");
+    }
 }

+ 4 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/EffectsPanes/VignettePane.cs

@@ -9,9 +9,8 @@ public class VignettePane : EffectPane<VignetteEffectManager>
     private readonly Slider blurSpreadSlider;
     private readonly Slider aberrationSlider;
 
-    protected override VignetteEffectManager EffectManager { get; set; }
-
-    public VignettePane(EffectManager effectManager) : base(effectManager)
+    public VignettePane(EffectManager effectManager)
+        : base(effectManager)
     {
         intensitySlider = new(Translation.Get("effectVignette", "intensity"), -40f, 70f);
         intensitySlider.ControlEvent += (_, _) =>
@@ -50,6 +49,8 @@ public class VignettePane : EffectPane<VignetteEffectManager>
         };
     }
 
+    protected override VignetteEffectManager EffectManager { get; set; }
+
     protected override void TranslatePane()
     {
         intensitySlider.Label = Translation.Get("effectVignette", "intensity");

+ 18 - 11
src/MeidoPhotoStudio.Plugin/GUI/Panes/BackgroundWindowPanes/LightsPane.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.DragPointLight;
@@ -8,12 +9,18 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class LightsPane : BasePane
 {
-    private static readonly string[] lightTypes = { "normal", "spot", "point" };
-    private static readonly Dictionary<LightProp, SliderProp> lightSliderProp;
-    private static readonly string[,] sliderNames =
+    private static readonly string[] LightTypes = { "normal", "spot", "point" };
+    private static readonly Dictionary<LightProp, SliderProp> LightSliderProp;
+    private static readonly string[,] SliderNames =
     {
-        { "lights", "x" }, { "lights", "y" }, { "lights", "intensity" }, { "lights", "shadow" }, { "lights", "spot" },
-        { "lights", "range" }, { "backgroundWindow", "red" }, { "backgroundWindow", "green" },
+        { "lights", "x" },
+        { "lights", "y" },
+        { "lights", "intensity" },
+        { "lights", "shadow" },
+        { "lights", "spot" },
+        { "lights", "range" },
+        { "backgroundWindow", "red" },
+        { "backgroundWindow", "green" },
         { "backgroundWindow", "blue" },
     };
 
@@ -38,7 +45,7 @@ public class LightsPane : BasePane
         var rotation = LightProperty.DefaultRotation.eulerAngles;
         var range = GameMain.Instance.MainLight.GetComponent<Light>().range;
 
-        lightSliderProp = new()
+        LightSliderProp = new()
         {
             [LightProp.LightRotX] = new(0f, 360f, rotation.x, rotation.x),
             [LightProp.LightRotY] = new(0f, 360f, rotation.y, rotation.y),
@@ -67,7 +74,7 @@ public class LightsPane : BasePane
         this.lightManager.ListModified += (_, _) =>
             UpdateList();
 
-        lightTypeGrid = new(Translation.GetArray("lightType", lightTypes));
+        lightTypeGrid = new(Translation.GetArray("lightType", LightTypes));
         lightTypeGrid.ControlEvent += (_, _) =>
             SetCurrentLightType();
 
@@ -98,9 +105,9 @@ public class LightsPane : BasePane
         for (var i = 0; i < numberOfLightProps; i++)
         {
             var lightProp = (LightProp)i;
-            var sliderProp = lightSliderProp[lightProp];
+            var sliderProp = LightSliderProp[lightProp];
 
-            var slider = new Slider(Translation.Get(sliderNames[i, 0], sliderNames[i, 1]), sliderProp)
+            var slider = new Slider(Translation.Get(SliderNames[i, 0], SliderNames[i, 1]), sliderProp)
             {
                 HasTextField = true,
                 HasReset = true,
@@ -227,7 +234,7 @@ public class LightsPane : BasePane
     {
         updating = true;
 
-        lightTypeGrid.SetItems(Translation.GetArray("lightType", lightTypes));
+        lightTypeGrid.SetItems(Translation.GetArray("lightType", LightTypes));
         lightDropdown.SetDropdownItems(lightManager.LightNameList);
         deleteLightButton.Label = Translation.Get("lightsPane", "delete");
         disableToggle.Label = Translation.Get("lightsPane", "disable");
@@ -235,7 +242,7 @@ public class LightsPane : BasePane
 
         for (var lightProp = LightProp.LightRotX; lightProp <= LightProp.Blue; lightProp++)
             lightSlider[lightProp].Label =
-                Translation.Get(sliderNames[(int)lightProp, 0], sliderNames[(int)lightProp, 1]);
+                Translation.Get(SliderNames[(int)lightProp, 0], SliderNames[(int)lightProp, 1]);
 
         colorToggle.Label = Translation.Get("lightsPane", "colour");
         resetPropsButton.Label = Translation.Get("lightsPane", "resetProperties");

+ 19 - 8
src/MeidoPhotoStudio.Plugin/GUI/Panes/BasePane.cs

@@ -7,9 +7,6 @@ public abstract class BasePane
     protected BaseWindow parent;
     protected bool updating;
 
-    public virtual bool Visible { get; set; }
-    public virtual bool Enabled { get; set; }
-
     protected BasePane() =>
         Translation.ReloadTranslationEvent += OnReloadTranslation;
 
@@ -17,18 +14,32 @@ public abstract class BasePane
     ~BasePane() =>
         Translation.ReloadTranslationEvent -= OnReloadTranslation;
 
+    public virtual bool Visible { get; set; }
+
+    public virtual bool Enabled { get; set; }
+
     public virtual void SetParent(BaseWindow window) =>
         parent = window;
 
-    public virtual void UpdatePane() { }
+    public virtual void UpdatePane()
+    {
+    }
 
-    public virtual void Draw() { }
+    public virtual void Draw()
+    {
+    }
 
-    public virtual void Activate() { }
+    public virtual void Activate()
+    {
+    }
 
-    public virtual void Deactivate() { }
+    public virtual void Deactivate()
+    {
+    }
 
-    protected virtual void ReloadTranslation() { }
+    protected virtual void ReloadTranslation()
+    {
+    }
 
     private void OnReloadTranslation(object sender, EventArgs args) =>
         ReloadTranslation();

+ 2 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/CallWindowPanes/MaidSelectorPane.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -117,8 +118,7 @@ public class MaidSelectorPane : BasePane
             GUI.Label(
                 new(95f, y + 30f, buttonWidth - 80f, buttonHeight),
                 $"{meido.LastName}\n{meido.FirstName}",
-                selectedMaid ? labelSelectedStyle : labelStyle
-            );
+                selectedMaid ? labelSelectedStyle : labelStyle);
         }
 
         GUI.EndScrollView();

+ 27 - 28
src/MeidoPhotoStudio.Plugin/GUI/Panes/FaceWindowPanes/MaidFaceBlendPane.cs

@@ -1,13 +1,14 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class MaidFaceBlendPane : BasePane
 {
-    private static readonly string[] tabTranslations = { "baseTab", "customTab" };
+    private static readonly string[] TabTranslations = { "baseTab", "customTab" };
 
     private readonly MeidoManager meidoManager;
     private readonly SelectionGrid faceBlendSourceGrid;
@@ -21,31 +22,13 @@ public class MaidFaceBlendPane : BasePane
     private bool facePresetMode;
     private bool faceListEnabled;
 
-    private Dictionary<string, List<string>> CurrentFaceDict =>
-        facePresetMode ? Constants.CustomFaceDict : Constants.FaceDict;
-
-    private List<string> CurrentFaceGroupList =>
-        facePresetMode ? Constants.CustomFaceGroupList : Constants.FaceGroupList;
-
-    private string SelectedFaceGroup =>
-        CurrentFaceGroupList[faceBlendCategoryDropdown.SelectedItemIndex];
-
-    private List<string> CurrentFaceList =>
-        CurrentFaceDict[SelectedFaceGroup];
-
-    private int SelectedFaceIndex =>
-        faceBlendDropdown.SelectedItemIndex;
-
-    private string SelectedFace =>
-        CurrentFaceList[SelectedFaceIndex];
-
     public MaidFaceBlendPane(MeidoManager meidoManager)
     {
         Constants.CustomFaceChange += OnPresetChange;
 
         this.meidoManager = meidoManager;
 
-        faceBlendSourceGrid = new(Translation.GetArray("maidFaceWindow", tabTranslations));
+        faceBlendSourceGrid = new(Translation.GetArray("maidFaceWindow", TabTranslations));
 
         faceBlendSourceGrid.ControlEvent += (_, _) =>
         {
@@ -96,6 +79,24 @@ public class MaidFaceBlendPane : BasePane
         faceListEnabled = CurrentFaceList.Count > 0;
     }
 
+    private Dictionary<string, List<string>> CurrentFaceDict =>
+        facePresetMode ? Constants.CustomFaceDict : Constants.FaceDict;
+
+    private List<string> CurrentFaceGroupList =>
+        facePresetMode ? Constants.CustomFaceGroupList : Constants.FaceGroupList;
+
+    private string SelectedFaceGroup =>
+        CurrentFaceGroupList[faceBlendCategoryDropdown.SelectedItemIndex];
+
+    private List<string> CurrentFaceList =>
+        CurrentFaceDict[SelectedFaceGroup];
+
+    private int SelectedFaceIndex =>
+        faceBlendDropdown.SelectedItemIndex;
+
+    private string SelectedFace =>
+        CurrentFaceList[SelectedFaceIndex];
+
     public override void Draw()
     {
         const float buttonHeight = 30;
@@ -138,23 +139,21 @@ public class MaidFaceBlendPane : BasePane
     protected override void ReloadTranslation()
     {
         updating = true;
-        faceBlendSourceGrid.SetItems(Translation.GetArray("maidFaceWindow", tabTranslations));
+        faceBlendSourceGrid.SetItems(Translation.GetArray("maidFaceWindow", TabTranslations));
 
         if (!facePresetMode)
             faceBlendCategoryDropdown.SetDropdownItems(
-                Translation.GetArray("faceBlendCategory", Constants.FaceGroupList)
-            );
+                Translation.GetArray("faceBlendCategory", Constants.FaceGroupList));
 
         updating = false;
     }
 
     private string[] UIFaceList() =>
         CurrentFaceList.Count is 0
-            ? (new[] { "No Face Presets" })
+            ? new[] { "No Face Presets" }
             : CurrentFaceList.Select(face => facePresetMode
                 ? Path.GetFileNameWithoutExtension(face)
-                : Translation.Get("faceBlendPresetsDropdown", face)
-            ).ToArray();
+                : Translation.Get("faceBlendPresetsDropdown", face)).ToArray();
 
     private void OnPresetChange(object sender, PresetChangeEventArgs args)
     {
@@ -173,8 +172,8 @@ public class MaidFaceBlendPane : BasePane
             updating = true;
             faceBlendSourceGrid.SelectedItemIndex = 1;
             faceBlendCategoryDropdown.SetDropdownItems(
-                CurrentFaceGroupList.ToArray(), CurrentFaceGroupList.IndexOf(args.Category)
-            );
+                CurrentFaceGroupList.ToArray(), CurrentFaceGroupList.IndexOf(args.Category));
+
             updating = false;
             faceBlendDropdown.SetDropdownItems(UIFaceList(), CurrentFaceList.IndexOf(args.Path));
         }

+ 80 - 97
src/MeidoPhotoStudio.Plugin/GUI/Panes/FaceWindowPanes/MaidFaceSliderPane.cs

@@ -2,8 +2,10 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using Newtonsoft.Json;
 using UnityEngine;
+
 using static MeidoPhotoStudio.Plugin.Meido;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -12,54 +14,30 @@ public class MaidFaceSliderPane : BasePane
 {
     private static readonly Dictionary<string, float> SliderLimits = new()
     {
-        // Eye Shut
-        ["eyeclose"] = 1f,
-        // Eye Smile
-        ["eyeclose2"] = 1f,
-        // Glare
-        ["eyeclose3"] = 1f,
-        // Wide Eyes
-        ["eyebig"] = 1f,
-        // Wink 1
-        ["eyeclose6"] = 1f,
-        // Wink 2
-        ["eyeclose5"] = 1f,
-        // Highlight
-        ["hitomih"] = 2f,
-        // Pupil Size
-        ["hitomis"] = 3f,
-        // Brow 1
-        ["mayuha"] = 1f,
-        // Brow 2
-        ["mayuw"] = 1f,
-        // Brow Up
-        ["mayuup"] = 1f,
-        // Brow Down 1
-        ["mayuv"] = 1f,
-        // Brow Down 2
-        ["mayuvhalf"] = 1f,
-        // Mouth Open 1
-        ["moutha"] = 1f,
-        // Mouth Open 2
-        ["mouths"] = 1f,
-        // Mouth Narrow
-        ["mouthc"] = 1f,
-        // Mouth Widen
-        ["mouthi"] = 1f,
-        // Smile
-        ["mouthup"] = 1.4f,
-        // Frown
-        ["mouthdw"] = 1f,
-        // Mouth Pucker
-        ["mouthhe"] = 1f,
-        // Grin
-        ["mouthuphalf"] = 2f,
-        // Tongue Out
-        ["tangout"] = 1f,
-        // Tongue Up
-        ["tangup"] = 1f,
-        // Tongue Base
-        ["tangopen"] = 1f,
+        ["eyeclose"] = 1f, // Eye Shut
+        ["eyeclose2"] = 1f, // Eye Smile
+        ["eyeclose3"] = 1f, // Glare
+        ["eyebig"] = 1f, // Wide Eyes
+        ["eyeclose6"] = 1f, // Wink 1
+        ["eyeclose5"] = 1f, // Wink 2
+        ["hitomih"] = 2f, // Highlight
+        ["hitomis"] = 3f, // Pupil Size
+        ["mayuha"] = 1f, // Brow 1
+        ["mayuw"] = 1f, // Brow 2
+        ["mayuup"] = 1f, // Brow Up
+        ["mayuv"] = 1f, // Brow Down 1
+        ["mayuvhalf"] = 1f, // Brow Down 2
+        ["moutha"] = 1f, // Mouth Open 1
+        ["mouths"] = 1f, // Mouth Open 2
+        ["mouthc"] = 1f, // Mouth Narrow
+        ["mouthi"] = 1f, // Mouth Widen
+        ["mouthup"] = 1.4f, // Smile
+        ["mouthdw"] = 1f, // Frown
+        ["mouthhe"] = 1f, // Mouth Pucker
+        ["mouthuphalf"] = 2f, // Grin
+        ["tangout"] = 1f, // Tongue Out
+        ["tangup"] = 1f, // Tongue Up
+        ["tangopen"] = 1f, // Tongue Base
     };
 
     private readonly MeidoManager meidoManager;
@@ -67,48 +45,13 @@ public class MaidFaceSliderPane : BasePane
 
     private bool hasTangOpen;
 
-    private static void InitializeSliderLimits(Dictionary<string, BaseControl> controls)
-    {
-        try
-        {
-            var sliderLimitsPath = Path.Combine(Constants.databasePath, "face_slider_limits.json");
-            var sliderLimitsJson = File.ReadAllText(sliderLimitsPath);
-
-            foreach (var kvp in JsonConvert.DeserializeObject<Dictionary<string, float>>(sliderLimitsJson))
-            {
-                var key = kvp.Key;
-
-                if (faceKeys.Contains(key) && controls.ContainsKey(key))
-                {
-                    var limit = kvp.Value;
-
-                    limit = kvp.Value >= 1f ? limit : SliderLimits[key];
-
-                    var slider = (Slider)controls[kvp.Key];
-
-                    slider.SetBounds(slider.Left, limit);
-                }
-                else
-                    Utility.LogWarning($"'{key}' is not a valid face key");
-            }
-        }
-        catch (IOException e)
-        {
-            Utility.LogWarning($"Could not open face slider limit database because {e.Message}");
-        }
-        catch (Exception e)
-        {
-            Utility.LogError($"Could not apply face slider limit database because {e.Message}");
-        }
-    }
-
     public MaidFaceSliderPane(MeidoManager meidoManager)
     {
         this.meidoManager = meidoManager;
 
         faceControls = new();
 
-        foreach (var key in faceKeys)
+        foreach (var key in FaceKeys)
         {
             var uiName = Translation.Get("faceBlendValues", key);
             var slider = new Slider(uiName, 0f, SliderLimits[key]);
@@ -120,7 +63,7 @@ public class MaidFaceSliderPane : BasePane
             faceControls[key] = slider;
         }
 
-        foreach (var key in faceToggleKeys)
+        foreach (var key in FaceToggleKeys)
         {
             var uiName = Translation.Get("faceBlendValues", key);
             var toggle = new Toggle(uiName);
@@ -140,20 +83,23 @@ public class MaidFaceSliderPane : BasePane
         updating = true;
         var meido = meidoManager.ActiveMeido;
 
-        for (var i = 0; i < faceKeys.Length; i++)
+        for (var i = 0; i < FaceKeys.Length; i++)
         {
-            var slider = (Slider)faceControls[faceKeys[i]];
+            var slider = (Slider)faceControls[FaceKeys[i]];
 
             try
             {
-                slider.Value = meido.GetFaceBlendValue(faceKeys[i]);
+                slider.Value = meido.GetFaceBlendValue(FaceKeys[i]);
+            }
+            catch
+            {
+                // Ignored
             }
-            catch { /* Ignored */ }
         }
 
-        for (var i = 0; i < faceToggleKeys.Length; i++)
+        for (var i = 0; i < FaceToggleKeys.Length; i++)
         {
-            var hash = faceToggleKeys[i];
+            var hash = FaceToggleKeys[i];
             var toggle = (Toggle)faceControls[hash];
 
             toggle.Value = meido.GetFaceBlendValue(hash) > 0f;
@@ -195,18 +141,55 @@ public class MaidFaceSliderPane : BasePane
 
     protected override void ReloadTranslation()
     {
-        for (var i = 0; i < faceKeys.Length; i++)
+        for (var i = 0; i < FaceKeys.Length; i++)
         {
-            var slider = (Slider)faceControls[faceKeys[i]];
+            var slider = (Slider)faceControls[FaceKeys[i]];
 
-            slider.Label = Translation.Get("faceBlendValues", faceKeys[i]);
+            slider.Label = Translation.Get("faceBlendValues", FaceKeys[i]);
         }
 
-        for (var i = 0; i < faceToggleKeys.Length; i++)
+        for (var i = 0; i < FaceToggleKeys.Length; i++)
+        {
+            var toggle = (Toggle)faceControls[FaceToggleKeys[i]];
+
+            toggle.Label = Translation.Get("faceBlendValues", FaceToggleKeys[i]);
+        }
+    }
+
+    private static void InitializeSliderLimits(Dictionary<string, BaseControl> controls)
+    {
+        try
         {
-            var toggle = (Toggle)faceControls[faceToggleKeys[i]];
+            var sliderLimitsPath = Path.Combine(Constants.DatabasePath, "face_slider_limits.json");
+            var sliderLimitsJson = File.ReadAllText(sliderLimitsPath);
+
+            foreach (var kvp in JsonConvert.DeserializeObject<Dictionary<string, float>>(sliderLimitsJson))
+            {
+                var key = kvp.Key;
+
+                if (FaceKeys.Contains(key) && controls.ContainsKey(key))
+                {
+                    var limit = kvp.Value;
+
+                    limit = kvp.Value >= 1f ? limit : SliderLimits[key];
+
+                    var slider = (Slider)controls[kvp.Key];
 
-            toggle.Label = Translation.Get("faceBlendValues", faceToggleKeys[i]);
+                    slider.SetBounds(slider.Left, limit);
+                }
+                else
+                {
+                    Utility.LogWarning($"'{key}' is not a valid face key");
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            Utility.LogWarning($"Could not open face slider limit database because {e.Message}");
+        }
+        catch (Exception e)
+        {
+            Utility.LogError($"Could not apply face slider limit database because {e.Message}");
         }
     }
 

+ 3 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/BG2WindowPane.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class BG2WindowPane : BaseMainWindowPane
 {
-    private static readonly string[] tabNames = { "props", "myRoom", "mod" };
+    private static readonly string[] TabNames = { "props", "myRoom", "mod" };
 
     private readonly MeidoManager meidoManager;
     private readonly PropManager propManager;
@@ -29,7 +29,7 @@ public class BG2WindowPane : BaseMainWindowPane
         attachPropPane = AddPane(new AttachPropPane(this.meidoManager, propManager));
         propManagerPane = AddPane(new PropManagerPane(propManager));
 
-        propTabs = new(Translation.GetArray("propsPaneTabs", tabNames));
+        propTabs = new(Translation.GetArray("propsPaneTabs", TabNames));
         propTabs.ControlEvent += (_, _) =>
             currentPropsPane = Panes[propTabs.SelectedItemIndex];
 
@@ -59,5 +59,5 @@ public class BG2WindowPane : BaseMainWindowPane
     }
 
     protected override void ReloadTranslation() =>
-        propTabs.SetItems(Translation.GetArray("propsPaneTabs", tabNames));
+        propTabs.SetItems(Translation.GetArray("propsPaneTabs", TabNames));
 }

+ 5 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/BGWindowPane.cs

@@ -13,9 +13,11 @@ public class BGWindowPane : BaseMainWindowPane
     private readonly Button sceneManagerButton;
 
     public BGWindowPane(
-        EnvironmentManager environmentManager, LightManager lightManager, EffectManager effectManager,
-        SceneWindow sceneWindow, CameraManager cameraManager
-    )
+        EnvironmentManager environmentManager,
+        LightManager lightManager,
+        EffectManager effectManager,
+        SceneWindow sceneWindow,
+        CameraManager cameraManager)
     {
         sceneManagerButton = new(Translation.Get("backgroundWindow", "manageScenesButton"));
         sceneManagerButton.ControlEvent += (_, _) =>

+ 3 - 4
src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/CallWindowPane.cs

@@ -13,11 +13,11 @@ public class CallWindowPane : BaseMainWindowPane
     {
         this.meidoManager = meidoManager;
 
-        placementDropdown = new(Translation.GetArray("placementDropdown", MaidPlacementUtility.placementTypes));
+        placementDropdown = new(Translation.GetArray("placementDropdown", MaidPlacementUtility.PlacementTypes));
 
         placementOKButton = new(Translation.Get("maidCallWindow", "okButton"));
         placementOKButton.ControlEvent += (_, _) =>
-            this.meidoManager.PlaceMeidos(MaidPlacementUtility.placementTypes[placementDropdown.SelectedItemIndex]);
+            this.meidoManager.PlaceMeidos(MaidPlacementUtility.PlacementTypes[placementDropdown.SelectedItemIndex]);
 
         maidSelectorPane = AddPane(new MaidSelectorPane(this.meidoManager));
     }
@@ -37,8 +37,7 @@ public class CallWindowPane : BaseMainWindowPane
     protected override void ReloadTranslation()
     {
         placementDropdown.SetDropdownItems(
-            Translation.GetArray("placementDropdown", MaidPlacementUtility.placementTypes)
-        );
+            Translation.GetArray("placementDropdown", MaidPlacementUtility.PlacementTypes));
 
         placementOKButton.Label = Translation.Get("maidCallWindow", "okButton");
     }

+ 2 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/PoseWindowPane.cs

@@ -8,7 +8,7 @@ public class PoseWindowPane : BaseMainWindowPane
     private readonly MaidPoseSelectorPane maidPosePane;
     private readonly SavePosePane savePosePane;
     private readonly MaidSwitcherPane maidSwitcherPane;
-    private readonly MaidFaceLookPane maidFaceLookPane;
+    private readonly MaidFreeLookPane maidFaceLookPane;
     private readonly MpnAttachPropPane mpnAttachPropPane;
     private readonly MaidDressingPane maidDressingPane;
     private readonly GravityControlPane gravityControlPane;
@@ -34,7 +34,7 @@ public class PoseWindowPane : BaseMainWindowPane
         maidPosePane = AddPane(new MaidPoseSelectorPane(meidoManager));
         savePosePane = AddPane(new SavePosePane(meidoManager));
 
-        maidFaceLookPane = AddPane(new MaidFaceLookPane(meidoManager));
+        maidFaceLookPane = AddPane(new MaidFreeLookPane(meidoManager));
         maidFaceLookPane.Enabled = false;
 
         freeLookToggle = new(Translation.Get("freeLookPane", "freeLookToggle"), false);

+ 22 - 21
src/MeidoPhotoStudio.Plugin/GUI/Panes/MainWindowPanes/SettingsWindowPane.cs

@@ -1,20 +1,21 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class SettingsWindowPane : BaseMainWindowPane
 {
-    private static readonly string[] headerTranslationKeys =
+    private static readonly string[] HeaderTranslationKeys =
     {
-        "controls", "controlsGeneral", "controlsMaids", "controlsCamera", "controlsDragPoint", "controlsScene"
+        "controls", "controlsGeneral", "controlsMaids", "controlsCamera", "controlsDragPoint", "controlsScene",
     };
 
-    private static readonly Dictionary<string, string> headers = new();
-    private static readonly string[] actionTranslationKeys;
-    private static readonly string[] actionLabels;
+    private static readonly Dictionary<string, string> Headers = new();
+    private static readonly string[] ActionTranslationKeys;
+    private static readonly string[] ActionLabels;
 
     private readonly Button reloadTranslationButton;
     private readonly Button reloadAllPresetsButton;
@@ -22,16 +23,16 @@ public class SettingsWindowPane : BaseMainWindowPane
 
     static SettingsWindowPane()
     {
-        actionTranslationKeys = Enum.GetNames(typeof(MpsKey))
+        ActionTranslationKeys = Enum.GetNames(typeof(MpsKey))
             .Select(action => char.ToLowerInvariant(action[0]) + action.Substring(1))
             .ToArray();
 
-        actionLabels = new string[actionTranslationKeys.Length];
+        ActionLabels = new string[ActionTranslationKeys.Length];
     }
 
     public SettingsWindowPane()
     {
-        rebindButtons = new KeyRebindButton[actionTranslationKeys.Length];
+        rebindButtons = new KeyRebindButton[ActionTranslationKeys.Length];
 
         for (var i = 0; i < rebindButtons.Length; i++)
         {
@@ -43,11 +44,11 @@ public class SettingsWindowPane : BaseMainWindowPane
 
             rebindButtons[i] = button;
 
-            actionLabels[i] = Translation.Get("controls", actionTranslationKeys[i]);
+            ActionLabels[i] = Translation.Get("controls", ActionTranslationKeys[i]);
         }
 
-        for (var i = 0; i < headerTranslationKeys.Length; i++)
-            headers[headerTranslationKeys[i]] = Translation.Get("settingsHeaders", headerTranslationKeys[i]);
+        for (var i = 0; i < HeaderTranslationKeys.Length; i++)
+            Headers[HeaderTranslationKeys[i]] = Translation.Get("settingsHeaders", HeaderTranslationKeys[i]);
 
         reloadTranslationButton = new(Translation.Get("settingsLabels", "reloadTranslation"));
         reloadTranslationButton.ControlEvent += (_, _) =>
@@ -66,32 +67,32 @@ public class SettingsWindowPane : BaseMainWindowPane
     {
         scrollPos = GUILayout.BeginScrollView(scrollPos);
 
-        MpsGui.Header(headers["controls"]);
+        MpsGui.Header(Headers["controls"]);
         MpsGui.WhiteLine();
 
-        MpsGui.Header(headers["controlsGeneral"]);
+        MpsGui.Header(Headers["controlsGeneral"]);
         MpsGui.WhiteLine();
 
         for (var key = MpsKey.Activate; key <= MpsKey.ToggleMessage; key++)
             DrawSetting(key);
 
-        MpsGui.Header(headers["controlsMaids"]);
+        MpsGui.Header(Headers["controlsMaids"]);
         MpsGui.WhiteLine();
         DrawSetting(MpsKey.MeidoUndressing);
 
-        MpsGui.Header(headers["controlsCamera"]);
+        MpsGui.Header(Headers["controlsCamera"]);
         MpsGui.WhiteLine();
 
         for (var key = MpsKey.CameraLayer; key <= MpsKey.CameraLoad; key++)
             DrawSetting(key);
 
-        MpsGui.Header(headers["controlsDragPoint"]);
+        MpsGui.Header(Headers["controlsDragPoint"]);
         MpsGui.WhiteLine();
 
         for (var key = MpsKey.DragSelect; key <= MpsKey.DragFinger; key++)
             DrawSetting(key);
 
-        MpsGui.Header(headers["controlsScene"]);
+        MpsGui.Header(Headers["controlsScene"]);
         MpsGui.WhiteLine();
 
         for (var key = MpsKey.SaveScene; key <= MpsKey.OpenSceneManager; key++)
@@ -119,10 +120,10 @@ public class SettingsWindowPane : BaseMainWindowPane
     protected override void ReloadTranslation()
     {
         for (var i = 0; i < rebindButtons.Length; i++)
-            actionLabels[i] = Translation.Get("controls", actionTranslationKeys[i]);
+            ActionLabels[i] = Translation.Get("controls", ActionTranslationKeys[i]);
 
-        for (var i = 0; i < headerTranslationKeys.Length; i++)
-            headers[headerTranslationKeys[i]] = Translation.Get("settingsHeaders", headerTranslationKeys[i]);
+        for (var i = 0; i < HeaderTranslationKeys.Length; i++)
+            Headers[HeaderTranslationKeys[i]] = Translation.Get("settingsHeaders", HeaderTranslationKeys[i]);
 
         reloadTranslationButton.Label = Translation.Get("settingsLabels", "reloadTranslation");
         reloadAllPresetsButton.Label = Translation.Get("settingsLabels", "reloadAllPresets");
@@ -133,7 +134,7 @@ public class SettingsWindowPane : BaseMainWindowPane
         var keyIndex = (int)key;
 
         GUILayout.BeginHorizontal();
-        GUILayout.Label(actionLabels[keyIndex]);
+        GUILayout.Label(ActionLabels[keyIndex]);
         GUILayout.FlexibleSpace();
         rebindButtons[keyIndex].Draw(GUILayout.Width(90f));
 

+ 17 - 15
src/MeidoPhotoStudio.Plugin/GUI/Panes/OtherPanes/TabsPane.cs

@@ -1,52 +1,54 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class TabsPane : BasePane
 {
-    private static readonly string[] tabNames = { "call", "pose", "face", "bg", "bg2" };
-
-    private readonly SelectionGrid Tabs;
+    private static readonly string[] TabNames = { "call", "pose", "face", "bg", "bg2" };
 
-    public event EventHandler TabChange;
+    private readonly SelectionGrid tabs;
 
     private Constants.Window selectedTab;
 
-    public Constants.Window SelectedTab
-    {
-        get => selectedTab;
-        set => Tabs.SelectedItemIndex = (int)value;
-    }
-
     public TabsPane()
     {
         Translation.ReloadTranslationEvent += (_, _) =>
             ReloadTranslation();
 
-        Tabs = new(Translation.GetArray("tabs", tabNames));
-        Tabs.ControlEvent += (_, _) =>
+        tabs = new(Translation.GetArray("tabs", TabNames));
+        tabs.ControlEvent += (_, _) =>
             OnChangeTab();
     }
 
+    public event EventHandler TabChange;
+
+    public Constants.Window SelectedTab
+    {
+        get => selectedTab;
+        set => tabs.SelectedItemIndex = (int)value;
+    }
+
     public override void Draw()
     {
-        Tabs.Draw(GUILayout.ExpandWidth(false));
+        tabs.Draw(GUILayout.ExpandWidth(false));
         MpsGui.BlackLine();
     }
 
     protected override void ReloadTranslation()
     {
         updating = true;
-        Tabs.SetItems(Translation.GetArray("tabs", tabNames), Tabs.SelectedItemIndex);
+        tabs.SetItems(Translation.GetArray("tabs", TabNames), tabs.SelectedItemIndex);
         updating = false;
     }
+
     private void OnChangeTab()
     {
         if (updating)
             return;
 
-        selectedTab = (Constants.Window)Tabs.SelectedItemIndex;
+        selectedTab = (Constants.Window)tabs.SelectedItemIndex;
         TabChange?.Invoke(null, EventArgs.Empty);
     }
 }

+ 11 - 8
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/CopyPosePane.cs

@@ -1,4 +1,5 @@
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -12,14 +13,6 @@ public class CopyPosePane : BasePane
     private int[] copyMeidoSlot;
     private string copyIKHeader;
 
-    private bool PlentyOfMaids =>
-        meidoManager.ActiveMeidoList.Count >= 2;
-
-    private Meido FromMeido =>
-        meidoManager.HasActiveMeido
-            ? meidoManager.ActiveMeidoList[copyMeidoSlot[meidoDropdown.SelectedItemIndex]]
-            : null;
-
     public CopyPosePane(MeidoManager meidoManager)
     {
         this.meidoManager = meidoManager;
@@ -33,6 +26,14 @@ public class CopyPosePane : BasePane
         copyIKHeader = Translation.Get("copyPosePane", "header");
     }
 
+    private bool PlentyOfMaids =>
+        meidoManager.ActiveMeidoList.Count >= 2;
+
+    private Meido FromMeido =>
+        meidoManager.HasActiveMeido
+            ? meidoManager.ActiveMeidoList[copyMeidoSlot[meidoDropdown.SelectedItemIndex]]
+            : null;
+
     public override void Draw()
     {
         GUI.enabled = PlentyOfMaids;
@@ -81,6 +82,8 @@ public class CopyPosePane : BasePane
             meidoDropdown.SetDropdownItems(dropdownList, 0);
         }
         else
+        {
             meidoDropdown.SetDropdownItems(new[] { Translation.Get("systemMessage", "noMaids") });
+        }
     }
 }

+ 13 - 11
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/HandPresetPane.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -20,15 +21,6 @@ public class HandPresetPane : BasePane
     private string previousCategory;
     private bool presetListEnabled = true;
 
-    private string SelectedCategory =>
-        Constants.CustomHandGroupList[presetCategoryDropdown.SelectedItemIndex];
-
-    private List<string> CurrentPresetList =>
-        Constants.CustomHandDict[SelectedCategory];
-
-    private string CurrentPreset =>
-        CurrentPresetList[presetDropdown.SelectedItemIndex];
-
     public HandPresetPane(MeidoManager meidoManager)
     {
         Constants.CustomHandChange += OnPresetChange;
@@ -69,6 +61,15 @@ public class HandPresetPane : BasePane
         presetListEnabled = CurrentPresetList.Count > 0;
     }
 
+    private string SelectedCategory =>
+        Constants.CustomHandGroupList[presetCategoryDropdown.SelectedItemIndex];
+
+    private List<string> CurrentPresetList =>
+        Constants.CustomHandDict[SelectedCategory];
+
+    private string CurrentPreset =>
+        CurrentPresetList[presetDropdown.SelectedItemIndex];
+
     public override void Draw()
     {
         var dropdownWidth = GUILayout.Width(156f);
@@ -112,7 +113,9 @@ public class HandPresetPane : BasePane
         presetListEnabled = CurrentPresetList.Count > 0;
 
         if (previousCategory == SelectedCategory)
+        {
             presetDropdown.SelectedItemIndex = 0;
+        }
         else
         {
             previousCategory = SelectedCategory;
@@ -138,8 +141,7 @@ public class HandPresetPane : BasePane
         else
         {
             presetCategoryDropdown.SetDropdownItems(
-                Constants.CustomHandGroupList.ToArray(), Constants.CustomHandGroupList.IndexOf(args.Category)
-            );
+                Constants.CustomHandGroupList.ToArray(), Constants.CustomHandGroupList.IndexOf(args.Category));
 
             presetDropdown.SetDropdownItems(UIPresetList(), CurrentPresetList.IndexOf(args.Path));
         }

+ 11 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidDressingPane.cs

@@ -1,5 +1,7 @@
 using System.Collections.Generic;
+
 using UnityEngine;
+
 using static MeidoPhotoStudio.Plugin.Meido;
 using static TBody;
 
@@ -12,10 +14,12 @@ public class MaidDressingPane : BasePane
         // main slots
         SlotID.wear, SlotID.skirt, SlotID.bra, SlotID.panz, SlotID.headset, SlotID.megane, SlotID.accUde,
         SlotID.glove, SlotID.accSenaka, SlotID.stkg, SlotID.shoes, SlotID.body,
+
         // detailed slots
         SlotID.accAshi, SlotID.accHana, SlotID.accHat, SlotID.accHeso, SlotID.accKamiSubL, SlotID.accKamiSubR,
         SlotID.accKami_1_, SlotID.accKami_2_, SlotID.accKami_3_, SlotID.accKubi, SlotID.accKubiwa, SlotID.accMiMiL,
         SlotID.accMiMiR, SlotID.accNipL, SlotID.accNipR, SlotID.accShippo, SlotID.accXXX,
+
         // unused slots
         // SlotID.mizugi, SlotID.onepiece, SlotID.accHead,
     };
@@ -34,7 +38,7 @@ public class MaidDressingPane : BasePane
         SlotID.accKami_3_,
     };
 
-    private static readonly string[] maskLabels = { "all", "underwear", "nude" };
+    private static readonly string[] MaskLabels = { "all", "underwear", "nude" };
 
     private readonly MeidoManager meidoManager;
     private readonly Dictionary<SlotID, Toggle> clothingToggles;
@@ -81,7 +85,7 @@ public class MaidDressingPane : BasePane
         pantsuShiftToggle.ControlEvent += (_, _) =>
             ToggleCurling(Curl.Shift, pantsuShiftToggle.Value);
 
-        maskModeGrid = new(Translation.GetArray("clothing", maskLabels));
+        maskModeGrid = new(Translation.GetArray("clothing", MaskLabels));
         maskModeGrid.ControlEvent += (_, _) =>
             SetMaskMode((Mask)maskModeGrid.SelectedItemIndex);
 
@@ -216,7 +220,7 @@ public class MaidDressingPane : BasePane
         }
 
         updating = true;
-        maskModeGrid.SetItems(Translation.GetArray("clothing", maskLabels));
+        maskModeGrid.SetItems(Translation.GetArray("clothing", MaskLabels));
         updating = false;
 
         detailedClothingToggle.Label = Translation.Get("clothing", "detail");
@@ -254,15 +258,19 @@ public class MaidDressingPane : BasePane
         else
         {
             if (slot is SlotID.wear)
+            {
                 foreach (var wearSlot in WearSlots)
                     body.SetMask(wearSlot, enabled);
+            }
             else if (slot is SlotID.megane)
             {
                 body.SetMask(SlotID.megane, enabled);
                 body.SetMask(SlotID.accHead, enabled);
             }
             else
+            {
                 body.SetMask(slot, enabled);
+            }
         }
     }
 

+ 2 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidFreeLookPane.cs

@@ -2,7 +2,7 @@ using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
-public class MaidFaceLookPane : BasePane
+public class MaidFreeLookPane : BasePane
 {
     private readonly MeidoManager meidoManager;
     private readonly Slider lookXSlider;
@@ -12,7 +12,7 @@ public class MaidFaceLookPane : BasePane
 
     private string bindLabel;
 
-    public MaidFaceLookPane(MeidoManager meidoManager)
+    public MaidFreeLookPane(MeidoManager meidoManager)
     {
         this.meidoManager = meidoManager;
 

+ 7 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidIKPane.cs

@@ -4,8 +4,6 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class MaidIKPane : BasePane
 {
-    private enum IKToggle { IK, Release, Bone }
-
     private readonly MeidoManager meidoManager;
     private readonly Toggle ikToggle;
     private readonly Toggle releaseIKToggle;
@@ -28,6 +26,13 @@ public class MaidIKPane : BasePane
             SetIK(IKToggle.Bone, boneIKToggle.Value);
     }
 
+    private enum IKToggle
+    {
+        IK,
+        Release,
+        Bone,
+    }
+
     public override void UpdatePane()
     {
         updating = true;

+ 35 - 29
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MaidPoseSelectorPane.cs

@@ -1,13 +1,14 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class MaidPoseSelectorPane : BasePane
 {
-    private static readonly string[] tabTranslations = new[] { "baseTab", "customTab" };
+    private static readonly string[] TabTranslations = new[] { "baseTab", "customTab" };
 
     private readonly MeidoManager meidoManager;
     private readonly Button poseLeftButton;
@@ -22,33 +23,12 @@ public class MaidPoseSelectorPane : BasePane
     private bool poseListEnabled;
     private string previousPoseGroup;
 
-    private Dictionary<string, List<string>> CurrentPoseDict =>
-        customPoseMode ? Constants.CustomPoseDict : Constants.PoseDict;
-
-    private List<string> CurrentPoseGroupList =>
-        customPoseMode ? Constants.CustomPoseGroupList : Constants.PoseGroupList;
-
-    private string SelectedPoseGroup =>
-        CurrentPoseGroupList[poseGroupDropdown.SelectedItemIndex];
-
-    private List<string> CurrentPoseList =>
-        CurrentPoseDict[SelectedPoseGroup];
-
-    private int SelectedPoseIndex =>
-        poseDropdown.SelectedItemIndex;
-
-    private string SelectedPose =>
-        CurrentPoseList[SelectedPoseIndex];
-
-    private PoseInfo CurrentPoseInfo =>
-        new(SelectedPoseGroup, SelectedPose, customPoseMode);
-
     public MaidPoseSelectorPane(MeidoManager meidoManager)
     {
         Constants.CustomPoseChange += OnPresetChange;
         this.meidoManager = meidoManager;
 
-        poseModeGrid = new(Translation.GetArray("posePane", tabTranslations));
+        poseModeGrid = new(Translation.GetArray("posePane", TabTranslations));
         poseModeGrid.ControlEvent += (_, _) =>
             SetPoseMode();
 
@@ -81,6 +61,27 @@ public class MaidPoseSelectorPane : BasePane
         poseListEnabled = CurrentPoseList.Count > 0;
     }
 
+    private Dictionary<string, List<string>> CurrentPoseDict =>
+        customPoseMode ? Constants.CustomPoseDict : Constants.PoseDict;
+
+    private List<string> CurrentPoseGroupList =>
+        customPoseMode ? Constants.CustomPoseGroupList : Constants.PoseGroupList;
+
+    private string SelectedPoseGroup =>
+        CurrentPoseGroupList[poseGroupDropdown.SelectedItemIndex];
+
+    private List<string> CurrentPoseList =>
+        CurrentPoseDict[SelectedPoseGroup];
+
+    private int SelectedPoseIndex =>
+        poseDropdown.SelectedItemIndex;
+
+    private string SelectedPose =>
+        CurrentPoseList[SelectedPoseIndex];
+
+    private PoseInfo CurrentPoseInfo =>
+        new(SelectedPoseGroup, SelectedPose, customPoseMode);
+
     public override void Draw()
     {
         const float buttonHeight = 30f;
@@ -137,13 +138,14 @@ public class MaidPoseSelectorPane : BasePane
                 poseGroupDropdown.SetDropdownItems(
                     customPoseMode
                         ? CurrentPoseGroupList.ToArray()
-                        : Translation.GetArray("poseGroupDropdown", CurrentPoseGroupList)
-                );
+                        : Translation.GetArray("poseGroupDropdown", CurrentPoseGroupList));
 
             var newPoseGroupIndex = CurrentPoseGroupList.IndexOf(cachedPose.PoseGroup);
 
             if (newPoseGroupIndex < 0)
+            {
                 poseGroupDropdown.SelectedItemIndex = 0;
+            }
             else if (oldCustomPoseMode != customPoseMode || poseGroupDropdown.SelectedItemIndex != newPoseGroupIndex)
             {
                 poseGroupDropdown.SelectedItemIndex = newPoseGroupIndex;
@@ -160,7 +162,10 @@ public class MaidPoseSelectorPane : BasePane
             poseDropdown.SelectedItemIndex = newPoseIndex;
             poseListEnabled = CurrentPoseList.Count > 0;
         }
-        catch { /* ignored */ }
+        catch
+        {
+            // Ignored
+        }
         finally
         {
             updating = false;
@@ -170,7 +175,7 @@ public class MaidPoseSelectorPane : BasePane
     protected override void ReloadTranslation()
     {
         updating = true;
-        poseModeGrid.SetItems(Translation.GetArray("posePane", tabTranslations));
+        poseModeGrid.SetItems(Translation.GetArray("posePane", TabTranslations));
 
         if (!customPoseMode)
             poseGroupDropdown.SetDropdownItems(Translation.GetArray("poseGroupDropdown", Constants.PoseGroupList));
@@ -195,8 +200,7 @@ public class MaidPoseSelectorPane : BasePane
             updating = true;
             poseModeGrid.SelectedItemIndex = 1;
             poseGroupDropdown.SetDropdownItems(
-                CurrentPoseGroupList.ToArray(), CurrentPoseGroupList.IndexOf(args.Category)
-            );
+                CurrentPoseGroupList.ToArray(), CurrentPoseGroupList.IndexOf(args.Category));
 
             updating = false;
 
@@ -227,7 +231,9 @@ public class MaidPoseSelectorPane : BasePane
         poseListEnabled = CurrentPoseList.Count > 0;
 
         if (previousPoseGroup == SelectedPoseGroup)
+        {
             poseDropdown.SelectedItemIndex = 0;
+        }
         else
         {
             previousPoseGroup = SelectedPoseGroup;

+ 2 - 2
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/MpnAttachPropPane.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -95,8 +96,7 @@ public class MpnAttachPropPane : BasePane
         IEnumerable<string> dropdownList = !Constants.MpnAttachInitialized
             ? new[] { Translation.Get("systemMessage", "initializing") }
             : Translation.GetArray(
-                "mpnAttachPropNames", Constants.MpnAttachPropList.Select(mpnProp => mpnProp.MenuFile)
-            );
+                "mpnAttachPropNames", Constants.MpnAttachPropList.Select(mpnProp => mpnProp.MenuFile));
 
         mpnAttachDropdown.SetDropdownItems(dropdownList.ToArray());
     }

+ 1 - 0
src/MeidoPhotoStudio.Plugin/GUI/Panes/PoseWindowPanes/SavePosePane.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;

+ 3 - 3
src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerDirectoryPane.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneManagerDirectoryPane : BasePane
 {
-    public static readonly int listWidth = 200;
+    public static readonly int ListWidth = 200;
 
     private readonly SceneManager sceneManager;
     private readonly SceneModalWindow sceneModalWindow;
@@ -58,7 +58,7 @@ public class SceneManagerDirectoryPane : BasePane
         directorySelectedStyle.normal.background = selectedTexture;
         directorySelectedStyle.hover.background = selectedTexture;
 
-        GUILayout.BeginVertical(GUILayout.Width(Utility.GetPix(listWidth)));
+        GUILayout.BeginVertical(GUILayout.Width(Utility.GetPix(ListWidth)));
 
         listScrollPos = GUILayout.BeginScrollView(listScrollPos);
 
@@ -84,7 +84,7 @@ public class SceneManagerDirectoryPane : BasePane
 
         if (createDirectoryMode)
         {
-            directoryTextField.Draw(buttonHeight, GUILayout.Width(Utility.GetPix(listWidth - 30)));
+            directoryTextField.Draw(buttonHeight, GUILayout.Width(Utility.GetPix(ListWidth - 30)));
             cancelButton.Draw(buttonStyle, buttonHeight, GUILayout.ExpandWidth(false));
         }
         else

+ 6 - 4
src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerScenePane.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneManagerScenePane : BasePane
 {
-    public static readonly float thumbnailScale = 0.55f;
+    public static readonly float ThumbnailScale = 0.55f;
     private readonly SceneManager sceneManager;
     private readonly SceneModalWindow sceneModalWindow;
     private readonly Button addSceneButton;
@@ -36,9 +36,9 @@ public class SceneManagerScenePane : BasePane
 
         GUILayout.BeginVertical();
 
-        var sceneWidth = SceneManager.sceneDimensions.x * thumbnailScale;
-        var sceneHeight = SceneManager.sceneDimensions.y * thumbnailScale;
-        var sceneGridWidth = parent.WindowRect.width - SceneManagerDirectoryPane.listWidth;
+        var sceneWidth = SceneManager.SceneDimensions.x * ThumbnailScale;
+        var sceneHeight = SceneManager.SceneDimensions.y * ThumbnailScale;
+        var sceneGridWidth = parent.WindowRect.width - SceneManagerDirectoryPane.ListWidth;
 
         var sceneLayoutOptions = new[]
         {
@@ -64,7 +64,9 @@ public class SceneManagerScenePane : BasePane
             for (var j = 0; j < columns; j++, currentScene++)
             {
                 if (currentScene is -1)
+                {
                     addSceneButton.Draw(addSceneStyle, sceneLayoutOptions);
+                }
                 else if (currentScene < sceneManager.SceneList.Count)
                 {
                     var scene = sceneManager.SceneList[currentScene];

+ 6 - 6
src/MeidoPhotoStudio.Plugin/GUI/Panes/SceneManagerPanes/SceneManagerTitleBar.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneManagerTitleBarPane : BasePane
 {
-    private static readonly string[] sortModes = new[] { "sortName", "sortCreated", "sortModified" };
+    private static readonly string[] SortModes = new[] { "sortName", "sortCreated", "sortModified" };
 
     private readonly SceneManager sceneManager;
     private readonly Button kankyoToggle;
@@ -13,8 +13,6 @@ public class SceneManagerTitleBarPane : BasePane
     private readonly Toggle descendingToggle;
     private readonly Button closeButton;
 
-    public event System.EventHandler CloseChange;
-
     private string sortLabel;
 
     public SceneManagerTitleBarPane(SceneManager sceneManager)
@@ -28,7 +26,7 @@ public class SceneManagerTitleBarPane : BasePane
         refreshButton.ControlEvent += (_, _) =>
             sceneManager.Refresh();
 
-        sortDropdown = new(Translation.GetArray("sceneManager", sortModes), (int)sceneManager.CurrentSortMode);
+        sortDropdown = new(Translation.GetArray("sceneManager", SortModes), (int)sceneManager.CurrentSortMode);
         sortDropdown.SelectionChange += (_, _) =>
         {
             var sortMode = (SceneManager.SortMode)sortDropdown.SelectedItemIndex;
@@ -53,6 +51,8 @@ public class SceneManagerTitleBarPane : BasePane
         sortLabel = Translation.Get("sceneManager", "sortLabel");
     }
 
+    public event System.EventHandler CloseChange;
+
     public override void Draw()
     {
         var buttonStyle = new GUIStyle(GUI.skin.button)
@@ -64,7 +64,7 @@ public class SceneManagerTitleBarPane : BasePane
 
         GUILayout.BeginHorizontal();
 
-        GUILayout.BeginHorizontal(GUILayout.Width(Utility.GetPix(SceneManagerDirectoryPane.listWidth)));
+        GUILayout.BeginHorizontal(GUILayout.Width(Utility.GetPix(SceneManagerDirectoryPane.ListWidth)));
 
         var originalColour = GUI.backgroundColor;
 
@@ -118,7 +118,7 @@ public class SceneManagerTitleBarPane : BasePane
     {
         kankyoToggle.Label = Translation.Get("sceneManager", "kankyoToggle");
         refreshButton.Label = Translation.Get("sceneManager", "refreshButton");
-        sortDropdown.SetDropdownItems(Translation.GetArray("sceneManager", sortModes));
+        sortDropdown.SetDropdownItems(Translation.GetArray("sceneManager", SortModes));
         descendingToggle.Label = Translation.Get("sceneManager", "descendingToggle");
         sortLabel = Translation.Get("sceneManager", "sortLabel");
     }

+ 14 - 12
src/MeidoPhotoStudio.Plugin/GUI/Windows/BaseWindow.cs

@@ -1,24 +1,22 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public abstract class BaseWindow : BasePane
 {
-    private static int id = 765;
-
-    private static int ID =>
-        id++;
-
-    public readonly int windowID = ID;
+    public readonly int WindowID = ID;
 
     protected readonly List<BasePane> Panes = new();
 
-    public bool ActiveWindow { get; set; }
-
     protected Vector2 scrollPos;
     protected Rect windowRect = new(0f, 0f, 480f, 270f);
 
+    private static int id = 765;
+
+    public bool ActiveWindow { get; set; }
+
     public virtual Rect WindowRect
     {
         get => windowRect;
@@ -34,6 +32,12 @@ public abstract class BaseWindow : BasePane
         }
     }
 
+    protected Vector2 MiddlePosition =>
+        new((float)Screen.width / 2 - windowRect.width / 2, (float)Screen.height / 2 - windowRect.height / 2);
+
+    private static int ID =>
+        id++;
+
     public virtual void Update() =>
         HandleZoom();
 
@@ -71,10 +75,8 @@ public abstract class BaseWindow : BasePane
             pane.Deactivate();
     }
 
-    protected Vector2 MiddlePosition =>
-        new((float)Screen.width / 2 - windowRect.width / 2, (float)Screen.height / 2 - windowRect.height / 2);
-
-    protected T AddPane<T>(T pane) where T : BasePane
+    protected T AddPane<T>(T pane)
+        where T : BasePane
     {
         Panes.Add(pane);
         pane.SetParent(this);

+ 29 - 24
src/MeidoPhotoStudio.Plugin/GUI/Windows/MainWindow.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -17,29 +18,6 @@ public class MainWindow : BaseWindow
     private string closeButtonLabel;
     private Constants.Window selectedWindow;
 
-    public override Rect WindowRect
-    {
-        set
-        {
-            value.width = 240f;
-            value.height = Screen.height * 0.9f;
-
-            if (MeidoPhotoStudio.EditMode)
-                value.height *= 0.85f;
-
-            value.x = Mathf.Clamp(value.x, 0, Screen.width - value.width);
-            value.y = Mathf.Clamp(value.y, -value.height + 30, Screen.height - 50);
-
-            windowRect = value;
-        }
-    }
-
-    public BaseMainWindowPane this[Constants.Window id]
-    {
-        get => windowPanes[id];
-        set => AddWindow(id, value);
-    }
-
     // TODO: Find a better way of doing this
     public MainWindow(MeidoManager meidoManager, PropManager propManager, LightManager lightManager)
     {
@@ -68,7 +46,9 @@ public class MainWindow : BaseWindow
         settingsButton.ControlEvent += (_, _) =>
         {
             if (selectedWindow is Constants.Window.Settings)
+            {
                 ChangeTab();
+            }
             else
             {
                 settingsButton.Label = closeButtonLabel;
@@ -77,6 +57,29 @@ public class MainWindow : BaseWindow
         };
     }
 
+    public override Rect WindowRect
+    {
+        set
+        {
+            value.width = 240f;
+            value.height = Screen.height * 0.9f;
+
+            if (MeidoPhotoStudio.EditMode)
+                value.height *= 0.85f;
+
+            value.x = Mathf.Clamp(value.x, 0, Screen.width - value.width);
+            value.y = Mathf.Clamp(value.y, -value.height + 30, Screen.height - 50);
+
+            windowRect = value;
+        }
+    }
+
+    public BaseMainWindowPane this[Constants.Window id]
+    {
+        get => windowPanes[id];
+        set => AddWindow(id, value);
+    }
+
     public void AddWindow(Constants.Window id, BaseMainWindowPane window)
     {
         if (windowPanes.ContainsKey(id))
@@ -122,7 +125,7 @@ public class MainWindow : BaseWindow
         };
 
         GUILayout.BeginHorizontal();
-        GUILayout.Label(MeidoPhotoStudio.pluginString, labelStyle);
+        GUILayout.Label(MeidoPhotoStudio.PluginString, labelStyle);
         GUILayout.FlexibleSpace();
 
         GUI.enabled = !InputManager.Listening;
@@ -169,7 +172,9 @@ public class MainWindow : BaseWindow
             ChangeWindow(newWindow);
         }
         else
+        {
             currentWindowPane.UpdatePanes();
+        }
     }
 
     private void ChangeWindow(Constants.Window window)

+ 11 - 10
src/MeidoPhotoStudio.Plugin/GUI/Windows/MessageWindow.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -13,16 +14,6 @@ public class MessageWindow : BaseWindow
 
     private int fontSize = 25;
 
-    public override Rect WindowRect
-    {
-        set
-        {
-            value.width = Mathf.Clamp(Screen.width * 0.4f, 440, Mathf.Infinity);
-            value.height = Mathf.Clamp(Screen.height * 0.15f, 150, Mathf.Infinity);
-            base.WindowRect = value;
-        }
-    }
-
     public MessageWindow(MessageWindowManager messageWindowManager)
     {
         this.messageWindowManager = messageWindowManager;
@@ -42,6 +33,16 @@ public class MessageWindow : BaseWindow
         okButton.ControlEvent += ShowMessage;
     }
 
+    public override Rect WindowRect
+    {
+        set
+        {
+            value.width = Mathf.Clamp(Screen.width * 0.4f, 440, Mathf.Infinity);
+            value.height = Mathf.Clamp(Screen.height * 0.15f, 150, Mathf.Infinity);
+            base.WindowRect = value;
+        }
+    }
+
     public override void Update()
     {
         base.Update();

+ 34 - 32
src/MeidoPhotoStudio.Plugin/GUI/Windows/SceneModalWindow.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneModalWindow : BaseWindow
 {
-    private static readonly Texture2D infoHighlight = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.8f));
+    private static readonly Texture2D InfoHighlight = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.8f));
 
     private readonly SceneManager sceneManager;
     private readonly Button okButton;
@@ -24,33 +24,6 @@ public class SceneModalWindow : BaseWindow
     private bool directoryMode;
     private bool deleteScene;
 
-    public override Rect WindowRect
-    {
-        set
-        {
-            value.width = Mathf.Clamp(Screen.width * 0.3f, 360f, 500f);
-            value.height = directoryMode ? 150f : Mathf.Clamp(Screen.height * 0.4f, 240f, 380f);
-
-            base.WindowRect = value;
-        }
-    }
-
-    public override bool Visible
-    {
-        get => visible;
-        set
-        {
-            visible = value;
-
-            if (!value)
-                return;
-
-            WindowRect = WindowRect;
-            windowRect.x = MiddlePosition.x;
-            windowRect.y = MiddlePosition.y;
-        }
-    }
-
     public SceneModalWindow(SceneManager sceneManager)
     {
         ReloadTranslation();
@@ -83,6 +56,33 @@ public class SceneModalWindow : BaseWindow
         };
     }
 
+    public override Rect WindowRect
+    {
+        set
+        {
+            value.width = Mathf.Clamp(Screen.width * 0.3f, 360f, 500f);
+            value.height = directoryMode ? 150f : Mathf.Clamp(Screen.height * 0.4f, 240f, 380f);
+
+            base.WindowRect = value;
+        }
+    }
+
+    public override bool Visible
+    {
+        get => visible;
+        set
+        {
+            visible = value;
+
+            if (!value)
+                return;
+
+            WindowRect = WindowRect;
+            windowRect.x = MiddlePosition.x;
+            windowRect.y = MiddlePosition.y;
+        }
+    }
+
     public override void Draw()
     {
         GUILayout.BeginArea(new(10f, 10f, WindowRect.width - 20f, WindowRect.height - 20f));
@@ -108,7 +108,7 @@ public class SceneModalWindow : BaseWindow
                 alignment = TextAnchor.MiddleCenter,
             };
 
-            labelStyle.normal.background = infoHighlight;
+            labelStyle.normal.background = InfoHighlight;
 
             var labelBox = GUILayoutUtility.GetLastRect();
 
@@ -119,8 +119,7 @@ public class SceneModalWindow : BaseWindow
             var labelSize = labelStyle.CalcSize(new GUIContent(infoString));
 
             labelBox = new(
-                labelBox.x + 10, labelBox.y + labelBox.height - (labelSize.y + 10), labelSize.x + 10, labelSize.y + 2
-            );
+                labelBox.x + 10, labelBox.y + labelBox.height - (labelSize.y + 10), labelSize.x + 10, labelSize.y + 2);
 
             GUI.Label(labelBox, infoString, labelStyle);
 
@@ -164,7 +163,6 @@ public class SceneModalWindow : BaseWindow
         GUILayout.FlexibleSpace();
 
         // Buttons
-
         var buttonStyle = new GUIStyle(GUI.skin.button)
         {
             fontSize = Utility.GetPix(12),
@@ -237,7 +235,9 @@ public class SceneModalWindow : BaseWindow
                 deleteScene = false;
             }
             else
+            {
                 sceneManager.LoadScene(sceneManager.CurrentScene);
+            }
 
             Modal.Close();
         }
@@ -246,7 +246,9 @@ public class SceneModalWindow : BaseWindow
     private void Cancel()
     {
         if (directoryMode)
+        {
             Modal.Close();
+        }
         else
         {
             if (deleteScene)

+ 18 - 19
src/MeidoPhotoStudio.Plugin/GUI/Windows/SceneWindow.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneWindow : BaseWindow
 {
-    private const float resizeHandleSize = 15f;
+    private const float ResizeHandleSize = 15f;
 
     private readonly SceneManager sceneManager;
     private readonly SceneManagerTitleBarPane titleBar;
@@ -15,18 +15,6 @@ public class SceneWindow : BaseWindow
     private bool resizing;
     private bool visible;
 
-    public override bool Visible
-    {
-        get => visible;
-        set
-        {
-            visible = value;
-
-            if (visible && !sceneManager.Initialized)
-                sceneManager.Initialize();
-        }
-    }
-
     public SceneWindow(SceneManager sceneManager)
     {
         windowRect.width = Screen.width * 0.65f;
@@ -34,7 +22,7 @@ public class SceneWindow : BaseWindow
         windowRect.x = Screen.width * 0.5f - windowRect.width * 0.5f;
         windowRect.y = Screen.height * 0.5f - windowRect.height * 0.5f;
 
-        resizeHandleRect = new(0f, 0f, resizeHandleSize, resizeHandleSize);
+        resizeHandleRect = new(0f, 0f, ResizeHandleSize, ResizeHandleSize);
 
         this.sceneManager = sceneManager;
         var sceneModalWindow = new SceneModalWindow(this.sceneManager);
@@ -48,6 +36,18 @@ public class SceneWindow : BaseWindow
         sceneGrid = AddPane(new SceneManagerScenePane(sceneManager, sceneModalWindow));
     }
 
+    public override bool Visible
+    {
+        get => visible;
+        set
+        {
+            visible = value;
+
+            if (visible && !sceneManager.Initialized)
+                sceneManager.Initialize();
+        }
+    }
+
     public override void GUIFunc(int id)
     {
         HandleResize();
@@ -88,8 +88,8 @@ public class SceneWindow : BaseWindow
 
     private void HandleResize()
     {
-        resizeHandleRect.x = windowRect.width - resizeHandleSize;
-        resizeHandleRect.y = windowRect.height - resizeHandleSize;
+        resizeHandleRect.x = windowRect.width - ResizeHandleSize;
+        resizeHandleRect.y = windowRect.height - ResizeHandleSize;
 
         if (!resizing && Input.GetMouseButton(0) && resizeHandleRect.Contains(Event.current.mousePosition))
             resizing = true;
@@ -100,9 +100,8 @@ public class SceneWindow : BaseWindow
             var rectHeight = Event.current.mousePosition.y;
 
             var minWidth = Utility.GetPix(
-                SceneManagerDirectoryPane.listWidth + (int)(SceneManager.sceneDimensions.x
-                * SceneManagerScenePane.thumbnailScale)
-            );
+                SceneManagerDirectoryPane.ListWidth + (int)(SceneManager.SceneDimensions.x
+                * SceneManagerScenePane.ThumbnailScale));
 
             windowRect.width = Mathf.Max(minWidth, rectWidth);
             windowRect.height = Mathf.Max(300, rectHeight);

+ 12 - 0
src/MeidoPhotoStudio.Plugin/KeyValuePairExtensions.cs

@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public static class KeyValuePairExtensions
+{
+    public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
+    {
+        key = kvp.Key;
+        value = kvp.Value;
+    }
+}

+ 19 - 15
src/MeidoPhotoStudio.Plugin/MPSScene.cs

@@ -1,14 +1,33 @@
 using System.IO;
 using System.Text;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class MPSScene
 {
+    private byte[] data;
+
+    public MPSScene(string path, Texture2D thumbnail = null)
+    {
+        FileInfo = new(path);
+
+        if (!thumbnail)
+        {
+            thumbnail = new(1, 1, TextureFormat.ARGB32, false);
+            thumbnail.LoadImage(File.ReadAllBytes(FileInfo.FullName));
+        }
+
+        Thumbnail = thumbnail;
+    }
+
     public Texture2D Thumbnail { get; }
+
     public FileInfo FileInfo { get; }
+
     public bool Environment { get; private set; }
+
     public int NumberOfMaids { get; private set; }
 
     public byte[] Data
@@ -23,21 +42,6 @@ public class MPSScene
         private set => data = value;
     }
 
-    private byte[] data;
-
-    public MPSScene(string path, Texture2D thumbnail = null)
-    {
-        FileInfo = new(path);
-
-        if (!thumbnail)
-        {
-            thumbnail = new(1, 1, TextureFormat.ARGB32, false);
-            thumbnail.LoadImage(File.ReadAllBytes(FileInfo.FullName));
-        }
-
-        Thumbnail = thumbnail;
-    }
-
     public void Preload()
     {
         if (data is not null)

+ 11 - 10
src/MeidoPhotoStudio.Plugin/MaidPlacementUtility.cs

@@ -1,19 +1,20 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public static class MaidPlacementUtility
 {
-    private const float pi = Mathf.PI;
-    private const float tau = Mathf.PI * 2f;
-
-    public static readonly string[] placementTypes =
+    public static readonly string[] PlacementTypes =
     {
         "horizontalRow", "verticalRow", "diagonalRow", "diagonalRowInverse", "wave", "waveInverse", "v", "vInverse",
-        "circleInner", "circleOuter", "fanInner", "fanOuter"
+        "circleInner", "circleOuter", "fanInner", "fanOuter",
     };
 
+    private const float Pi = Mathf.PI;
+    private const float Tau = Mathf.PI * 2f;
+
     public static int AlternatingSequence(int x) =>
         (int)((x % 2 == 0 ? 1 : -1) * Mathf.Ceil(x / 2f));
 
@@ -111,7 +112,7 @@ public static class MaidPlacementUtility
         {
             var maid = list[i].Maid;
             var x = AlternatingSequence(i) * 0.4f;
-            var z = (inverse ? -1 : 1) * Mathf.Cos(AlternatingSequence(i) * pi) * 0.35f;
+            var z = (inverse ? -1 : 1) * Mathf.Cos(AlternatingSequence(i) * Pi) * 0.35f;
 
             maid.SetPos(new(x, 0, z));
             maid.SetRot(Vector3.zero);
@@ -140,14 +141,14 @@ public static class MaidPlacementUtility
         for (var i = 0; i < maidCount; i++)
         {
             var maid = list[i].Maid;
-            var angle = pi / 2f + tau * AlternatingSequence(i) / maidCount;
+            var angle = Pi / 2f + Tau * AlternatingSequence(i) / maidCount;
             var x = Mathf.Cos(angle) * radius;
             var z = Mathf.Sin(angle) * radius;
 
             var rotation = Mathf.Atan2(x, z);
 
             if (inner)
-                rotation += pi;
+                rotation += Pi;
 
             maid.SetPos(new(x, 0, z));
             maid.SetRot(new(0, rotation * Mathf.Rad2Deg, 0));
@@ -165,13 +166,13 @@ public static class MaidPlacementUtility
         for (var i = 1; i < maidCount; i++)
         {
             var maid = list[i].Maid;
-            var angle = pi * AlternatingSequence(i - 1) / maidCount;
+            var angle = Pi * AlternatingSequence(i - 1) / maidCount;
             var x = Mathf.Sin(angle) * radius;
             var z = Mathf.Cos(angle) * radius;
             var rotation = Mathf.Atan2(x, z);
 
             if (outer)
-                rotation += pi;
+                rotation += Pi;
 
             maid.SetPos(new(-x, 0, -z));
             maid.SetRot(new(0, rotation * Mathf.Rad2Deg, 0));

+ 50 - 0
src/MeidoPhotoStudio.Plugin/Managers/CameraInfo.cs

@@ -0,0 +1,50 @@
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class CameraInfo
+{
+    public CameraInfo() =>
+        Reset();
+
+    public Vector3 TargetPos { get; set; }
+
+    public Quaternion Angle { get; set; }
+
+    public float Distance { get; set; }
+
+    public float FOV { get; set; }
+
+    public static CameraInfo FromCamera(CameraMain mainCamera)
+    {
+        var info = new CameraInfo();
+
+        info.UpdateInfo(mainCamera);
+
+        return info;
+    }
+
+    public void Reset()
+    {
+        TargetPos = new(0f, 0.9f, 0f);
+        Angle = Quaternion.Euler(10f, 180f, 0f);
+        Distance = 3f;
+        FOV = 35f;
+    }
+
+    public void UpdateInfo(CameraMain camera)
+    {
+        TargetPos = camera.GetTargetPos();
+        Angle = camera.transform.rotation;
+        Distance = camera.GetDistance();
+        FOV = camera.camera.fieldOfView;
+    }
+
+    public void Apply(CameraMain camera)
+    {
+        camera.SetTargetPos(TargetPos);
+        camera.SetDistance(Distance);
+        camera.transform.rotation = Angle;
+        camera.camera.fieldOfView = FOV;
+    }
+}

+ 50 - 93
src/MeidoPhotoStudio.Plugin/Managers/CameraManager.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using Input = MeidoPhotoStudio.Plugin.InputManager;
@@ -8,41 +9,25 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class CameraManager : IManager
 {
-    public const string header = "CAMERA";
+    public const string Header = "CAMERA";
 
-    private const float cameraFastMoveSpeed = 0.1f;
-    private const float cameraFastZoomSpeed = 3f;
-    private const float cameraSlowMoveSpeed = 0.004f;
-    private const float cameraSlowZoomSpeed = 0.1f;
+    private const float CameraFastMoveSpeed = 0.1f;
+    private const float CameraFastZoomSpeed = 3f;
+    private const float CameraSlowMoveSpeed = 0.004f;
+    private const float CameraSlowZoomSpeed = 0.1f;
     private const KeyCode AlphaOne = KeyCode.Alpha1;
 
-    private static readonly CameraMain mainCamera = CameraUtility.MainCamera;
-    private static readonly UltimateOrbitCamera ultimateOrbitCamera = CameraUtility.UOCamera;
+    private static readonly CameraMain MainCamera = CameraUtility.MainCamera;
+    private static readonly UltimateOrbitCamera UltimateOrbitCamera = CameraUtility.UOCamera;
 
     private readonly CameraInfo tempCameraInfo = new();
     private readonly CameraInfo[] cameraInfos;
 
-    public EventHandler CameraChange;
-
     private float defaultCameraMoveSpeed;
     private float defaultCameraZoomSpeed;
     private Camera subCamera;
     private int currentCameraIndex;
 
-    public int CameraCount =>
-        cameraInfos.Length;
-
-    public int CurrentCameraIndex
-    {
-        get => currentCameraIndex;
-        set
-        {
-            cameraInfos[currentCameraIndex].UpdateInfo(mainCamera);
-            currentCameraIndex = value;
-            LoadCameraInfo(cameraInfos[currentCameraIndex]);
-        }
-    }
-
     static CameraManager()
     {
         Input.Register(MpsKey.CameraLayer, KeyCode.Q, "Camera control layer");
@@ -61,12 +46,28 @@ public class CameraManager : IManager
         Activate();
     }
 
+    public event EventHandler CameraChange;
+
+    public int CameraCount =>
+        cameraInfos.Length;
+
+    public int CurrentCameraIndex
+    {
+        get => currentCameraIndex;
+        set
+        {
+            cameraInfos[currentCameraIndex].UpdateInfo(MainCamera);
+            currentCameraIndex = value;
+            LoadCameraInfo(cameraInfos[currentCameraIndex]);
+        }
+    }
+
     public void Activate()
     {
-        ultimateOrbitCamera.enabled = true;
+        UltimateOrbitCamera.enabled = true;
 
-        defaultCameraMoveSpeed = ultimateOrbitCamera.moveSpeed;
-        defaultCameraZoomSpeed = ultimateOrbitCamera.zoomSpeed;
+        defaultCameraMoveSpeed = UltimateOrbitCamera.moveSpeed;
+        defaultCameraZoomSpeed = UltimateOrbitCamera.zoomSpeed;
 
         if (!MeidoPhotoStudio.EditMode)
             ResetCamera();
@@ -78,35 +79,35 @@ public class CameraManager : IManager
         for (var i = 0; i < CameraCount; i++)
             cameraInfos[i].Reset();
 
-        mainCamera.ForceCalcNearClip();
+        MainCamera.ForceCalcNearClip();
 
         var subCamGo = new GameObject("subcam");
 
         subCamera = subCamGo.AddComponent<Camera>();
-        subCamera.CopyFrom(mainCamera.camera);
+        subCamera.CopyFrom(MainCamera.camera);
         subCamera.clearFlags = CameraClearFlags.Depth;
         subCamera.cullingMask = 1 << 8;
         subCamera.depth = 1f;
-        subCamera.transform.parent = mainCamera.transform;
+        subCamera.transform.parent = MainCamera.transform;
     }
 
     public void Deactivate()
     {
         UnityEngine.Object.Destroy(subCamera.gameObject);
-        mainCamera.camera.backgroundColor = Color.black;
+        MainCamera.camera.backgroundColor = Color.black;
 
-        ultimateOrbitCamera.moveSpeed = defaultCameraMoveSpeed;
-        ultimateOrbitCamera.zoomSpeed = defaultCameraZoomSpeed;
+        UltimateOrbitCamera.moveSpeed = defaultCameraMoveSpeed;
+        UltimateOrbitCamera.zoomSpeed = defaultCameraZoomSpeed;
 
         if (MeidoPhotoStudio.EditMode)
             return;
 
-        mainCamera.Reset(CameraMain.CameraType.Target, true);
-        mainCamera.SetTargetPos(new(0.5609447f, 1.380762f, -1.382336f));
-        mainCamera.SetDistance(1.6f);
-        mainCamera.SetAroundAngle(new(245.5691f, 6.273283f));
+        MainCamera.Reset(CameraMain.CameraType.Target, true);
+        MainCamera.SetTargetPos(new(0.5609447f, 1.380762f, -1.382336f));
+        MainCamera.SetDistance(1.6f);
+        MainCamera.SetAroundAngle(new(245.5691f, 6.273283f));
 
-        mainCamera.ResetCalcNearClip();
+        MainCamera.ResetCalcNearClip();
     }
 
     public void Update()
@@ -125,87 +126,43 @@ public class CameraManager : IManager
                     CurrentCameraIndex = i;
         }
 
-        subCamera.fieldOfView = mainCamera.camera.fieldOfView;
+        subCamera.fieldOfView = MainCamera.camera.fieldOfView;
 
         if (Input.Shift)
         {
-            ultimateOrbitCamera.moveSpeed = cameraFastMoveSpeed;
-            ultimateOrbitCamera.zoomSpeed = cameraFastZoomSpeed;
+            UltimateOrbitCamera.moveSpeed = CameraFastMoveSpeed;
+            UltimateOrbitCamera.zoomSpeed = CameraFastZoomSpeed;
         }
         else if (Input.Control)
         {
-            ultimateOrbitCamera.moveSpeed = cameraSlowMoveSpeed;
-            ultimateOrbitCamera.zoomSpeed = cameraSlowZoomSpeed;
+            UltimateOrbitCamera.moveSpeed = CameraSlowMoveSpeed;
+            UltimateOrbitCamera.zoomSpeed = CameraSlowZoomSpeed;
         }
         else
         {
-            ultimateOrbitCamera.moveSpeed = defaultCameraMoveSpeed;
-            ultimateOrbitCamera.zoomSpeed = defaultCameraZoomSpeed;
+            UltimateOrbitCamera.moveSpeed = defaultCameraMoveSpeed;
+            UltimateOrbitCamera.zoomSpeed = defaultCameraZoomSpeed;
         }
     }
 
     public void LoadCameraInfo(CameraInfo info)
     {
-        info.Apply(mainCamera);
+        info.Apply(MainCamera);
         CameraUtility.StopAll();
         CameraChange?.Invoke(this, EventArgs.Empty);
     }
 
     private void SaveTempCamera()
     {
-        tempCameraInfo.UpdateInfo(mainCamera);
+        tempCameraInfo.UpdateInfo(MainCamera);
         CameraUtility.StopAll();
     }
 
     private void ResetCamera()
     {
-        mainCamera.Reset(CameraMain.CameraType.Target, true);
-        mainCamera.SetTargetPos(new(0f, 0.9f, 0f));
-        mainCamera.SetDistance(3f);
+        MainCamera.Reset(CameraMain.CameraType.Target, true);
+        MainCamera.SetTargetPos(new(0f, 0.9f, 0f));
+        MainCamera.SetDistance(3f);
         CameraChange?.Invoke(this, EventArgs.Empty);
     }
 }
-
-public class CameraInfo
-{
-    public Vector3 TargetPos { get; set; }
-    public Quaternion Angle { get; set; }
-    public float Distance { get; set; }
-    public float FOV { get; set; }
-
-    public static CameraInfo FromCamera(CameraMain mainCamera)
-    {
-        var info = new CameraInfo();
-
-        info.UpdateInfo(mainCamera);
-
-        return info;
-    }
-
-    public CameraInfo() =>
-        Reset();
-
-    public void Reset()
-    {
-        TargetPos = new(0f, 0.9f, 0f);
-        Angle = Quaternion.Euler(10f, 180f, 0f);
-        Distance = 3f;
-        FOV = 35f;
-    }
-
-    public void UpdateInfo(CameraMain camera)
-    {
-        TargetPos = camera.GetTargetPos();
-        Angle = camera.transform.rotation;
-        Distance = camera.GetDistance();
-        FOV = camera.camera.fieldOfView;
-    }
-
-    public void Apply(CameraMain camera)
-    {
-        camera.SetTargetPos(TargetPos);
-        camera.SetDistance(Distance);
-        camera.transform.rotation = Angle;
-        camera.camera.fieldOfView = FOV;
-    }
-}

+ 14 - 10
src/MeidoPhotoStudio.Plugin/Managers/EffectManager.cs

@@ -5,19 +5,21 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class EffectManager : IManager
 {
-    public const string header = "EFFECT";
-    public const string footer = "END_EFFECT";
+    public const string Header = "EFFECT";
+    public const string Footer = "END_EFFECT";
 
-    private readonly Dictionary<Type, IEffectManager> EffectManagers = new();
+    private readonly Dictionary<Type, IEffectManager> effectManagers = new();
 
-    public T Get<T>() where T : IEffectManager =>
-        EffectManagers.ContainsKey(typeof(T)) ? (T)EffectManagers[typeof(T)] : default;
+    public T Get<T>()
+        where T : IEffectManager =>
+        effectManagers.ContainsKey(typeof(T)) ? (T)effectManagers[typeof(T)] : default;
 
-    public T AddManager<T>() where T : IEffectManager, new()
+    public T AddManager<T>()
+        where T : IEffectManager, new()
     {
         var manager = new T();
 
-        EffectManagers[typeof(T)] = manager;
+        effectManagers[typeof(T)] = manager;
         manager.Activate();
 
         return manager;
@@ -25,15 +27,17 @@ public class EffectManager : IManager
 
     public void Activate()
     {
-        foreach (var effectManager in EffectManagers.Values)
+        foreach (var effectManager in effectManagers.Values)
             effectManager.Activate();
     }
 
     public void Deactivate()
     {
-        foreach (var effectManager in EffectManagers.Values)
+        foreach (var effectManager in effectManagers.Values)
             effectManager.Deactivate();
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 26 - 14
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/BloomEffectManager.cs

@@ -1,17 +1,23 @@
 using System.Reflection;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class BloomEffectManager : IEffectManager
 {
-    private const float bloomDefIntensity = 5.7f;
+    public const string Header = "EFFECT_BLOOM";
+
+    private const float DefaultBloomDefIntensity = 5.7f;
+
+    private static readonly CameraMain Camera = GameMain.Instance.MainCamera;
 
-    public const string header = "EFFECT_BLOOM";
+#pragma warning disable SA1308, SA1310, SA1311
 
-    private static readonly CameraMain camera = GameMain.Instance.MainCamera;
+    // TODO: Refactor reflection to using private members directly
     private static readonly float backup_m_fBloomDefIntensity;
     private static readonly FieldInfo m_fBloomDefIntensity = Utility.GetFieldInfo<CameraMain>("m_fBloomDefIntensity");
+#pragma warning restore SA1308, SA1310, SA1311
 
     // CMSystem's bloomValue;
     private static int backupBloomValue;
@@ -24,17 +30,14 @@ public class BloomEffectManager : IEffectManager
     private Color bloomThresholdColour;
     private bool bloomHdr;
 
-    private Bloom Bloom { get; set; }
     private float bloomValue;
 
+    static BloomEffectManager() =>
+        backup_m_fBloomDefIntensity = BloomDefIntensity;
+
     public bool Ready { get; private set; }
-    public bool Active { get; private set; }
 
-    private static float BloomDefIntensity
-    {
-        set => m_fBloomDefIntensity.SetValue(camera, value);
-        get => (float)m_fBloomDefIntensity.GetValue(camera);
-    }
+    public bool Active { get; private set; }
 
     public float BloomValue
     {
@@ -97,8 +100,13 @@ public class BloomEffectManager : IEffectManager
         }
     }
 
-    static BloomEffectManager() =>
-        backup_m_fBloomDefIntensity = BloomDefIntensity;
+    private static float BloomDefIntensity
+    {
+        get => (float)m_fBloomDefIntensity.GetValue(Camera);
+        set => m_fBloomDefIntensity.SetValue(Camera, value);
+    }
+
+    private Bloom Bloom { get; set; }
 
     public void Activate()
     {
@@ -151,11 +159,15 @@ public class BloomEffectManager : IEffectManager
             Bloom.bloomThreshholdColor = BloomThresholdColour;
             Bloom.hdr = BloomHDR ? Bloom.HDRBloomMode.On : Bloom.HDRBloomMode.Auto;
 
-            BloomDefIntensity = bloomDefIntensity;
+            BloomDefIntensity = DefaultBloomDefIntensity;
         }
         else
+        {
             Reset();
+        }
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 5 - 2
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/BlurEffectManager.cs

@@ -2,7 +2,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class BlurEffectManager : IEffectManager
 {
-    public const string header = "EFFECT_BLUR";
+    public const string Header = "EFFECT_BLUR";
 
     private float initialBlurSize;
     private int initialBlurIterations;
@@ -10,6 +10,7 @@ public class BlurEffectManager : IEffectManager
     private float blurSize;
 
     public bool Ready { get; private set; }
+
     public bool Active { get; private set; }
 
     public float BlurSize
@@ -73,5 +74,7 @@ public class BlurEffectManager : IEffectManager
         Blur.downsample = initialDownsample;
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 7 - 2
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/DepthOfFieldManager.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DepthOfFieldEffectManager : IEffectManager
 {
-    public const string header = "EFFECT_DOF";
+    public const string Header = "EFFECT_DOF";
 
     private readonly float initialValue = 0f;
 
@@ -15,6 +15,7 @@ public class DepthOfFieldEffectManager : IEffectManager
     private bool visualizeFocus;
 
     public bool Ready { get; private set; }
+
     public bool Active { get; private set; }
 
     public float FocalLength
@@ -100,8 +101,12 @@ public class DepthOfFieldEffectManager : IEffectManager
             DepthOfField.maxBlurSize = MaxBlurSize;
         }
         else
+        {
             Reset();
+        }
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 7 - 2
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/FogEffectManager.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class FogEffectManager : IEffectManager
 {
-    public const string header = "EFFECT_FOG";
+    public const string Header = "EFFECT_FOG";
 
     private readonly float initialDistance = 4f;
     private readonly float initialDensity = 1f;
@@ -19,6 +19,7 @@ public class FogEffectManager : IEffectManager
     private Color fogColour;
 
     public bool Ready { get; private set; }
+
     public bool Active { get; private set; }
 
     public float Distance
@@ -139,8 +140,12 @@ public class FogEffectManager : IEffectManager
             Fog.globalFogColor = FogColour;
         }
         else
+        {
             Reset();
+        }
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 2 - 0
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/IEffectManager.cs

@@ -3,8 +3,10 @@ namespace MeidoPhotoStudio.Plugin;
 public interface IEffectManager : IManager
 {
     bool Ready { get; }
+
     bool Active { get; }
 
     void SetEffectActive(bool active);
+
     void Reset();
 }

+ 9 - 4
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/SepiaToneEffectManager.cs

@@ -2,11 +2,12 @@ using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
-public class SepiaToneEffectManger : IEffectManager
+public class SepiaToneEffectManager : IEffectManager
 {
-    public const string header = "EFFECT_SEPIA";
+    public const string Header = "EFFECT_SEPIA";
 
     public bool Ready { get; private set; }
+
     public bool Active { get; private set; }
 
     private SepiaToneEffect SepiaTone { get; set; }
@@ -31,7 +32,11 @@ public class SepiaToneEffectManger : IEffectManager
     public void SetEffectActive(bool active) =>
         SepiaTone.enabled = Active = active;
 
-    public void Reset() { }
+    public void Reset()
+    {
+    }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 7 - 2
src/MeidoPhotoStudio.Plugin/Managers/EffectManagers/VignetteEffectManager.cs

@@ -2,7 +2,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class VignetteEffectManager : IEffectManager
 {
-    public const string header = "EFFECT_VIGNETTE";
+    public const string Header = "EFFECT_VIGNETTE";
 
     private float initialIntensity;
     private float initialBlur;
@@ -14,6 +14,7 @@ public class VignetteEffectManager : IEffectManager
     private float intensity;
 
     public bool Ready { get; private set; }
+
     public bool Active { get; private set; }
 
     public float Intensity
@@ -89,8 +90,12 @@ public class VignetteEffectManager : IEffectManager
             Vignette.chromaticAberration = ChromaticAberration;
         }
         else
+        {
             Reset();
+        }
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 }

+ 36 - 29
src/MeidoPhotoStudio.Plugin/Managers/EnvironmentManager.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using Object = UnityEngine.Object;
@@ -7,16 +8,29 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class EnvironmentManager : IManager
 {
-    public const string header = "ENVIRONMENT";
-    public const string defaultBg = "Theater";
+    public const string Header = "ENVIRONMENT";
+    public const string DefaultBg = "Theater";
 
-    private const string myRoomPrefix = "マイルーム:";
+    private const string MyRoomPrefix = "マイルーム:";
 
-    private static readonly BgMgr bgMgr = GameMain.Instance.BgMgr;
+    private static readonly BgMgr BgMgr = GameMain.Instance.BgMgr;
 
     private static bool cubeActive;
     private static bool cubeSmall;
+
+    private Transform bg;
+    private GameObject bgObject;
+    private DragPointBG bgDragPoint;
+    private bool bgVisible = true;
+
+    public EnvironmentManager()
+    {
+        DragPointLight.EnvironmentManager = this;
+        Activate();
+    }
+
     private static event EventHandler CubeActiveChange;
+
     private static event EventHandler CubeSmallChange;
 
     public static bool CubeActive
@@ -45,12 +59,7 @@ public class EnvironmentManager : IManager
         }
     }
 
-    private Transform bg;
-    private GameObject bgObject;
-    private DragPointBG bgDragPoint;
-    private bool bgVisible = true;
-
-    public string CurrentBgAsset { get; private set; } = defaultBg;
+    public string CurrentBgAsset { get; private set; } = DefaultBg;
 
     public bool BGVisible
     {
@@ -62,27 +71,23 @@ public class EnvironmentManager : IManager
         }
     }
 
-    public EnvironmentManager()
+    public void Update()
     {
-        DragPointLight.EnvironmentManager = this;
-        Activate();
     }
 
-    public void Update() { }
-
     public void Activate()
     {
         BgMgrPatcher.ChangeBgBegin += OnChangeBegin;
         BgMgrPatcher.ChangeBgEnd += OnChangeEnd;
 
-        bgObject = bgMgr.Parent;
+        bgObject = BgMgr.Parent;
 
         bgObject.SetActive(true);
 
         if (MeidoPhotoStudio.EditMode)
             UpdateBG();
         else
-            ChangeBackground(defaultBg);
+            ChangeBackground(DefaultBg);
 
         CubeSmallChange += OnCubeSmall;
         CubeActiveChange += OnCubeActive;
@@ -97,16 +102,18 @@ public class EnvironmentManager : IManager
         BGVisible = true;
 
         if (MeidoPhotoStudio.EditMode)
-            bgMgr.ChangeBg(defaultBg);
+        {
+            BgMgr.ChangeBg(DefaultBg);
+        }
         else
         {
             var isNight = GameMain.Instance.CharacterMgr.status.GetFlag("時間帯") is 3;
 
-            bgMgr.ChangeBg(isNight ? "ShinShitsumu_ChairRot_Night" : "ShinShitsumu_ChairRot");
+            BgMgr.ChangeBg(isNight ? "ShinShitsumu_ChairRot_Night" : "ShinShitsumu_ChairRot");
         }
 
-        if (bgMgr.BgObject)
-            bgMgr.BgObject.transform.localScale = Vector3.one;
+        if (BgMgr.BgObject)
+            BgMgr.BgObject.transform.localScale = Vector3.one;
 
         CubeSmallChange -= OnCubeSmall;
         CubeActiveChange -= OnCubeActive;
@@ -115,9 +122,9 @@ public class EnvironmentManager : IManager
     public void ChangeBackground(string assetName, bool creative = false)
     {
         if (creative)
-            bgMgr.ChangeBgMyRoom(assetName);
+            BgMgr.ChangeBgMyRoom(assetName);
         else
-            bgMgr.ChangeBg(assetName);
+            BgMgr.ChangeBg(assetName);
     }
 
     private void AttachDragPoint(Transform bgTransform)
@@ -138,15 +145,15 @@ public class EnvironmentManager : IManager
 
     private void UpdateBG()
     {
-        if (!bgMgr.BgObject)
+        if (!BgMgr.BgObject)
             return;
 
-        CurrentBgAsset = bgMgr.GetBGName();
+        CurrentBgAsset = BgMgr.GetBGName();
 
-        if (CurrentBgAsset.StartsWith(myRoomPrefix))
-            CurrentBgAsset = CurrentBgAsset.Replace(myRoomPrefix, string.Empty);
+        if (CurrentBgAsset.StartsWith(MyRoomPrefix))
+            CurrentBgAsset = CurrentBgAsset.Replace(MyRoomPrefix, string.Empty);
 
-        bg = bgMgr.BgObject.transform;
+        bg = BgMgr.BgObject.transform;
 
         AttachDragPoint(bg);
     }
@@ -158,7 +165,7 @@ public class EnvironmentManager : IManager
     }
 
     private void OnCubeSmall(object sender, EventArgs args) =>
-        bgDragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
+        bgDragPoint.DragPointScale = CubeSmall ? DragPointGeneral.SmallCube : 1f;
 
     private void OnCubeActive(object sender, EventArgs args) =>
         bgDragPoint.gameObject.SetActive(CubeActive);

+ 2 - 0
src/MeidoPhotoStudio.Plugin/Managers/IManager.cs

@@ -3,6 +3,8 @@ namespace MeidoPhotoStudio.Plugin;
 public interface IManager
 {
     void Update();
+
     void Activate();
+
     void Deactivate();
 }

+ 45 - 24
src/MeidoPhotoStudio.Plugin/Managers/InputManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using BepInEx.Configuration;
 using UnityEngine;
 
@@ -9,30 +10,53 @@ namespace MeidoPhotoStudio.Plugin;
 public enum MpsKey
 {
     // MeidoPhotoStudio
-    Activate, Screenshot, ToggleUI, ToggleMessage,
+    Activate,
+    Screenshot,
+    ToggleUI,
+    ToggleMessage,
+
     // MeidoManager
     MeidoUndressing,
+
     // Camera
-    CameraLayer, CameraReset, CameraSave, CameraLoad,
+    CameraLayer,
+    CameraReset,
+    CameraSave,
+    CameraLoad,
+
     // Dragpoint
-    DragSelect, DragDelete, DragMove, DragRotate, DragScale, DragFinger,
+    DragSelect,
+    DragDelete,
+    DragMove,
+    DragRotate,
+    DragScale,
+    DragFinger,
+
     // Scene management
-    SaveScene, LoadScene, OpenSceneManager,
+    SaveScene,
+    LoadScene,
+    OpenSceneManager,
 }
 
 public static class InputManager
 {
-    public const KeyCode upperKeyCode = KeyCode.F15;
-    public const string configHeader = "Controls";
+    public const KeyCode UpperKeyCode = KeyCode.F15;
+    public const string ConfigHeader = "Controls";
 
-    public static readonly AcceptableValueBase controlRange;
+    public static readonly AcceptableValueBase ControlRange;
 
     private static readonly Dictionary<MpsKey, KeyCode> ActionKeys = new();
     private static readonly Dictionary<MpsKey, ConfigEntry<KeyCode>> ConfigEntries = new();
 
+    private static InputListener inputListener;
+
+    static InputManager() =>
+        ControlRange = new AcceptableValueRange<KeyCode>(default, UpperKeyCode);
+
     public static event EventHandler KeyChange;
 
     public static KeyCode CurrentKeyCode { get; private set; }
+
     public static bool Listening { get; private set; }
 
     public static bool Control =>
@@ -44,23 +68,20 @@ public static class InputManager
     public static bool Shift =>
         Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
 
-    private static InputListener inputListener;
-
-    static InputManager() =>
-        controlRange = new AcceptableValueRange<KeyCode>(default, upperKeyCode);
-
     public static void Register(MpsKey action, KeyCode key, string description)
     {
-        key = Clamp(key, default, upperKeyCode);
+        key = Clamp(key, default, UpperKeyCode);
 
         if (ConfigEntries.ContainsKey(action))
+        {
             Rebind(action, key);
+        }
         else
         {
-            var configDescription = new ConfigDescription(description, controlRange);
+            var configDescription = new ConfigDescription(description, ControlRange);
 
             ConfigEntries[action] =
-                Configuration.Config.Bind(configHeader, action.ToString(), key, configDescription);
+                Configuration.Config.Bind(ConfigHeader, action.ToString(), key, configDescription);
 
             key = ConfigEntries[action].Value;
             ActionKeys[action] = key;
@@ -69,7 +90,7 @@ public static class InputManager
 
     public static void Rebind(MpsKey action, KeyCode key)
     {
-        key = Clamp(key, default, upperKeyCode);
+        key = Clamp(key, default, UpperKeyCode);
 
         if (ConfigEntries.ContainsKey(action))
             ConfigEntries[action].Value = key;
@@ -133,16 +154,16 @@ public static class InputManager
     /* Listener taken from https://forum.unity.com/threads/find-out-which-key-was-pressed.385250/ */
     private class InputListener : MonoBehaviour
     {
-        private static readonly KeyCode[] keyCodes;
-
-        public event EventHandler<KeyChangeEventArgs> KeyChange;
+        private static readonly KeyCode[] KeyCodes;
 
         static InputListener() =>
-            keyCodes = Enum.GetValues(typeof(KeyCode))
+            KeyCodes = Enum.GetValues(typeof(KeyCode))
             .Cast<KeyCode>()
-            .Where(keyCode => keyCode <= upperKeyCode)
+            .Where(keyCode => keyCode <= UpperKeyCode)
             .ToArray();
 
+        public event EventHandler<KeyChangeEventArgs> KeyChange;
+
         private void Awake() =>
             DontDestroyOnLoad(this);
 
@@ -151,7 +172,7 @@ public static class InputManager
             if (!Input.anyKeyDown)
                 return;
 
-            foreach (var key in keyCodes)
+            foreach (var key in KeyCodes)
             {
                 if (!Input.GetKeyDown(key))
                     continue;
@@ -165,9 +186,9 @@ public static class InputManager
 
     private class KeyChangeEventArgs : EventArgs
     {
-        public KeyCode Key { get; }
-
         public KeyChangeEventArgs(KeyCode key) =>
             Key = key;
+
+        public KeyCode Key { get; }
     }
 }

+ 21 - 14
src/MeidoPhotoStudio.Plugin/Managers/LightManager.cs

@@ -1,15 +1,32 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class LightManager : IManager
 {
-    public const string header = "LIGHT";
+    public const string Header = "LIGHT";
 
     private static bool cubeActive = true;
+
+    private readonly List<DragPointLight> lightList = new();
+
+    private int selectedLightIndex;
+
+    public LightManager() =>
+        Activate();
+
+    public event EventHandler Rotate;
+
+    public event EventHandler Scale;
+
+    public event EventHandler ListModified;
+
+    public event EventHandler Select;
+
     private static event EventHandler CubeActiveChange;
 
     public static bool CubeActive
@@ -25,15 +42,6 @@ public class LightManager : IManager
         }
     }
 
-    private readonly List<DragPointLight> lightList = new();
-
-    public event EventHandler Rotate;
-    public event EventHandler Scale;
-    public event EventHandler ListModified;
-    public event EventHandler Select;
-
-    private int selectedLightIndex;
-
     public int SelectedLightIndex
     {
         get => selectedLightIndex;
@@ -53,9 +61,6 @@ public class LightManager : IManager
     public DragPointLight CurrentLight =>
         lightList[SelectedLightIndex];
 
-    public LightManager() =>
-        Activate();
-
     public void Activate()
     {
         GameMain.Instance.MainCamera.GetComponent<Camera>().backgroundColor = Color.black;
@@ -80,7 +85,9 @@ public class LightManager : IManager
         CubeActiveChange -= OnCubeActive;
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 
     public void AddLight(GameObject lightGo = null, bool isMain = false)
     {

+ 139 - 153
src/MeidoPhotoStudio.Plugin/Managers/MeidoManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using HarmonyLib;
 using UnityEngine;
 
@@ -8,26 +9,35 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class MeidoManager : IManager
 {
-    public const string header = "MEIDO";
+    public const string Header = "MEIDO";
 
-    private static readonly CharacterMgr characterMgr = GameMain.Instance.CharacterMgr;
+    private static readonly CharacterMgr CharacterMgr = GameMain.Instance.CharacterMgr;
 
     private static bool active;
 
-    private static int EditMaidIndex { get; set; }
-
-    public event EventHandler<MeidoUpdateEventArgs> UpdateMeido;
-    public event EventHandler EndCallMeidos;
-    public event EventHandler BeginCallMeidos;
-
     private int selectedMeido;
     private bool globalGravity;
     private int undress;
     private int tempEditMaidIndex = -1;
 
+    static MeidoManager() =>
+        InputManager.Register(MpsKey.MeidoUndressing, KeyCode.H, "All maid undressing");
+
+    public MeidoManager() =>
+        Activate();
+
+    public event EventHandler<MeidoUpdateEventArgs> UpdateMeido;
+
+    public event EventHandler EndCallMeidos;
+
+    public event EventHandler BeginCallMeidos;
+
     public Meido[] Meidos { get; private set; }
+
     public HashSet<int> SelectedMeidoSet { get; } = new();
+
     public List<int> SelectMeidoList { get; } = new();
+
     public List<Meido> ActiveMeidoList { get; } = new();
 
     public int SelectedMeido
@@ -72,136 +82,19 @@ public class MeidoManager : IManager
         }
     }
 
-    static MeidoManager() =>
-        InputManager.Register(MpsKey.MeidoUndressing, KeyCode.H, "All maid undressing");
-
-    private static void SetEditorMaid(Maid maid)
-    {
-        if (!maid)
-        {
-            Utility.LogWarning("Refusing to change editing maid because the new maid is null!");
-
-            return;
-        }
-
-        if (SceneEdit.Instance.maid.status.guid == maid.status.guid)
-        {
-            Utility.LogDebug("Editing maid is the same as new maid");
-
-            return;
-        }
-
-        var uiRoot = GameObject.Find("UI Root");
-
-        if (!TryGetUIControl<PresetCtrl>(uiRoot, "PresetPanel", out var presetCtrl))
-            return;
-
-        if (!TryGetUIControl<PresetButtonCtrl>(uiRoot, "PresetButtonPanel", out var presetButtonCtrl))
-            return;
-
-        if (!TryGetUIControl<ProfileCtrl>(uiRoot, "ProfilePanel", out var profileCtrl))
-            return;
-
-        if (!TryGetUIControl<SceneEditWindow.CustomPartsWindow>(
-            uiRoot, "Window/CustomPartsWindow", out var sceneEditWindow
-        ))
-            return;
-
-        // Preset application
-        presetCtrl.m_maid = maid;
-
-        // Preset saving
-        presetButtonCtrl.m_maid = maid;
-
-        // Maid profile (name, description, experience etc)
-        profileCtrl.m_maidStatus = maid.status;
-
-        // Accessory/Parts placement
-        sceneEditWindow.maid = maid;
-
-        // Stopping maid animation and head movement when customizing parts placement
-        sceneEditWindow.animation = maid.GetAnimation();
-
-        // Clothing/body in general and maybe other things
-        SceneEdit.Instance.m_maid = maid;
-
-        // Body status, parts colours and maybe more
-        GameMain.Instance.CharacterMgr.m_gcActiveMaid[0] = maid;
-
-        static bool TryGetUIControl<T>(GameObject root, string hierarchy, out T uiControl) where T : MonoBehaviour
-        {
-            uiControl = null;
-
-            var uiElement = UTY.GetChildObjectNoError(root, hierarchy);
-
-            if (!uiElement)
-                return false;
-
-            uiControl = uiElement.GetComponent<T>();
-
-            return uiControl;
-        }
-    }
-
-    [HarmonyPostfix]
-    [HarmonyPatch(typeof(SceneEdit), nameof(SceneEdit.Start))]
-    private static void SceneEditStartPostfix()
-    {
-        EditMaidIndex = -1;
-
-        if (!SceneEdit.Instance.maid)
-            return;
-
-        var originalEditingMaid = SceneEdit.Instance.maid;
-
-        EditMaidIndex = GameMain.Instance.CharacterMgr.GetStockMaidList()
-            .FindIndex(maid => maid.status.guid == originalEditingMaid.status.guid);
-
-        try
-        {
-            var editOkCancelButton = UTY.GetChildObject(GameObject.Find("UI Root"), "OkCancel")
-                .GetComponent<EditOkCancel>();
-
-            EditOkCancel.OnClick newEditOkCancelDelegate = RestoreOriginalEditingMaid;
-
-            newEditOkCancelDelegate += editOkCancelButton.m_dgOnClickOk;
-
-            editOkCancelButton.m_dgOnClickOk = newEditOkCancelDelegate;
-
-            void RestoreOriginalEditingMaid()
-            {
-                // Only restore original editing maid when active.
-                if (!active)
-                    return;
-
-                Utility.LogDebug($"Setting Editing maid back to '{originalEditingMaid.status.fullNameJpStyle}'");
-
-                SetEditorMaid(originalEditingMaid);
-
-                // Set SceneEdit's maid regardless of UI integration failing
-                SceneEdit.Instance.m_maid = originalEditingMaid;
-            }
-        }
-        catch (Exception e)
-        {
-            Utility.LogWarning($"Failed to hook onto Edit Mode OK button: {e}");
-        }
-    }
-
-    public MeidoManager() =>
-        Activate();
+    private static int EditMaidIndex { get; set; }
 
     public void ChangeMaid(int index) =>
         OnUpdateMeido(null, new(index));
 
     public void Activate()
     {
-        characterMgr.ResetCharaPosAll();
+        CharacterMgr.ResetCharaPosAll();
 
         if (!MeidoPhotoStudio.EditMode)
-            characterMgr.DeactivateMaid(0);
+            CharacterMgr.DeactivateMaid(0);
 
-        Meidos = characterMgr.GetStockMaidList()
+        Meidos = CharacterMgr.GetStockMaidList()
             .Select((_, stockNo) => new Meido(stockNo))
             .ToArray();
 
@@ -260,13 +153,13 @@ public class MeidoManager : IManager
             return;
         }
 
-        void callMeidos() =>
+        void LoadMeido() =>
             GameMain.Instance.StartCoroutine(LoadMeidos());
 
         if (MeidoPhotoStudio.EditMode && !moreThanEditMaid && SelectMeidoList.Count is 1)
-            callMeidos();
+            LoadMeido();
         else
-            GameMain.Instance.MainCamera.FadeOut(0.01f, f_bSkipable: false, f_dg: callMeidos);
+            GameMain.Instance.MainCamera.FadeOut(0.01f, f_bSkipable: false, f_dg: LoadMeido);
     }
 
     public void SelectMeido(int index)
@@ -321,13 +214,125 @@ public class MeidoManager : IManager
     public void PlaceMeidos(string placementType) =>
         MaidPlacementUtility.ApplyPlacement(placementType, ActiveMeidoList);
 
+    private static void SetEditorMaid(Maid maid)
+    {
+        if (!maid)
+        {
+            Utility.LogWarning("Refusing to change editing maid because the new maid is null!");
+
+            return;
+        }
+
+        if (SceneEdit.Instance.maid.status.guid == maid.status.guid)
+        {
+            Utility.LogDebug("Editing maid is the same as new maid");
+
+            return;
+        }
+
+        var uiRoot = GameObject.Find("UI Root");
+
+        if (!TryGetUIControl<PresetCtrl>(uiRoot, "PresetPanel", out var presetCtrl))
+            return;
+
+        if (!TryGetUIControl<PresetButtonCtrl>(uiRoot, "PresetButtonPanel", out var presetButtonCtrl))
+            return;
+
+        if (!TryGetUIControl<ProfileCtrl>(uiRoot, "ProfilePanel", out var profileCtrl))
+            return;
+
+        if (!TryGetUIControl<SceneEditWindow.CustomPartsWindow>(
+            uiRoot, "Window/CustomPartsWindow", out var sceneEditWindow))
+            return;
+
+        // Preset application
+        presetCtrl.m_maid = maid;
+
+        // Preset saving
+        presetButtonCtrl.m_maid = maid;
+
+        // Maid profile (name, description, experience etc)
+        profileCtrl.m_maidStatus = maid.status;
+
+        // Accessory/Parts placement
+        sceneEditWindow.maid = maid;
+
+        // Stopping maid animation and head movement when customizing parts placement
+        sceneEditWindow.animation = maid.GetAnimation();
+
+        // Clothing/body in general and maybe other things
+        SceneEdit.Instance.m_maid = maid;
+
+        // Body status, parts colours and maybe more
+        GameMain.Instance.CharacterMgr.m_gcActiveMaid[0] = maid;
+
+        static bool TryGetUIControl<T>(GameObject root, string hierarchy, out T uiControl)
+            where T : MonoBehaviour
+        {
+            uiControl = null;
+
+            var uiElement = UTY.GetChildObjectNoError(root, hierarchy);
+
+            if (!uiElement)
+                return false;
+
+            uiControl = uiElement.GetComponent<T>();
+
+            return uiControl;
+        }
+    }
+
+    [HarmonyPostfix]
+    [HarmonyPatch(typeof(SceneEdit), nameof(SceneEdit.Start))]
+    private static void SceneEditStartPostfix()
+    {
+        EditMaidIndex = -1;
+
+        if (!SceneEdit.Instance.maid)
+            return;
+
+        var originalEditingMaid = SceneEdit.Instance.maid;
+
+        EditMaidIndex = GameMain.Instance.CharacterMgr.GetStockMaidList()
+            .FindIndex(maid => maid.status.guid == originalEditingMaid.status.guid);
+
+        try
+        {
+            var editOkCancelButton = UTY.GetChildObject(GameObject.Find("UI Root"), "OkCancel")
+                .GetComponent<EditOkCancel>();
+
+            EditOkCancel.OnClick newEditOkCancelDelegate = RestoreOriginalEditingMaid;
+
+            newEditOkCancelDelegate += editOkCancelButton.m_dgOnClickOk;
+
+            editOkCancelButton.m_dgOnClickOk = newEditOkCancelDelegate;
+
+            void RestoreOriginalEditingMaid()
+            {
+                // Only restore original editing maid when active.
+                if (!active)
+                    return;
+
+                Utility.LogDebug($"Setting Editing maid back to '{originalEditingMaid.status.fullNameJpStyle}'");
+
+                SetEditorMaid(originalEditingMaid);
+
+                // Set SceneEdit's maid regardless of UI integration failing
+                SceneEdit.Instance.m_maid = originalEditingMaid;
+            }
+        }
+        catch (Exception e)
+        {
+            Utility.LogWarning($"Failed to hook onto Edit Mode OK button: {e}");
+        }
+    }
+
     private void UnloadMeidos()
     {
         SelectedMeido = 0;
 
         var commonMeidoIDs = new HashSet<int>(
-            ActiveMeidoList.Where(meido => SelectedMeidoSet.Contains(meido.StockNo)).Select(meido => meido.StockNo)
-        );
+            ActiveMeidoList.Where(meido => SelectedMeidoSet.Contains(meido.StockNo)).Select(meido => meido.StockNo));
 
         foreach (var meido in ActiveMeidoList)
         {
@@ -402,22 +407,3 @@ public class MeidoManager : IManager
             meido.ApplyGravity(args.LocalPosition, args.IsSkirt);
     }
 }
-
-public class MeidoUpdateEventArgs : EventArgs
-{
-    public static new MeidoUpdateEventArgs Empty { get; } = new(-1);
-
-    public int SelectedMeido { get; }
-    public bool IsBody { get; }
-    public bool FromMeido { get; }
-
-    public bool IsEmpty =>
-        this == Empty || SelectedMeido is -1 && !FromMeido && IsBody;
-
-    public MeidoUpdateEventArgs(int meidoIndex = -1, bool fromMaid = false, bool isBody = true)
-    {
-        SelectedMeido = meidoIndex;
-        IsBody = isBody;
-        FromMeido = fromMaid;
-    }
-}

+ 24 - 0
src/MeidoPhotoStudio.Plugin/Managers/MeidoUpdateEventArgs.cs

@@ -0,0 +1,24 @@
+using System;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class MeidoUpdateEventArgs : EventArgs
+{
+    public MeidoUpdateEventArgs(int meidoIndex = -1, bool fromMaid = false, bool isBody = true)
+    {
+        SelectedMeido = meidoIndex;
+        IsBody = isBody;
+        FromMeido = fromMaid;
+    }
+
+    public static new MeidoUpdateEventArgs Empty { get; } = new(-1);
+
+    public int SelectedMeido { get; }
+
+    public bool IsBody { get; }
+
+    public bool FromMeido { get; }
+
+    public bool IsEmpty =>
+        this == Empty || SelectedMeido is -1 && !FromMeido && IsBody;
+}

+ 23 - 21
src/MeidoPhotoStudio.Plugin/Managers/MessageWindowManager.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class MessageWindowManager : IManager
 {
-    public const string header = "TEXTBOX";
+    public const string Header = "TEXTBOX";
 
     public static readonly SliderProp FontBounds = new(25f, 60f);
 
@@ -16,6 +16,27 @@ public class MessageWindowManager : IManager
     private readonly UILabel messageLabel;
     private readonly UILabel speakerLabel;
 
+    static MessageWindowManager() =>
+        InputManager.Register(MpsKey.ToggleMessage, KeyCode.M, "Show/hide message box");
+
+    public MessageWindowManager()
+    {
+        messageWindowMgr = GameMain.Instance.MsgWnd;
+
+        var messageWindowPanel =
+            Utility.GetFieldValue<MessageWindowMgr, GameObject>(messageWindowMgr, "m_goMessageWindowPanel");
+
+        var msgParent = UTY.GetChildObject(messageWindowPanel, "MessageViewer/MsgParent");
+
+        messageButtons = UTY.GetChildObject(msgParent, "Buttons");
+        hitRetSprite = UTY.GetChildObject(msgParent, "Hitret");
+        subtitlesDisplayPanel = UTY.GetChildObject(msgParent, "SubtitlesDisplayPanel");
+
+        messageBox = UTY.GetChildObject(msgParent, "MessageBox");
+        speakerLabel = UTY.GetChildObject(msgParent, "SpeakerName/Name").GetComponent<UILabel>();
+        messageLabel = UTY.GetChildObject(msgParent, "Message").GetComponent<UILabel>();
+    }
+
     public bool ShowingMessage
     {
         get => messageWindowMgr.IsVisibleMessageViewer;
@@ -46,29 +67,10 @@ public class MessageWindowManager : IManager
         set => messageLabel.fontSize = (int)Mathf.Clamp(value, FontBounds.Left, FontBounds.Right);
     }
 
-    static MessageWindowManager() =>
-        InputManager.Register(MpsKey.ToggleMessage, KeyCode.M, "Show/hide message box");
-
-    public MessageWindowManager()
+    public void Update()
     {
-        messageWindowMgr = GameMain.Instance.MsgWnd;
-
-        var messageWindowPanel =
-            Utility.GetFieldValue<MessageWindowMgr, GameObject>(messageWindowMgr, "m_goMessageWindowPanel");
-
-        var msgParent = UTY.GetChildObject(messageWindowPanel, "MessageViewer/MsgParent");
-
-        messageButtons = UTY.GetChildObject(msgParent, "Buttons");
-        hitRetSprite = UTY.GetChildObject(msgParent, "Hitret");
-        subtitlesDisplayPanel = UTY.GetChildObject(msgParent, "SubtitlesDisplayPanel");
-
-        messageBox = UTY.GetChildObject(msgParent, "MessageBox");
-        speakerLabel = UTY.GetChildObject(msgParent, "SpeakerName/Name").GetComponent<UILabel>();
-        messageLabel = UTY.GetChildObject(msgParent, "Message").GetComponent<UILabel>();
     }
 
-    public void Update() { }
-
     public void Activate()
     {
         if (Product.supportMultiLanguage)

+ 56 - 48
src/MeidoPhotoStudio.Plugin/Managers/PropManager.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using BepInEx.Configuration;
 using UnityEngine;
 
@@ -13,14 +14,41 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class PropManager : IManager
 {
-    public const string header = "PROP";
+    public const string Header = "PROP";
 
-    private static readonly ConfigEntry<bool> modItemsOnly;
+    private static readonly ConfigEntry<bool> ModItemsOnlyValue;
 
     private static bool cubeActive = true;
     private static Dictionary<string, string> modFileToFullPath;
     private static bool cubeSmall;
+
+    private readonly List<DragPointProp> propList = new();
+    private readonly MeidoManager meidoManager;
+
+    private int currentPropIndex;
+
+    static PropManager() =>
+        ModItemsOnlyValue = Configuration.Config.Bind(
+            "Prop", "ModItemsOnly", false, "Disable waiting for and loading base game clothing");
+
+    public PropManager(MeidoManager meidoManager)
+    {
+        this.meidoManager = meidoManager;
+
+        meidoManager.BeginCallMeidos += OnBeginCallMeidos;
+        meidoManager.EndCallMeidos += OnEndCallMeidos;
+
+        Activate();
+    }
+
+    public event EventHandler PropSelectionChange;
+
+    public event EventHandler FromPropSelect;
+
+    public event EventHandler PropListChange;
+
     private static event EventHandler CubeActiveChange;
+
     private static event EventHandler CubeSmallChange;
 
     public static bool CubeActive
@@ -49,44 +77,12 @@ public class PropManager : IManager
         }
     }
 
-    private static Dictionary<string, string> ModFileToFullPath
-    {
-        get
-        {
-            if (modFileToFullPath is not null)
-                return modFileToFullPath;
-
-            var modFiles = Menu.GetModFiles();
-
-            modFileToFullPath = new(modFiles.Length, StringComparer.OrdinalIgnoreCase);
-
-            foreach (var mod in modFiles)
-            {
-                var key = Path.GetFileName(mod);
-
-                if (!modFileToFullPath.ContainsKey(key))
-                    modFileToFullPath[key] = mod;
-            }
-
-            return modFileToFullPath;
-        }
-    }
-
     public static bool ModItemsOnly =>
-        modItemsOnly.Value;
+        ModItemsOnlyValue.Value;
 
     public DragPointProp CurrentProp =>
         PropCount is 0 ? null : propList[CurrentPropIndex];
 
-    private readonly List<DragPointProp> propList = new();
-    private readonly MeidoManager meidoManager;
-
-    public event EventHandler PropSelectionChange;
-    public event EventHandler FromPropSelect;
-    public event EventHandler PropListChange;
-
-    private int currentPropIndex;
-
     public string[] PropNameList =>
         propList.Count is 0
             ? new[] { Translation.Get("systemMessage", "noProps") }
@@ -118,19 +114,27 @@ public class PropManager : IManager
         }
     }
 
-    static PropManager() =>
-        modItemsOnly = Configuration.Config.Bind(
-            "Prop", "ModItemsOnly", false, "Disable waiting for and loading base game clothing"
-        );
-
-    public PropManager(MeidoManager meidoManager)
+    private static Dictionary<string, string> ModFileToFullPath
     {
-        this.meidoManager = meidoManager;
+        get
+        {
+            if (modFileToFullPath is not null)
+                return modFileToFullPath;
 
-        meidoManager.BeginCallMeidos += OnBeginCallMeidos;
-        meidoManager.EndCallMeidos += OnEndCallMeidos;
+            var modFiles = Menu.GetModFiles();
 
-        Activate();
+            modFileToFullPath = new(modFiles.Length, StringComparer.OrdinalIgnoreCase);
+
+            foreach (var mod in modFiles)
+            {
+                var key = Path.GetFileName(mod);
+
+                if (!modFileToFullPath.ContainsKey(key))
+                    modFileToFullPath[key] = mod;
+            }
+
+            return modFileToFullPath;
+        }
     }
 
     public bool AddFromPropInfo(PropInfo propInfo)
@@ -146,7 +150,9 @@ public class PropManager : IManager
                     modItem.BaseMenuFile = propInfo.SubFilename;
                 }
                 else
+                {
                     modItem = ModItem.Mod(propInfo.Filename);
+                }
 
                 return AddModProp(modItem);
             case PropInfo.PropType.MyRoom:
@@ -278,7 +284,9 @@ public class PropManager : IManager
         prop.AttachTo(meido, point);
     }
 
-    public void Update() { }
+    public void Update()
+    {
+    }
 
     public void Activate()
     {
@@ -301,7 +309,7 @@ public class PropManager : IManager
         dragPoint.Set(model.transform);
         dragPoint.AddGizmo(0.45f, CustomGizmo.GizmoMode.World);
         dragPoint.ConstantScale = true;
-        dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
+        dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.SmallCube : 1f;
         dragPoint.Delete += OnDeleteProp;
         dragPoint.Select += OnSelectProp;
 
@@ -356,7 +364,7 @@ public class PropManager : IManager
     private void OnCubeSmall(object sender, EventArgs args)
     {
         foreach (var dragPoint in propList)
-            dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
+            dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.SmallCube : 1f;
     }
 
     private void OnCubeActive(object sender, EventArgs args)

+ 48 - 37
src/MeidoPhotoStudio.Plugin/Managers/SceneManager.cs

@@ -2,8 +2,10 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using BepInEx.Configuration;
 using UnityEngine;
+
 using Input = MeidoPhotoStudio.Plugin.InputManager;
 using Object = UnityEngine.Object;
 
@@ -11,32 +13,55 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class SceneManager : IManager
 {
-    public enum SortMode { Name, DateCreated, DateModified }
+    public static readonly Vector2 SceneDimensions = new(480, 270);
 
-    public static readonly Vector2 sceneDimensions = new(480, 270);
+    private static readonly ConfigEntry<bool> SortDescendingConfig =
+        Configuration.Config.Bind("SceneManager", "SortDescending", false, "Sort scenes descending (Z-A)");
 
-    private static readonly ConfigEntry<bool> sortDescending;
-    private static readonly ConfigEntry<SortMode> currentSortMode;
+    private static readonly ConfigEntry<SortMode> CurrentSortModeConfig =
+        Configuration.Config.Bind("SceneManager", "SortMode", SortMode.Name, "Scene sorting mode");
 
     private static byte[] tempSceneData;
 
-    public static bool Busy { get; private set; }
+    private readonly MeidoPhotoStudio meidoPhotoStudio;
 
-    private static string TempScenePath =>
-        Path.Combine(Constants.configPath, "mpstempscene");
+    static SceneManager()
+    {
+        Input.Register(MpsKey.OpenSceneManager, KeyCode.F8, "Hide/show scene manager");
+        Input.Register(MpsKey.SaveScene, KeyCode.S, "Quick save scene");
+        Input.Register(MpsKey.LoadScene, KeyCode.A, "Load quick saved scene");
+    }
 
-    private readonly MeidoPhotoStudio meidoPhotoStudio;
+    public SceneManager(MeidoPhotoStudio meidoPhotoStudio)
+    {
+        this.meidoPhotoStudio = meidoPhotoStudio;
+
+        Activate();
+    }
+
+    public enum SortMode
+    {
+        Name,
+        DateCreated,
+        DateModified,
+    }
+
+    public static bool Busy { get; private set; }
 
     public bool Initialized { get; private set; }
+
     public bool KankyoMode { get; set; }
+
     public int CurrentSceneIndex { get; private set; } = -1;
+
     public List<MPSScene> SceneList { get; } = new();
+
     public int CurrentDirectoryIndex { get; private set; } = -1;
 
     public bool SortDescending
     {
-        get => sortDescending.Value;
-        set => sortDescending.Value = value;
+        get => SortDescendingConfig.Value;
+        set => SortDescendingConfig.Value = value;
     }
 
     public string CurrentDirectoryName =>
@@ -46,45 +71,30 @@ public class SceneManager : IManager
         KankyoMode ? Constants.KankyoDirectoryList : Constants.SceneDirectoryList;
 
     public string CurrentBasePath =>
-        KankyoMode ? Constants.kankyoPath : Constants.scenesPath;
+        KankyoMode ? Constants.KankyoPath : Constants.ScenesPath;
 
     public string CurrentScenesDirectory =>
         CurrentDirectoryIndex is 0 ? CurrentBasePath : Path.Combine(CurrentBasePath, CurrentDirectoryName);
 
     public SortMode CurrentSortMode
     {
-        get => currentSortMode.Value;
-        private set => currentSortMode.Value = value;
+        get => CurrentSortModeConfig.Value;
+        private set => CurrentSortModeConfig.Value = value;
     }
 
     public MPSScene CurrentScene =>
         SceneList.Count is 0 ? null : SceneList[CurrentSceneIndex];
 
+    private static string TempScenePath =>
+        Path.Combine(Constants.ConfigPath, "mpstempscene");
+
     private int SortDirection =>
         SortDescending ? -1 : 1;
 
-    static SceneManager()
-    {
-        sortDescending =
-            Configuration.Config.Bind("SceneManager", "SortDescending", false, "Sort scenes descending (Z-A)");
-
-        currentSortMode =
-            Configuration.Config.Bind("SceneManager", "SortMode", SortMode.Name, "Scene sorting mode");
-
-        Input.Register(MpsKey.OpenSceneManager, KeyCode.F8, "Hide/show scene manager");
-        Input.Register(MpsKey.SaveScene, KeyCode.S, "Quick save scene");
-        Input.Register(MpsKey.LoadScene, KeyCode.A, "Load quick saved scene");
-    }
-
-    public SceneManager(MeidoPhotoStudio meidoPhotoStudio)
+    public void Activate()
     {
-        this.meidoPhotoStudio = meidoPhotoStudio;
-
-        Activate();
     }
 
-    public void Activate() { }
-
     public void Initialize()
     {
         if (Initialized)
@@ -182,7 +192,7 @@ public class SceneManager : IManager
 
         if (!fullPath.StartsWith(CurrentBasePath))
         {
-            var baseDirectoryName = KankyoMode ? Constants.kankyoDirectory : Constants.sceneDirectory;
+            var baseDirectoryName = KankyoMode ? Constants.KankyoDirectory : Constants.SceneDirectory;
 
             Utility.LogError($"Could not add directory to {baseDirectoryName}. Path is invalid: '{fullPath}'");
 
@@ -265,13 +275,12 @@ public class SceneManager : IManager
 
     private void UpdateDirectoryList()
     {
-        var baseDirectoryName = KankyoMode ? Constants.kankyoDirectory : Constants.sceneDirectory;
+        var baseDirectoryName = KankyoMode ? Constants.KankyoDirectory : Constants.SceneDirectory;
 
         CurrentDirectoryList.Sort((a, b) =>
             a.Equals(baseDirectoryName, StringComparison.InvariantCultureIgnoreCase)
                 ? -1
-                : WindowsLogicalComparer.StrCmpLogicalW(a, b)
-            );
+                : WindowsLogicalComparer.StrCmpLogicalW(a, b));
     }
 
     private void ClearSceneList()
@@ -325,7 +334,7 @@ public class SceneManager : IManager
             var fileName = $"{scenePrefix}{Utility.Timestamp}.png";
             var savePath = Path.Combine(CurrentScenesDirectory, fileName);
 
-            Utility.ResizeToFit(screenshot, (int)sceneDimensions.x, (int)sceneDimensions.y);
+            Utility.ResizeToFit(screenshot, (int)SceneDimensions.x, (int)SceneDimensions.y);
 
             try
             {
@@ -362,7 +371,9 @@ public class SceneManager : IManager
             SortScenes(CurrentSortMode);
         }
         else
+        {
             Object.DestroyImmediate(screenshot);
+        }
 
         Busy = false;
     }

+ 13 - 12
src/MeidoPhotoStudio.Plugin/Managers/WindowManager.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+
 using UnityEngine;
 
 using static MeidoPhotoStudio.Plugin.Constants;
@@ -7,21 +8,21 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class WindowManager : IManager
 {
-    private readonly Dictionary<Window, BaseWindow> Windows = new();
+    private readonly Dictionary<Window, BaseWindow> windows = new();
+
+    public WindowManager() =>
+        InputManager.Register(MpsKey.ToggleUI, KeyCode.Tab, "Show/hide all UI");
 
     public BaseWindow this[Window id]
     {
-        get => Windows[id];
+        get => windows[id];
         set
         {
-            Windows[id] = value;
-            Windows[id].Activate();
+            windows[id] = value;
+            windows[id].Activate();
         }
     }
 
-    public WindowManager() =>
-        InputManager.Register(MpsKey.ToggleUI, KeyCode.Tab, "Show/hide all UI");
-
     public void DrawWindow(BaseWindow window)
     {
         if (!window.Visible)
@@ -29,30 +30,30 @@ public class WindowManager : IManager
 
         var windowStyle = new GUIStyle(GUI.skin.box);
 
-        window.WindowRect = GUI.Window(window.windowID, window.WindowRect, window.GUIFunc, "", windowStyle);
+        window.WindowRect = GUI.Window(window.WindowID, window.WindowRect, window.GUIFunc, string.Empty, windowStyle);
     }
 
     public void DrawWindows()
     {
-        foreach (var window in Windows.Values)
+        foreach (var window in windows.Values)
             DrawWindow(window);
     }
 
     public void Update()
     {
-        foreach (var window in Windows.Values)
+        foreach (var window in windows.Values)
             window.Update();
     }
 
     public void Activate()
     {
-        foreach (var window in Windows.Values)
+        foreach (var window in windows.Values)
             window.Activate();
     }
 
     public void Deactivate()
     {
-        foreach (var window in Windows.Values)
+        foreach (var window in windows.Values)
             window.Deactivate();
     }
 }

+ 27 - 0
src/MeidoPhotoStudio.Plugin/Meido/AttachPoint.cs

@@ -0,0 +1,27 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public enum AttachPoint
+{
+    None,
+    Head,
+    Neck,
+    UpperArmL,
+    UpperArmR,
+    ForearmL,
+    ForearmR,
+    MuneL,
+    MuneR,
+    HandL,
+    HandR,
+    Pelvis,
+    ThighL,
+    ThighR,
+    CalfL,
+    CalfR,
+    FootL,
+    FootR,
+    Spine1a,
+    Spine1,
+    Spine0a,
+    Spine0,
+}

+ 29 - 0
src/MeidoPhotoStudio.Plugin/Meido/AttachPointInfo.cs

@@ -0,0 +1,29 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public readonly struct AttachPointInfo
+{
+    private static readonly AttachPointInfo EmptyValue = new(AttachPoint.None, string.Empty, -1);
+
+    public AttachPointInfo(AttachPoint attachPoint, Meido meido)
+    {
+        AttachPoint = attachPoint;
+        MaidGuid = meido.Maid.status.guid;
+        MaidIndex = meido.Slot;
+    }
+
+    public AttachPointInfo(AttachPoint attachPoint, string maidGuid, int maidIndex)
+    {
+        AttachPoint = attachPoint;
+        MaidGuid = maidGuid;
+        MaidIndex = maidIndex;
+    }
+
+    public static ref readonly AttachPointInfo Empty =>
+        ref EmptyValue;
+
+    public AttachPoint AttachPoint { get; }
+
+    public string MaidGuid { get; }
+
+    public int MaidIndex { get; }
+}

+ 18 - 0
src/MeidoPhotoStudio.Plugin/Meido/GravityEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class GravityEventArgs : EventArgs
+{
+    public GravityEventArgs(bool isSkirt, Vector3 localPosition)
+    {
+        LocalPosition = localPosition;
+        IsSkirt = isSkirt;
+    }
+
+    public Vector3 LocalPosition { get; }
+
+    public bool IsSkirt { get; }
+}

+ 13 - 13
src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointFinger.cs

@@ -6,9 +6,9 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointFinger : DragPointMeido
 {
-    private static readonly Color dragpointColour = new(0.1f, 0.4f, 0.95f, defaultAlpha);
+    private static readonly Color DragpointColour = new(0.1f, 0.4f, 0.95f, DefaultAlpha);
 
-    private readonly TBody.IKCMO IK = new();
+    private readonly TBody.IKCMO ik = new();
     private readonly Quaternion[] jointRotation = new Quaternion[2];
 
     private IKCtrlData ikCtrlData;
@@ -37,7 +37,7 @@ public class DragPointFinger : DragPointMeido
         else
             ApplyProperties(false, false, false);
 
-        ApplyColour(dragpointColour);
+        ApplyColour(DragpointColour);
     }
 
     protected override void UpdateDragType() =>
@@ -51,10 +51,10 @@ public class DragPointFinger : DragPointMeido
     {
         base.OnMouseDown();
 
-        jointRotation[jointUpper] = ikChain[jointUpper].localRotation;
-        jointRotation[jointMiddle] = ikChain[jointMiddle].localRotation;
+        jointRotation[JointUpper] = ikChain[JointUpper].localRotation;
+        jointRotation[JointMiddle] = ikChain[JointMiddle].localRotation;
 
-        InitializeIK(IK, ikChain[jointUpper], ikChain[jointUpper], ikChain[jointMiddle]);
+        InitializeIK(ik, ikChain[JointUpper], ikChain[JointUpper], ikChain[JointMiddle]);
     }
 
     protected override void Drag()
@@ -64,25 +64,25 @@ public class DragPointFinger : DragPointMeido
 
         if (CurrentDragType is DragType.MoveXZ)
         {
-            Porc(IK, ikCtrlData, ikChain[jointUpper], ikChain[jointUpper], ikChain[jointMiddle]);
+            Porc(ik, ikCtrlData, ikChain[JointUpper], ikChain[JointUpper], ikChain[JointMiddle]);
 
             if (!baseFinger)
             {
-                SetRotation(jointUpper);
-                SetRotation(jointMiddle);
+                SetRotation(JointUpper);
+                SetRotation(JointMiddle);
             }
             else
             {
-                jointRotation[jointUpper] = ikChain[jointUpper].localRotation;
-                jointRotation[jointMiddle] = ikChain[jointMiddle].localRotation;
+                jointRotation[JointUpper] = ikChain[JointUpper].localRotation;
+                jointRotation[JointMiddle] = ikChain[JointMiddle].localRotation;
             }
         }
         else if (CurrentDragType is DragType.RotLocalY)
         {
             var mouseDelta = MouseDelta();
 
-            ikChain[jointUpper].localRotation = jointRotation[jointUpper];
-            ikChain[jointUpper].Rotate(Vector3.right * (mouseDelta.x / 1.5f));
+            ikChain[JointUpper].localRotation = jointRotation[JointUpper];
+            ikChain[JointUpper].Rotate(Vector3.right * (mouseDelta.x / 1.5f));
         }
     }
 

+ 13 - 2
src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointHead.cs

@@ -1,4 +1,5 @@
 using System;
+
 using UnityEngine;
 
 using Input = MeidoPhotoStudio.Plugin.InputManager;
@@ -7,12 +8,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointHead : DragPointMeido
 {
-    public event EventHandler Select;
-
     private Quaternion headRotation;
     private Vector3 eyeRotationL;
     private Vector3 eyeRotationR;
 
+    public event EventHandler Select;
+
     public bool IsIK { get; set; }
 
     protected override void ApplyDragType()
@@ -25,7 +26,9 @@ public class DragPointHead : DragPointMeido
             ApplyProperties(active, false, false);
         }
         else
+        {
             ApplyProperties(CurrentDragType is not DragType.None, false, false);
+        }
     }
 
     protected override void UpdateDragType()
@@ -34,19 +37,25 @@ public class DragPointHead : DragPointMeido
         var alt = Input.Alt;
 
         if (alt && Input.Control)
+        {
             // eyes
             CurrentDragType = shift
                 ? DragType.MoveY
                 : DragType.MoveXZ;
+        }
         else if (alt)
+        {
             // head
             CurrentDragType = shift
                 ? DragType.RotLocalY
                 : DragType.RotLocalXZ;
+        }
         else
+        {
             CurrentDragType = Input.GetKey(MpsKey.DragSelect)
                 ? DragType.Select
                 : DragType.None;
+        }
     }
 
     protected override void OnMouseDown()
@@ -70,7 +79,9 @@ public class DragPointHead : DragPointMeido
             meido.Body.quaDefEyeR = meido.DefaultEyeRotR;
         }
         else if (CurrentDragType is DragType.RotLocalY or DragType.RotLocalXZ)
+        {
             meido.FreeLook = !meido.FreeLook;
+        }
     }
 
     protected override void Drag()

+ 1 - 0
src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointPelvis.cs

@@ -19,6 +19,7 @@ public class DragPointPelvis : DragPointMeido
     }
 
     protected override void UpdateDragType() =>
+
         // TODO: Rethink this formatting
         CurrentDragType = Input.Alt && !Input.Control
             ? Input.Shift

+ 14 - 0
src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointSpine.cs

@@ -45,7 +45,9 @@ public class DragPointSpine : DragPointMeido
                 ApplyProperties(!isThigh, !isThigh, false);
         }
         else
+        {
             ApplyProperties(false, false, false);
+        }
     }
 
     protected override void UpdateDragType()
@@ -54,19 +56,31 @@ public class DragPointSpine : DragPointMeido
         var alt = Input.Alt;
 
         if (OtherDragType())
+        {
             CurrentDragType = DragType.Ignore;
+        }
         else if (isThigh && !Input.Control && alt && shift)
+        {
             // gizmo thigh rotation
             CurrentDragType = DragType.RotLocalXZ;
+        }
         else if (alt)
+        {
             CurrentDragType = DragType.Ignore;
+        }
         else if (shift)
+        {
             CurrentDragType = DragType.RotLocalY;
+        }
         else if (Input.Control)
+        {
             // hip y transform and spine gizmo rotation
             CurrentDragType = DragType.MoveY;
+        }
         else
+        {
             CurrentDragType = DragType.None;
+        }
     }
 
     protected override void OnMouseDown()

+ 7 - 5
src/MeidoPhotoStudio.Plugin/Meido/IK/DragPointTorso.cs

@@ -6,8 +6,9 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointTorso : DragPointMeido
 {
-    private static readonly float[] blah = new[] { 0.03f, 0.1f, 0.09f, 0.07f };
-    private static readonly float[] something = new[] { 0.08f, 0.15f };
+    // TODO: Rename these to something more descriptive
+    private static readonly float[] Blah = new[] { 0.03f, 0.1f, 0.09f, 0.07f };
+    private static readonly float[] Something = new[] { 0.08f, 0.15f };
 
     private readonly Quaternion[] spineRotation = new Quaternion[4];
     private readonly Transform[] spine = new Transform[4];
@@ -37,6 +38,7 @@ public class DragPointTorso : DragPointMeido
     }
 
     protected override void UpdateDragType() =>
+
         // TODO: Rethink this formatting
         CurrentDragType = Input.Alt && !Input.Control
             ? Input.Shift
@@ -68,15 +70,15 @@ public class DragPointTorso : DragPointMeido
             for (var i = 0; i < spine.Length; i++)
             {
                 spine[i].localRotation = spineRotation[i];
-                spine[i].Rotate(camera.transform.forward, -mouseDelta.x / 1.5f * blah[i], Space.World);
-                spine[i].Rotate(camera.transform.right, mouseDelta.y * blah[i], Space.World);
+                spine[i].Rotate(camera.transform.forward, -mouseDelta.x / 1.5f * Blah[i], Space.World);
+                spine[i].Rotate(camera.transform.right, mouseDelta.y * Blah[i], Space.World);
             }
 
         if (CurrentDragType is DragType.RotLocalY)
             for (var i = 0; i < spine.Length; i++)
             {
                 spine[i].localRotation = spineRotation[i];
-                spine[i].Rotate(Vector3.right * (mouseDelta.x / 1.5f * something[i / 2]));
+                spine[i].Rotate(Vector3.right * (mouseDelta.x / 1.5f * Something[i / 2]));
             }
     }
 }

+ 1 - 1
src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointChain.cs

@@ -30,7 +30,7 @@ public abstract class DragPointChain : DragPointMeido
 
         InitializeRotation();
 
-        InitializeIK(IK, ikChain[jointUpper], ikChain[jointMiddle], ikChain[jointLower]);
+        InitializeIK(IK, ikChain[JointUpper], ikChain[JointMiddle], ikChain[JointLower]);
     }
 
     protected void InitializeRotation()

+ 17 - 7
src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointLimb.cs

@@ -17,7 +17,7 @@ public class DragPointLimb : DragPointChain
         {
             base.IsBone = value;
 
-            BaseScale = isBone ? boneScale : OriginalScale;
+            BaseScale = isBone ? BoneScale : OriginalScale;
         }
     }
 
@@ -42,7 +42,9 @@ public class DragPointLimb : DragPointChain
         var isBone = IsBone;
 
         if (CurrentDragType is DragType.Ignore)
+        {
             ApplyProperties();
+        }
         else if (current is DragType.RotLocalXZ)
         {
             if (isLower)
@@ -74,7 +76,9 @@ public class DragPointLimb : DragPointChain
                 ApplyProperties();
         }
         else
+        {
             ApplyProperties(true, isBone, false);
+        }
     }
 
     protected override void UpdateDragType()
@@ -84,7 +88,9 @@ public class DragPointLimb : DragPointChain
 
         // Check for DragMove so that hand dragpoint is not in the way
         if (OtherDragType())
+        {
             CurrentDragType = DragType.Ignore;
+        }
         else if (control && !Input.GetKey(MpsKey.DragMove))
         {
             if (alt)
@@ -93,14 +99,18 @@ public class DragPointLimb : DragPointChain
                 CurrentDragType = DragType.MoveXZ;
         }
         else if (alt)
+        {
             // TODO: Rethink this formatting
             CurrentDragType = Input.Shift
                 ? DragType.RotLocalY
                 : DragType.RotLocalXZ;
+        }
         else
+        {
             CurrentDragType = Input.Shift
                 ? DragType.Ignore
                 : DragType.None;
+        }
     }
 
     protected override void Drag()
@@ -112,9 +122,9 @@ public class DragPointLimb : DragPointChain
 
         if (CurrentDragType is DragType.None || altRotation)
         {
-            var upperJoint = altRotation ? jointMiddle : jointUpper;
+            var upperJoint = altRotation ? JointMiddle : JointUpper;
 
-            Porc(IK, ikCtrlData, ikChain[upperJoint], ikChain[jointMiddle], ikChain[jointLower]);
+            Porc(IK, ikCtrlData, ikChain[upperJoint], ikChain[JointMiddle], ikChain[JointLower]);
 
             InitializeRotation();
         }
@@ -123,7 +133,7 @@ public class DragPointLimb : DragPointChain
 
         if (CurrentDragType is DragType.RotLocalY)
         {
-            var joint = isMiddle ? jointUpper : jointLower;
+            var joint = isMiddle ? JointUpper : JointLower;
 
             ikChain[joint].localRotation = jointRotation[joint];
             ikChain[joint].Rotate(Vector3.right * (-mouseDelta.x / 1.5f));
@@ -131,9 +141,9 @@ public class DragPointLimb : DragPointChain
 
         if (CurrentDragType is DragType.RotLocalXZ)
         {
-            ikChain[jointLower].localRotation = jointRotation[jointLower];
-            ikChain[jointLower].Rotate(Vector3.up * (foot * mouseDelta.x / 1.5f));
-            ikChain[jointLower].Rotate(Vector3.forward * (foot * mouseDelta.y / 1.5f));
+            ikChain[JointLower].localRotation = jointRotation[JointLower];
+            ikChain[JointLower].Rotate(Vector3.up * (foot * mouseDelta.x / 1.5f));
+            ikChain[JointLower].Rotate(Vector3.forward * (foot * mouseDelta.y / 1.5f));
         }
     }
 }

+ 8 - 6
src/MeidoPhotoStudio.Plugin/Meido/IK/IK Chain/DragPointMune.cs

@@ -36,11 +36,12 @@ public class DragPointMune : DragPointChain
     }
 
     protected override void UpdateDragType() =>
+
         // TODO: Rethink this formatting
         CurrentDragType = Input.Control && Input.Alt
             ? Input.Shift
-                ? DragType.RotLocalY
-                : DragType.RotLocalXZ
+            ? DragType.RotLocalY
+            : DragType.RotLocalXZ
             : DragType.None;
 
     protected override void Drag()
@@ -50,7 +51,7 @@ public class DragPointMune : DragPointChain
 
         if (CurrentDragType is DragType.RotLocalXZ)
         {
-            Porc(IK, ikCtrlData, ikChain[jointUpper], ikChain[jointMiddle], ikChain[jointLower]);
+            Porc(IK, ikCtrlData, ikChain[JointUpper], ikChain[JointMiddle], ikChain[JointLower]);
             InitializeRotation();
         }
 
@@ -58,10 +59,11 @@ public class DragPointMune : DragPointChain
         {
             var mouseDelta = MouseDelta();
 
-            ikChain[jointLower].localRotation = jointRotation[jointLower];
+            ikChain[JointLower].localRotation = jointRotation[JointLower];
+
             // TODO: Reorder operands for better performance
-            ikChain[jointLower].Rotate(Vector3.up * (-mouseDelta.x / 1.5f) * inv);
-            ikChain[jointLower].Rotate(Vector3.forward * (mouseDelta.y / 1.5f) * inv);
+            ikChain[JointLower].Rotate(Vector3.up * (-mouseDelta.x / 1.5f) * inv);
+            ikChain[JointLower].Rotate(Vector3.forward * (mouseDelta.y / 1.5f) * inv);
         }
     }
 }

+ 78 - 69
src/MeidoPhotoStudio.Plugin/Meido/Meido.cs

@@ -5,20 +5,20 @@ using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Xml.Linq;
+
 using UnityEngine;
+
 using static TBody;
+
 using Object = UnityEngine.Object;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class Meido
 {
-    public enum Curl { Front, Back, Shift }
-    public enum Mask { All, Underwear, Nude }
+    public static readonly string DefaultFaceBlendSet = "通常";
 
-    public static readonly string defaultFaceBlendSet = "通常";
-
-    public static readonly string[] faceKeys =
+    public static readonly string[] FaceKeys =
         new string[24]
         {
             "eyeclose", "eyeclose2", "eyeclose3", "eyebig", "eyeclose6", "eyeclose5", "hitomih",
@@ -27,39 +27,81 @@ public class Meido
             "tangup", "tangopen",
         };
 
-    public static readonly string[] faceToggleKeys =
+    public static readonly string[] FaceToggleKeys =
         new string[12]
         {
             // blush, shade, nose up, tears, drool, teeth
             "hoho2", "shock", "nosefook", "namida", "yodare", "toothoff",
+
             // cry 1, cry 2, cry 3, blush 1, blush 2, blush 3
             "tear1", "tear2", "tear3", "hohos", "hoho", "hohol",
         };
 
+#pragma warning disable SA1308
+
+    // TODO: Refactor reflection to using private members directly
     private readonly FieldInfo m_eMaskMode = Utility.GetFieldInfo<TBody>("m_eMaskMode");
+#pragma warning restore SA1308
+
+    private bool initialized;
+    private float[] blendSetValueBackup;
+    private bool freeLook;
+
+    public Meido(int stockMaidIndex)
+    {
+        StockNo = stockMaidIndex;
+        Maid = GameMain.Instance.CharacterMgr.GetStockMaid(stockMaidIndex);
+
+        IKManager = new(this);
+        IKManager.SelectMaid += (_, args) =>
+            OnUpdateMeido(args);
+    }
 
     public event EventHandler<GravityEventArgs> GravityMove;
+
     public event EventHandler<MeidoUpdateEventArgs> UpdateMeido;
 
-    private bool initialized;
-    private float[] BlendSetValueBackup;
-    private bool freeLook;
+    public enum Curl
+    {
+        Front,
+        Back,
+        Shift,
+    }
+
+    public enum Mask
+    {
+        All,
+        Underwear,
+        Nude,
+    }
 
     public MaskMode CurrentMaskMode =>
         !Body.isLoadedBody ? default : (MaskMode)m_eMaskMode.GetValue(Body);
 
     public DragPointGravity HairGravityControl { get; private set; }
+
     public DragPointGravity SkirtGravityControl { get; private set; }
+
     public Quaternion DefaultEyeRotL { get; private set; }
+
     public Quaternion DefaultEyeRotR { get; private set; }
+
     public bool Active { get; private set; }
+
     public bool IsEditMaid { get; set; }
+
     public PoseInfo CachedPose { get; private set; } = PoseInfo.DefaultPose;
-    public string CurrentFaceBlendSet { get; private set; } = defaultFaceBlendSet;
+
+    public string CurrentFaceBlendSet { get; private set; } = DefaultFaceBlendSet;
+
     public int Slot { get; private set; }
+
     public bool Loading { get; private set; }
+
     public int StockNo { get; }
+
     public Maid Maid { get; }
+
     public MeidoDragPointManager IKManager { get; }
 
     public TBody Body =>
@@ -165,7 +207,9 @@ public class Meido
                 return;
 
             if (value)
+            {
                 Maid.GetAnimation().Stop();
+            }
             else
             {
                 Body.boEyeToCam = true;
@@ -202,16 +246,6 @@ public class Meido
         }
     }
 
-    public Meido(int stockMaidIndex)
-    {
-        StockNo = stockMaidIndex;
-        Maid = GameMain.Instance.CharacterMgr.GetStockMaid(stockMaidIndex);
-
-        IKManager = new(this);
-        IKManager.SelectMaid += (_, args) =>
-            OnUpdateMeido(args);
-    }
-
     public void Load(int slot)
     {
         if (Busy)
@@ -264,7 +298,7 @@ public class Meido
             ApplyGravity(Vector3.zero, skirt: false);
             ApplyGravity(Vector3.zero, skirt: true);
 
-            SetFaceBlendSet(defaultFaceBlendSet);
+            SetFaceBlendSet(DefaultFaceBlendSet);
         }
 
         AllProcPropSeqStartPatcher.SequenceStart -= ReinitializeBody;
@@ -312,7 +346,7 @@ public class Meido
         if (!Body.isLoadedBody)
             return;
 
-        if (pose.StartsWith(Constants.customPosePath))
+        if (pose.StartsWith(Constants.CustomPosePath))
         {
             var poseFilename = Path.GetFileNameWithoutExtension(pose);
 
@@ -448,7 +482,7 @@ public class Meido
     {
         var faceData = new Dictionary<string, float>();
 
-        foreach (var hash in faceKeys.Concat(faceToggleKeys))
+        foreach (var hash in FaceKeys.Concat(FaceToggleKeys))
         {
             try
             {
@@ -456,7 +490,10 @@ public class Meido
 
                 faceData.Add(hash, value);
             }
-            catch { }
+            catch
+            {
+                // Ignored
+            }
         }
 
         return faceData;
@@ -464,7 +501,7 @@ public class Meido
 
     public void SetFaceBlendSet(string blendSet)
     {
-        if (blendSet.StartsWith(Constants.customFacePath))
+        if (blendSet.StartsWith(Constants.CustomFacePath))
         {
             var blendSetFileName = Path.GetFileNameWithoutExtension(blendSet);
 
@@ -480,7 +517,7 @@ public class Meido
                     return;
                 }
 
-                var hashKeys = new HashSet<string>(faceKeys.Concat(faceToggleKeys));
+                var hashKeys = new HashSet<string>(FaceKeys.Concat(FaceToggleKeys));
 
                 foreach (var element in faceDataElement.Elements())
                 {
@@ -509,12 +546,16 @@ public class Meido
                         {
                             SetFaceBlendValue(key, value);
                         }
-                        catch { /* Ignore */ }
+                        catch
+                        {
+                            // Ignored.
+                        }
                     }
                     else
+                    {
                         Utility.LogWarning(
-                            $"{blendSetFileName}: Could not parse value '{element.Value}' of '{key}' at line {line}"
-                        );
+                                $"{blendSetFileName}: Could not parse value '{element.Value}' of '{key}' at line {line}");
+                    }
                 }
             }
             catch (System.Xml.XmlException e)
@@ -549,7 +590,7 @@ public class Meido
 
             var morph = Body.Face.morph;
 
-            foreach (var faceKey in faceKeys)
+            foreach (var faceKey in FaceKeys)
             {
                 var hash = Utility.GP01FbFaceHash(morph, faceKey);
 
@@ -718,7 +759,7 @@ public class Meido
             initialized = true;
         }
 
-        if (BlendSetValueBackup is null)
+        if (blendSetValueBackup is null)
             BackupBlendSetValues();
 
         if (!HairGravityControl)
@@ -732,7 +773,7 @@ public class Meido
 
         IKManager.Initialize();
 
-        SetFaceBlendSet(defaultFaceBlendSet);
+        SetFaceBlendSet(DefaultFaceBlendSet);
 
         IK = true;
         Stop = false;
@@ -746,7 +787,7 @@ public class Meido
         if (Loading || !Body.isLoadedBody)
             return;
 
-        if (args.maid.status.guid != Maid.status.guid)
+        if (args.Maid.status.guid != Maid.status.guid)
             return;
 
         var gravityControlProps =
@@ -768,7 +809,7 @@ public class Meido
         // Change face
         if (Maid.GetProp(MPN.head).boDut)
         {
-            SetFaceBlendSet(defaultFaceBlendSet);
+            SetFaceBlendSet(DefaultFaceBlendSet);
             action += ReinitializeFace;
         }
 
@@ -832,13 +873,13 @@ public class Meido
     {
         var values = Body.Face.morph.dicBlendSet[CurrentFaceBlendSet];
 
-        BlendSetValueBackup = new float[values.Length];
-        values.CopyTo(BlendSetValueBackup, 0);
+        blendSetValueBackup = new float[values.Length];
+        values.CopyTo(blendSetValueBackup, 0);
     }
 
     private void ApplyBackupBlendSet()
     {
-        BlendSetValueBackup.CopyTo(Body.Face.morph.dicBlendSet[CurrentFaceBlendSet], 0);
+        blendSetValueBackup.CopyTo(Body.Face.morph.dicBlendSet[CurrentFaceBlendSet], 0);
         Maid.boNoseFook = false;
     }
 
@@ -897,35 +938,3 @@ public class Meido
         GravityMove?.Invoke(this, args);
     }
 }
-
-public class GravityEventArgs : EventArgs
-{
-    public Vector3 LocalPosition { get; }
-    public bool IsSkirt { get; }
-
-    public GravityEventArgs(bool isSkirt, Vector3 localPosition)
-    {
-        LocalPosition = localPosition;
-        IsSkirt = isSkirt;
-    }
-}
-
-public readonly struct PoseInfo
-{
-    private static readonly PoseInfo defaultPose =
-        new(Constants.PoseGroupList[0], Constants.PoseDict[Constants.PoseGroupList[0]][0]);
-
-    public static ref readonly PoseInfo DefaultPose =>
-        ref defaultPose;
-
-    public string PoseGroup { get; }
-    public string Pose { get; }
-    public bool CustomPose { get; }
-
-    public PoseInfo(string poseGroup, string pose, bool customPose = false)
-    {
-        PoseGroup = poseGroup;
-        Pose = pose;
-        CustomPose = customPose;
-    }
-}

+ 276 - 245
src/MeidoPhotoStudio.Plugin/Meido/MeidoDragPointManager.cs

@@ -2,62 +2,13 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
-public enum AttachPoint
-{
-    None, Head, Neck, UpperArmL, UpperArmR, ForearmL, ForearmR, MuneL, MuneR, HandL, HandR, Pelvis, ThighL, ThighR,
-    CalfL, CalfR, FootL, FootR, Spine1a, Spine1, Spine0a, Spine0,
-}
-
 public class MeidoDragPointManager
 {
-    private enum Bone
-    {
-        // Head
-        Head, HeadNub, ClavicleL, ClavicleR,
-
-        // Arms
-        UpperArmL, UpperArmR, ForearmL, ForearmR, HandL, HandR, /*IKHandL, IKHandR,*/
-
-        // Mune
-        MuneL, MuneSubL, MuneR, MuneSubR,
-
-        // Spine
-        Neck, Spine, Spine0a, Spine1, Spine1a, ThighL, ThighR,
-
-        // Hip
-        Pelvis, Hip,
-
-        // Legs
-        CalfL, CalfR, FootL, FootR,
-
-        // Dragpoint specific
-        Cube, Body, Torso,
-
-        // Fingers
-        Finger0L, Finger01L, Finger02L, Finger0NubL,
-        Finger1L, Finger11L, Finger12L, Finger1NubL,
-        Finger2L, Finger21L, Finger22L, Finger2NubL,
-        Finger3L, Finger31L, Finger32L, Finger3NubL,
-        Finger4L, Finger41L, Finger42L, Finger4NubL,
-        Finger0R, Finger01R, Finger02R, Finger0NubR,
-        Finger1R, Finger11R, Finger12R, Finger1NubR,
-        Finger2R, Finger21R, Finger22R, Finger2NubR,
-        Finger3R, Finger31R, Finger32R, Finger3NubR,
-        Finger4R, Finger41R, Finger42R, Finger4NubR,
-
-        // Toes
-        Toe0L, Toe01L, Toe0NubL,
-        Toe1L, Toe11L, Toe1NubL,
-        Toe2L, Toe21L, Toe2NubL,
-        Toe0R, Toe01R, Toe0NubR,
-        Toe1R, Toe11R, Toe1NubR,
-        Toe2R, Toe21R, Toe2NubR,
-    }
-
     private static readonly Dictionary<AttachPoint, Bone> PointToBone = new()
     {
         [AttachPoint.Head] = Bone.Head,
@@ -90,8 +41,136 @@ public class MeidoDragPointManager
 
     private static bool cubeActive;
     private static bool cubeSmall;
-    private static EventHandler CubeActiveChange;
-    private static EventHandler CubeSmallChange;
+
+    private static EventHandler cubeActiveChange;
+
+    private static EventHandler cubeSmallChange;
+
+    private readonly Meido meido;
+    private readonly Dictionary<Bone, DragPointMeido> dragPoints = new();
+
+    private Dictionary<Bone, Transform> boneTransform = new();
+    private DragPointBody dragBody;
+    private DragPointBody dragCube;
+    private bool initialized;
+    private bool isBone;
+    private bool active = true;
+
+    public MeidoDragPointManager(Meido meido) =>
+        this.meido = meido;
+
+    public event EventHandler<MeidoUpdateEventArgs> SelectMaid;
+
+    private enum Bone
+    {
+        // Head
+        Head,
+        HeadNub,
+        ClavicleL,
+        ClavicleR,
+
+        // Arms
+        UpperArmL,
+        UpperArmR,
+        ForearmL,
+        ForearmR,
+
+        // IKHandL and IKHandR
+        HandL,
+        HandR,
+
+        // Mune
+        MuneL,
+        MuneSubL,
+        MuneR,
+        MuneSubR,
+
+        // Spine
+        Neck,
+        Spine,
+        Spine0a,
+        Spine1,
+        Spine1a,
+        ThighL,
+        ThighR,
+
+        // Hip
+        Pelvis,
+        Hip,
+
+        // Legs
+        CalfL,
+        CalfR,
+        FootL,
+        FootR,
+
+        // Dragpoint specific
+        Cube,
+        Body,
+        Torso,
+
+        // Fingers
+        Finger0L,
+        Finger01L,
+        Finger02L,
+        Finger0NubL,
+        Finger1L,
+        Finger11L,
+        Finger12L,
+        Finger1NubL,
+        Finger2L,
+        Finger21L,
+        Finger22L,
+        Finger2NubL,
+        Finger3L,
+        Finger31L,
+        Finger32L,
+        Finger3NubL,
+        Finger4L,
+        Finger41L,
+        Finger42L,
+        Finger4NubL,
+        Finger0R,
+        Finger01R,
+        Finger02R,
+        Finger0NubR,
+        Finger1R,
+        Finger11R,
+        Finger12R,
+        Finger1NubR,
+        Finger2R,
+        Finger21R,
+        Finger22R,
+        Finger2NubR,
+        Finger3R,
+        Finger31R,
+        Finger32R,
+        Finger3NubR,
+        Finger4R,
+        Finger41R,
+        Finger42R,
+        Finger4NubR,
+
+        // Toes
+        Toe0L,
+        Toe01L,
+        Toe0NubL,
+        Toe1L,
+        Toe11L,
+        Toe1NubL,
+        Toe2L,
+        Toe21L,
+        Toe2NubL,
+        Toe0R,
+        Toe01R,
+        Toe0NubR,
+        Toe1R,
+        Toe11R,
+        Toe1NubR,
+        Toe2R,
+        Toe21R,
+        Toe2NubR,
+    }
 
     public static bool CubeActive
     {
@@ -102,7 +181,7 @@ public class MeidoDragPointManager
                 return;
 
             cubeActive = value;
-            CubeActiveChange?.Invoke(null, EventArgs.Empty);
+            cubeActiveChange?.Invoke(null, EventArgs.Empty);
         }
     }
 
@@ -115,22 +194,10 @@ public class MeidoDragPointManager
                 return;
 
             cubeSmall = value;
-            CubeSmallChange?.Invoke(null, EventArgs.Empty);
+            cubeSmallChange?.Invoke(null, EventArgs.Empty);
         }
     }
 
-    private readonly Meido meido;
-    private readonly Dictionary<Bone, DragPointMeido> DragPoints = new();
-
-    public event EventHandler<MeidoUpdateEventArgs> SelectMaid;
-
-    private Dictionary<Bone, Transform> BoneTransform = new();
-    private DragPointBody dragBody;
-    private DragPointBody dragCube;
-    private bool initialized;
-    private bool isBone;
-    private bool active = true;
-
     public bool IsBone
     {
         get => isBone;
@@ -144,11 +211,11 @@ public class MeidoDragPointManager
 
             isBone = value;
 
-            foreach (var dragPoint in DragPoints.Values)
+            foreach (var dragPoint in dragPoints.Values)
                 dragPoint.IsBone = isBone;
 
             foreach (var bone in SpineBones)
-                DragPoints[bone].gameObject.SetActive(isBone);
+                dragPoints[bone].gameObject.SetActive(isBone);
         }
     }
 
@@ -165,13 +232,13 @@ public class MeidoDragPointManager
 
             active = value;
 
-            foreach (var dragPoint in DragPoints.Values)
+            foreach (var dragPoint in dragPoints.Values)
                 dragPoint.gameObject.SetActive(active);
 
             foreach (var bone in SpineBones)
-                DragPoints[bone].gameObject.SetActive(active && IsBone);
+                dragPoints[bone].gameObject.SetActive(active && IsBone);
 
-            var head = (DragPointHead)DragPoints[Bone.Head];
+            var head = (DragPointHead)dragPoints[Bone.Head];
 
             head.gameObject.SetActive(true);
             head.IsIK = !active;
@@ -179,59 +246,6 @@ public class MeidoDragPointManager
         }
     }
 
-    private static DragPointLimb[] MakeArmChain(Transform lower, Meido meido)
-    {
-        var limbDragPointSize = Vector3.one * 0.12f;
-
-        var realLower = CMT.SearchObjName(meido.Body.goSlot[0].obj_tr, lower.name, false);
-
-        var dragPoints = new DragPointLimb[3];
-
-        for (var i = dragPoints.Length - 1; i >= 0; i--)
-        {
-            var joint = lower;
-            var positionJoint = realLower;
-
-            dragPoints[i] = DragPoint.Make<DragPointLimb>(PrimitiveType.Sphere, limbDragPointSize);
-            dragPoints[i].Initialize(meido, () => positionJoint.position, () => Vector3.zero);
-            dragPoints[i].Set(joint);
-            dragPoints[i].AddGizmo();
-            dragPoints[i].Gizmo.SetAlternateTarget(positionJoint);
-
-            lower = lower.parent;
-            realLower = realLower.parent;
-        }
-
-        return dragPoints;
-    }
-
-    private static DragPointFinger[] MakeFingerChain(Transform lower, Meido meido)
-    {
-        var fingerDragPointSize = Vector3.one * 0.01f;
-
-        var dragPoints = new DragPointFinger[3];
-
-        var realLower = CMT.SearchObjName(meido.Body.goSlot[0].obj_tr, lower.parent.name, false);
-
-        for (var i = dragPoints.Length - 1; i >= 0; i--)
-        {
-            var joint = lower;
-            var positionJoint = realLower;
-
-            dragPoints[i] = DragPoint.Make<DragPointFinger>(PrimitiveType.Sphere, fingerDragPointSize);
-            dragPoints[i].Initialize(meido, () => positionJoint.position, () => Vector3.zero);
-            dragPoints[i].Set(joint);
-
-            lower = lower.parent;
-            realLower = realLower.parent;
-        }
-
-        return dragPoints;
-    }
-
-    public MeidoDragPointManager(Meido meido) =>
-        this.meido = meido;
-
     public void Deserialize(BinaryReader reader)
     {
         var sixtyFourFlag = reader.ReadBoolean();
@@ -239,7 +253,7 @@ public class MeidoDragPointManager
 
         // finger rotations. Toe rotations as well if sixtyFourFlag is false
         for (var bone = Bone.Finger0L; bone <= upperBone; ++bone)
-            BoneTransform[bone].localRotation = reader.ReadQuaternion();
+            boneTransform[bone].localRotation = reader.ReadQuaternion();
 
         var bones = sixtyFourFlag
             ? new[]
@@ -267,7 +281,7 @@ public class MeidoDragPointManager
                 /*
                  * Versions of MM possibly serialized ClavicleL improperly.
                  * At least I think that's what happened otherwise why would they make this check at all.
-                 * https://git.coder.horse/meidomustard/modifiedMM/src/master/MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs#L4355 
+                 * https://git.coder.horse/meidomustard/modifiedMM/src/master/MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs#L4355
                  *
                  * Just look at the way MM serializes rotations.
                  * https://git.coder.horse/meidomustard/modifiedMM/src/master/MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs#L2364
@@ -287,9 +301,9 @@ public class MeidoDragPointManager
             var rotation = reader.ReadQuaternion();
 
             if (sixtyFourFlag || i > localRotationIndex)
-                BoneTransform[bone].localRotation = rotation;
+                boneTransform[bone].localRotation = rotation;
             else
-                BoneTransform[bone].rotation = rotation;
+                boneTransform[bone].rotation = rotation;
         }
 
         // WHY????
@@ -307,10 +321,10 @@ public class MeidoDragPointManager
                 Bone.ThighL, Bone.ThighR, Bone.CalfL, Bone.CalfR, Bone.HandL, Bone.HandR, Bone.FootL, Bone.FootR,
             };
 
-        var singleRotations = single.Select(bone => BoneTransform[bone].eulerAngles).ToList();
-        var pairRotations = pair.Select(bone => BoneTransform[bone].eulerAngles).ToList();
+        var singleRotations = single.Select(bone => boneTransform[bone].eulerAngles).ToList();
+        var pairRotations = pair.Select(bone => boneTransform[bone].eulerAngles).ToList();
 
-        var hip = BoneTransform[Bone.Hip];
+        var hip = boneTransform[Bone.Hip];
         var vecHip = hip.eulerAngles;
 
         var hipL = meido.Maid.body0.GetBone("Hip_L");
@@ -329,7 +343,7 @@ public class MeidoDragPointManager
         {
             var bone = single[i];
 
-            BoneTransform[bone].rotation = FlipRotation(singleRotations[i]);
+            boneTransform[bone].rotation = FlipRotation(singleRotations[i]);
         }
 
         for (var i = 0; i < pair.Length; i += 2)
@@ -337,8 +351,8 @@ public class MeidoDragPointManager
             var boneA = pair[i];
             var boneB = pair[i + 1];
 
-            BoneTransform[boneA].rotation = FlipRotation(pairRotations[i + 1]);
-            BoneTransform[boneB].rotation = FlipRotation(pairRotations[i]);
+            boneTransform[boneA].rotation = FlipRotation(pairRotations[i + 1]);
+            boneTransform[boneB].rotation = FlipRotation(pairRotations[i]);
         }
 
         var leftHand = SerializeHand(right: false);
@@ -355,7 +369,7 @@ public class MeidoDragPointManager
     }
 
     public Transform GetAttachPointTransform(AttachPoint point) =>
-        point is AttachPoint.None ? null : BoneTransform[PointToBone[point]];
+        point is AttachPoint.None ? null : boneTransform[PointToBone[point]];
 
     public byte[] SerializeHand(bool right)
     {
@@ -391,7 +405,7 @@ public class MeidoDragPointManager
 
     public void Destroy()
     {
-        foreach (var dragPoint in DragPoints.Values)
+        foreach (var dragPoint in dragPoints.Values)
             if (dragPoint)
                 UnityEngine.Object.Destroy(dragPoint.gameObject);
 
@@ -401,10 +415,10 @@ public class MeidoDragPointManager
         if (dragBody)
             UnityEngine.Object.Destroy(dragBody.gameObject);
 
-        BoneTransform.Clear();
-        DragPoints.Clear();
-        CubeActiveChange -= OnCubeActive;
-        CubeSmallChange -= OnCubeSmall;
+        boneTransform.Clear();
+        dragPoints.Clear();
+        cubeActiveChange -= OnCubeActive;
+        cubeSmallChange -= OnCubeSmall;
         initialized = false;
     }
 
@@ -414,8 +428,8 @@ public class MeidoDragPointManager
             return;
 
         initialized = true;
-        CubeActiveChange += OnCubeActive;
-        CubeSmallChange += OnCubeSmall;
+        cubeActiveChange += OnCubeActive;
+        cubeSmallChange += OnCubeSmall;
 
         InitializeBones();
         InitializeDragPoints();
@@ -424,27 +438,77 @@ public class MeidoDragPointManager
 
     public void SetDragPointScale(float scale)
     {
-        foreach (var dragPoint in DragPoints.Values)
+        foreach (var dragPoint in dragPoints.Values)
             dragPoint.DragPointScale = scale;
 
         dragBody.DragPointScale = scale;
     }
 
+    private static DragPointLimb[] MakeArmChain(Transform lower, Meido meido)
+    {
+        var limbDragPointSize = Vector3.one * 0.12f;
+
+        var realLower = CMT.SearchObjName(meido.Body.goSlot[0].obj_tr, lower.name, false);
+
+        var dragPoints = new DragPointLimb[3];
+
+        for (var i = dragPoints.Length - 1; i >= 0; i--)
+        {
+            var joint = lower;
+            var positionJoint = realLower;
+
+            dragPoints[i] = DragPoint.Make<DragPointLimb>(PrimitiveType.Sphere, limbDragPointSize);
+            dragPoints[i].Initialize(meido, () => positionJoint.position, () => Vector3.zero);
+            dragPoints[i].Set(joint);
+            dragPoints[i].AddGizmo();
+            dragPoints[i].Gizmo.SetAlternateTarget(positionJoint);
+
+            lower = lower.parent;
+            realLower = realLower.parent;
+        }
+
+        return dragPoints;
+    }
+
+    private static DragPointFinger[] MakeFingerChain(Transform lower, Meido meido)
+    {
+        var fingerDragPointSize = Vector3.one * 0.01f;
+
+        var dragPoints = new DragPointFinger[3];
+
+        var realLower = CMT.SearchObjName(meido.Body.goSlot[0].obj_tr, lower.parent.name, false);
+
+        for (var i = dragPoints.Length - 1; i >= 0; i--)
+        {
+            var joint = lower;
+            var positionJoint = realLower;
+
+            dragPoints[i] = DragPoint.Make<DragPointFinger>(PrimitiveType.Sphere, fingerDragPointSize);
+            dragPoints[i].Initialize(meido, () => positionJoint.position, () => Vector3.zero);
+            dragPoints[i].Set(joint);
+
+            lower = lower.parent;
+            realLower = realLower.parent;
+        }
+
+        return dragPoints;
+    }
+
     /*
        Somebody smarter than me please help me find a way to do this better T_T
        inb4 for loop.
        */
     private System.Collections.IEnumerator ApplyHipPosition(Vector3 hipPosition)
     {
-        BoneTransform[Bone.Hip].position = hipPosition;
+        boneTransform[Bone.Hip].position = hipPosition;
 
         yield return new WaitForEndOfFrame();
 
-        BoneTransform[Bone.Hip].position = hipPosition;
+        boneTransform[Bone.Hip].position = hipPosition;
 
         yield return new WaitForEndOfFrame();
 
-        BoneTransform[Bone.Hip].position = hipPosition;
+        boneTransform[Bone.Hip].position = hipPosition;
     }
 
     private Quaternion FlipRotation(Vector3 rotation) =>
@@ -452,7 +516,7 @@ public class MeidoDragPointManager
 
     private byte[] SerializeFinger(Bone start, Bone end)
     {
-        var joints = BoneTransform[start].name.Split(' ')[2].StartsWith("Finger") ? 4 : 3;
+        var joints = boneTransform[start].name.Split(' ')[2].StartsWith("Finger") ? 4 : 3;
 
         byte[] buf;
 
@@ -461,7 +525,7 @@ public class MeidoDragPointManager
 
         for (var bone = start; bone <= end; bone += joints)
             for (var i = 0; i < joints - 1; i++)
-                binaryWriter.WriteQuaternion(BoneTransform[bone + i].localRotation);
+                binaryWriter.WriteQuaternion(boneTransform[bone + i].localRotation);
 
         buf = memoryStream.ToArray();
 
@@ -470,7 +534,7 @@ public class MeidoDragPointManager
 
     private void DeserializeFinger(Bone start, Bone end, byte[] fingerBinary, bool mirroring = false)
     {
-        var joints = BoneTransform[start].name.Split(' ')[2].StartsWith("Finger") ? 4 : 3;
+        var joints = boneTransform[start].name.Split(' ')[2].StartsWith("Finger") ? 4 : 3;
         var mirror = mirroring ? -1 : 1;
 
         using var memoryStream = new MemoryStream(fingerBinary);
@@ -478,10 +542,11 @@ public class MeidoDragPointManager
 
         for (var bone = start; bone <= end; bone += joints)
             for (var i = 0; i < joints - 1; i++)
-                BoneTransform[bone + i].localRotation = new(
-                    binaryReader.ReadSingle() * mirror, binaryReader.ReadSingle() * mirror,
-                    binaryReader.ReadSingle(), binaryReader.ReadSingle()
-                );
+                boneTransform[bone + i].localRotation = new(
+                    binaryReader.ReadSingle() * mirror,
+                    binaryReader.ReadSingle() * mirror,
+                    binaryReader.ReadSingle(),
+                    binaryReader.ReadSingle());
     }
 
     private void InitializeDragPoints()
@@ -500,15 +565,13 @@ public class MeidoDragPointManager
 
         dragBody.Initialize(
             () => new(
-                (BoneTransform[Bone.Hip].position.x + BoneTransform[Bone.Spine0a].position.x) / 2f,
-                (BoneTransform[Bone.Spine1].position.y + BoneTransform[Bone.Spine0a].position.y) / 2f,
-                (BoneTransform[Bone.Spine0a].position.z + BoneTransform[Bone.Hip].position.z) / 2f
-            ),
+                (boneTransform[Bone.Hip].position.x + boneTransform[Bone.Spine0a].position.x) / 2f,
+                (boneTransform[Bone.Spine1].position.y + boneTransform[Bone.Spine0a].position.y) / 2f,
+                (boneTransform[Bone.Spine0a].position.z + boneTransform[Bone.Hip].position.z) / 2f),
             () => new(
-                BoneTransform[Bone.Spine0a].eulerAngles.x, BoneTransform[Bone.Spine0a].eulerAngles.y,
-                BoneTransform[Bone.Spine0a].eulerAngles.z + 90f
-            )
-        );
+                boneTransform[Bone.Spine0a].eulerAngles.x,
+                boneTransform[Bone.Spine0a].eulerAngles.y,
+                boneTransform[Bone.Spine0a].eulerAngles.z + 90f));
 
         dragBody.Set(meido.Maid.transform);
         dragBody.Select += OnSelectBody;
@@ -517,102 +580,97 @@ public class MeidoDragPointManager
         // Neck Dragpoint
         var dragNeck = DragPoint.Make<DragPointHead>(PrimitiveType.Sphere, new(0.2f, 0.24f, 0.2f));
 
-        dragNeck.Initialize(meido,
+        dragNeck.Initialize(
+            meido,
             () => new(
-                BoneTransform[Bone.Head].position.x,
-                (BoneTransform[Bone.Head].position.y * 1.2f + BoneTransform[Bone.HeadNub].position.y * 0.8f) / 2f,
-                BoneTransform[Bone.Head].position.z
-            ),
+                boneTransform[Bone.Head].position.x,
+                (boneTransform[Bone.Head].position.y * 1.2f + boneTransform[Bone.HeadNub].position.y * 0.8f) / 2f,
+                boneTransform[Bone.Head].position.z),
             () => new(
-                BoneTransform[Bone.Head].eulerAngles.x, BoneTransform[Bone.Head].eulerAngles.y,
-                BoneTransform[Bone.Head].eulerAngles.z + 90f
-            )
-        );
+                boneTransform[Bone.Head].eulerAngles.x,
+                boneTransform[Bone.Head].eulerAngles.y,
+                boneTransform[Bone.Head].eulerAngles.z + 90f));
 
-        dragNeck.Set(BoneTransform[Bone.Neck]);
+        dragNeck.Set(boneTransform[Bone.Neck]);
         dragNeck.Select += OnSelectFace;
 
-        DragPoints[Bone.Head] = dragNeck;
+        dragPoints[Bone.Head] = dragNeck;
 
         // Head Dragpoint
         var dragHead = DragPoint.Make<DragPointSpine>(PrimitiveType.Sphere, Vector3.one * 0.045f);
 
-        dragHead.Initialize(meido, () => BoneTransform[Bone.Head].position, () => Vector3.zero);
-        dragHead.Set(BoneTransform[Bone.Head]);
+        dragHead.Initialize(meido, () => boneTransform[Bone.Head].position, () => Vector3.zero);
+        dragHead.Set(boneTransform[Bone.Head]);
         dragHead.AddGizmo();
 
-        DragPoints[Bone.HeadNub] = dragHead;
+        dragPoints[Bone.HeadNub] = dragHead;
 
         // Torso Dragpoint
-        var spineTrans1 = BoneTransform[Bone.Spine1];
-        var spineTrans2 = BoneTransform[Bone.Spine1a];
+        var spineTrans1 = boneTransform[Bone.Spine1];
+        var spineTrans2 = boneTransform[Bone.Spine1a];
 
         var dragTorso = DragPoint.Make<DragPointTorso>(PrimitiveType.Capsule, new Vector3(0.2f, 0.19f, 0.24f));
 
         dragTorso.Initialize(
             meido,
             () => new(spineTrans1.position.x, spineTrans2.position.y, spineTrans1.position.z - 0.05f),
-            () => new(spineTrans1.eulerAngles.x, spineTrans1.eulerAngles.y, spineTrans1.eulerAngles.z + 90f)
-        );
+            () => new(spineTrans1.eulerAngles.x, spineTrans1.eulerAngles.y, spineTrans1.eulerAngles.z + 90f));
 
-        dragTorso.Set(BoneTransform[Bone.Spine1a]);
+        dragTorso.Set(boneTransform[Bone.Spine1a]);
 
-        DragPoints[Bone.Torso] = dragTorso;
+        dragPoints[Bone.Torso] = dragTorso;
 
         // Pelvis Dragpoint
-        var pelvisTrans = BoneTransform[Bone.Pelvis];
-        var spineTrans = BoneTransform[Bone.Spine];
+        var pelvisTrans = boneTransform[Bone.Pelvis];
+        var spineTrans = boneTransform[Bone.Spine];
 
         var dragPelvis = DragPoint.Make<DragPointPelvis>(PrimitiveType.Capsule, new(0.2f, 0.15f, 0.24f));
 
         dragPelvis.Initialize(
             meido,
             () => new(
-                pelvisTrans.position.x, (pelvisTrans.position.y + spineTrans.position.y) / 2f,
-                pelvisTrans.position.z
-            ),
-            () => new(pelvisTrans.eulerAngles.x + 90f, pelvisTrans.eulerAngles.y + 90f, pelvisTrans.eulerAngles.z)
-        );
+                pelvisTrans.position.x, (pelvisTrans.position.y + spineTrans.position.y) / 2f, pelvisTrans.position.z),
+            () => new(pelvisTrans.eulerAngles.x + 90f, pelvisTrans.eulerAngles.y + 90f, pelvisTrans.eulerAngles.z));
 
-        dragPelvis.Set(BoneTransform[Bone.Pelvis]);
+        dragPelvis.Set(boneTransform[Bone.Pelvis]);
 
-        DragPoints[Bone.Pelvis] = dragPelvis;
+        dragPoints[Bone.Pelvis] = dragPelvis;
 
         InitializeMuneDragPoint(left: true);
         InitializeMuneDragPoint(left: false);
 
-        var armDragPointL = MakeArmChain(BoneTransform[Bone.HandL], meido);
+        var armDragPointL = MakeArmChain(boneTransform[Bone.HandL], meido);
 
-        DragPoints[Bone.UpperArmL] = armDragPointL[0];
-        DragPoints[Bone.ForearmL] = armDragPointL[1];
-        DragPoints[Bone.HandL] = armDragPointL[2];
+        dragPoints[Bone.UpperArmL] = armDragPointL[0];
+        dragPoints[Bone.ForearmL] = armDragPointL[1];
+        dragPoints[Bone.HandL] = armDragPointL[2];
 
-        var armDragPointR = MakeArmChain(BoneTransform[Bone.HandR], meido);
+        var armDragPointR = MakeArmChain(boneTransform[Bone.HandR], meido);
 
-        DragPoints[Bone.UpperArmR] = armDragPointR[0];
-        DragPoints[Bone.ForearmR] = armDragPointR[1];
-        DragPoints[Bone.HandR] = armDragPointR[2];
+        dragPoints[Bone.UpperArmR] = armDragPointR[0];
+        dragPoints[Bone.ForearmR] = armDragPointR[1];
+        dragPoints[Bone.HandR] = armDragPointR[2];
 
-        var legDragPointL = MakeLegChain(BoneTransform[Bone.FootL]);
+        var legDragPointL = MakeLegChain(boneTransform[Bone.FootL]);
 
-        DragPoints[Bone.CalfL] = legDragPointL[0];
-        DragPoints[Bone.FootL] = legDragPointL[1];
+        dragPoints[Bone.CalfL] = legDragPointL[0];
+        dragPoints[Bone.FootL] = legDragPointL[1];
 
-        var legDragPointR = MakeLegChain(BoneTransform[Bone.FootR]);
+        var legDragPointR = MakeLegChain(boneTransform[Bone.FootR]);
 
-        DragPoints[Bone.CalfR] = legDragPointR[0];
-        DragPoints[Bone.FootR] = legDragPointR[1];
+        dragPoints[Bone.CalfR] = legDragPointR[0];
+        dragPoints[Bone.FootR] = legDragPointR[1];
 
         InitializeSpineDragPoint(SpineBones);
 
         for (var bone = Bone.Finger4NubR; bone >= Bone.Finger0L; bone -= 4)
         {
-            var chain = MakeFingerChain(BoneTransform[bone], meido);
+            var chain = MakeFingerChain(boneTransform[bone], meido);
             var i = 2;
 
             for (var joint = bone - 1; joint > bone - 4; joint--)
             {
-                DragPoints[joint] = chain[i];
+                dragPoints[joint] = chain[i];
                 i--;
             }
         }
@@ -628,12 +686,11 @@ public class MeidoDragPointManager
 
         muneDragPoint.Initialize(
             meido,
-            () => (BoneTransform[mune].position + BoneTransform[sub].position) / 2f,
-            () => Vector3.zero
-        );
+            () => (boneTransform[mune].position + boneTransform[sub].position) / 2f,
+            () => Vector3.zero);
 
-        muneDragPoint.Set(BoneTransform[sub]);
-        DragPoints[mune] = muneDragPoint;
+        muneDragPoint.Set(boneTransform[sub]);
+        dragPoints[mune] = muneDragPoint;
     }
 
     private DragPointLimb[] MakeLegChain(Transform lower)
@@ -666,23 +723,23 @@ public class MeidoDragPointManager
         {
             for (var i = 1; i < joints; i++)
             {
-                var trans = BoneTransform[bone + i];
+                var trans = boneTransform[bone + i];
                 var chain = DragPoint.Make<DragPointFinger>(PrimitiveType.Sphere, fingerDragPointSize);
 
                 chain.Initialize(meido, () => trans.position, () => Vector3.zero);
                 chain.Set(trans);
-                DragPoints[bone + i] = chain;
+                dragPoints[bone + i] = chain;
             }
         }
     }
 
     private void InitializeSpineDragPoint(params Bone[] bones)
     {
-        var spineDragPointSize = DragPointMeido.boneScale;
+        var spineDragPointSize = DragPointMeido.BoneScale;
 
         foreach (var bone in bones)
         {
-            var spine = BoneTransform[bone];
+            var spine = boneTransform[bone];
             var primitive = bone is Bone.Hip ? PrimitiveType.Cube : PrimitiveType.Sphere;
             var dragPoint = DragPoint.Make<DragPointSpine>(primitive, spineDragPointSize);
 
@@ -690,8 +747,8 @@ public class MeidoDragPointManager
 
             dragPoint.Set(spine);
             dragPoint.AddGizmo();
-            DragPoints[bone] = dragPoint;
-            DragPoints[bone].gameObject.SetActive(false);
+            dragPoints[bone] = dragPoint;
+            dragPoints[bone].gameObject.SetActive(false);
         }
     }
 
@@ -699,7 +756,7 @@ public class MeidoDragPointManager
         dragCube.gameObject.SetActive(CubeActive);
 
     private void OnCubeSmall(object sender, EventArgs args) =>
-        dragCube.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
+        dragCube.DragPointScale = CubeSmall ? DragPointGeneral.SmallCube : 1f;
 
     private void OnSetDragPointScale(object sender, EventArgs args) =>
         SetDragPointScale(meido.Maid.transform.localScale.x);
@@ -715,7 +772,7 @@ public class MeidoDragPointManager
         // TODO: Move to external file somehow
         var transform = meido.Body.m_Bones.transform;
 
-        BoneTransform = new()
+        boneTransform = new()
         {
             [Bone.Head] = CMT.SearchObjName(transform, "Bip01 Head"),
             [Bone.Neck] = CMT.SearchObjName(transform, "Bip01 Neck"),
@@ -809,29 +866,3 @@ public class MeidoDragPointManager
         };
     }
 }
-
-public readonly struct AttachPointInfo
-{
-    private static readonly AttachPointInfo empty = new(AttachPoint.None, string.Empty, -1);
-
-    public AttachPoint AttachPoint { get; }
-    public string MaidGuid { get; }
-    public int MaidIndex { get; }
-
-    public static ref readonly AttachPointInfo Empty =>
-        ref empty;
-
-    public AttachPointInfo(AttachPoint attachPoint, Meido meido)
-    {
-        AttachPoint = attachPoint;
-        MaidGuid = meido.Maid.status.guid;
-        MaidIndex = meido.Slot;
-    }
-
-    public AttachPointInfo(AttachPoint attachPoint, string maidGuid, int maidIndex)
-    {
-        AttachPoint = attachPoint;
-        MaidGuid = maidGuid;
-        MaidIndex = maidIndex;
-    }
-}

+ 23 - 0
src/MeidoPhotoStudio.Plugin/Meido/PoseInfo.cs

@@ -0,0 +1,23 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public readonly struct PoseInfo
+{
+    private static readonly PoseInfo DefaultPoseValue =
+        new(Constants.PoseGroupList[0], Constants.PoseDict[Constants.PoseGroupList[0]][0]);
+
+    public PoseInfo(string poseGroup, string pose, bool customPose = false)
+    {
+        PoseGroup = poseGroup;
+        Pose = pose;
+        CustomPose = customPose;
+    }
+
+    public static ref readonly PoseInfo DefaultPose =>
+        ref DefaultPoseValue;
+
+    public string PoseGroup { get; }
+
+    public string Pose { get; }
+
+    public bool CustomPose { get; }
+}

+ 40 - 42
src/MeidoPhotoStudio.Plugin/MeidoPhotoStudio.cs

@@ -2,36 +2,32 @@ using System;
 using System.Collections;
 using System.IO;
 using System.Text;
+
 using BepInEx;
 using UnityEngine;
 using UnityEngine.SceneManagement;
+
 using Input = MeidoPhotoStudio.Plugin.InputManager;
 
 namespace MeidoPhotoStudio.Plugin;
 
-[BepInPlugin(pluginGuid, pluginName, pluginVersion)]
+[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
 [BepInDependency("org.bepinex.plugins.unityinjectorloader", BepInDependency.DependencyFlags.SoftDependency)]
 public class MeidoPhotoStudio : BaseUnityPlugin
 {
-    public const string pluginName = "MeidoPhotoStudio";
-    public const string pluginVersion = "1.0.0";
-    public const string pluginSubVersion = "beta.4.1";
-    public const short sceneVersion = 2;
-    public const int kankyoMagic = -765;
-
-    private const string pluginGuid = "com.habeebweeb.com3d2.meidophotostudio";
+    public const string PluginName = "MeidoPhotoStudio";
+    public const string PluginVersion = "1.0.0";
+    public const string PluginSubVersion = "beta.4.1";
+    public const short SceneVersion = 2;
+    public const int KankyoMagic = -765;
 
     public static readonly byte[] SceneHeader = Encoding.UTF8.GetBytes("MPSSCENE");
-    public static readonly string pluginString = $"{pluginName} {pluginVersion}";
+    public static readonly string PluginString = $"{PluginName} {PluginVersion}";
 
-    public static event EventHandler<ScreenshotEventArgs> NotifyRawScreenshot;
+    private const string PluginGuid = "com.habeebweeb.com3d2.meidophotostudio";
 
-    private static event EventHandler<ScreenshotEventArgs> ScreenshotEvent;
     private static Constants.Scene currentScene;
 
-    public static bool EditMode =>
-        currentScene is Constants.Scene.Edit;
-
     private HarmonyLib.Harmony harmony;
     private WindowManager windowManager;
     private SceneManager sceneManager;
@@ -51,10 +47,17 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         Input.Register(MpsKey.Screenshot, KeyCode.S, "Take screenshot");
         Input.Register(MpsKey.Activate, KeyCode.F6, "Activate/deactivate MeidoPhotoStudio");
 
-        if (!string.IsNullOrEmpty(pluginSubVersion))
-            pluginString += $"-{pluginSubVersion}";
+        if (!string.IsNullOrEmpty(PluginSubVersion))
+            PluginString += $"-{PluginSubVersion}";
     }
 
+    public static event EventHandler<ScreenshotEventArgs> NotifyRawScreenshot;
+
+    private static event EventHandler<ScreenshotEventArgs> ScreenshotEvent;
+
+    public static bool EditMode =>
+        currentScene is Constants.Scene.Edit;
+
     public static void TakeScreenshot(ScreenshotEventArgs args) =>
         ScreenshotEvent?.Invoke(null, args);
 
@@ -75,9 +78,9 @@ public class MeidoPhotoStudio : BaseUnityPlugin
 
             new SceneMetadata
             {
-                Version = sceneVersion,
+                Version = SceneVersion,
                 Environment = environment,
-                MaidCount = environment ? kankyoMagic : meidoManager.ActiveMeidoList.Count,
+                MaidCount = environment ? KankyoMagic : meidoManager.ActiveMeidoList.Count,
                 MMConverted = false,
             }.WriteMetadata(headerWriter);
 
@@ -133,10 +136,10 @@ public class MeidoPhotoStudio : BaseUnityPlugin
 
         var metadata = SceneMetadata.ReadMetadata(headerReader);
 
-        if (metadata.Version > sceneVersion)
+        if (metadata.Version > SceneVersion)
         {
             Utility.LogWarning("Cannot load scene. Scene is too new.");
-            Utility.LogWarning($"Your version: {sceneVersion}, Scene version: {metadata.Version}");
+            Utility.LogWarning($"Your version: {SceneVersion}, Scene version: {metadata.Version}");
 
             return;
         }
@@ -153,32 +156,32 @@ public class MeidoPhotoStudio : BaseUnityPlugin
             {
                 switch (header)
                 {
-                    case MeidoManager.header:
+                    case MeidoManager.Header:
                         Serialization.Get<MeidoManager>().Deserialize(meidoManager, dataReader, metadata);
 
                         break;
-                    case MessageWindowManager.header:
+                    case MessageWindowManager.Header:
                         Serialization.Get<MessageWindowManager>()
                             .Deserialize(messageWindowManager, dataReader, metadata);
 
                         break;
-                    case CameraManager.header:
+                    case CameraManager.Header:
                         Serialization.Get<CameraManager>().Deserialize(cameraManager, dataReader, metadata);
 
                         break;
-                    case LightManager.header:
+                    case LightManager.Header:
                         Serialization.Get<LightManager>().Deserialize(lightManager, dataReader, metadata);
 
                         break;
-                    case EffectManager.header:
+                    case EffectManager.Header:
                         Serialization.Get<EffectManager>().Deserialize(effectManager, dataReader, metadata);
 
                         break;
-                    case EnvironmentManager.header:
+                    case EnvironmentManager.Header:
                         Serialization.Get<EnvironmentManager>().Deserialize(environmentManager, dataReader, metadata);
 
                         break;
-                    case PropManager.header:
+                    case PropManager.Header:
                         Serialization.Get<PropManager>().Deserialize(propManager, dataReader, metadata);
 
                         break;
@@ -193,8 +196,7 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         {
             Utility.LogError(
                 $"Failed to deserialize scene because {e.Message}\nCurrent header: '{header}'. " +
-                $"Last header: '{previousHeader}'"
-            );
+                $"Last header: '{previousHeader}'");
 
             Utility.LogError(e.StackTrace);
         }
@@ -422,7 +424,7 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         effectManager.AddManager<DepthOfFieldEffectManager>();
         effectManager.AddManager<FogEffectManager>();
         effectManager.AddManager<VignetteEffectManager>();
-        effectManager.AddManager<SepiaToneEffectManger>();
+        effectManager.AddManager<SepiaToneEffectManager>();
         effectManager.AddManager<BlurEffectManager>();
 
         meidoManager.BeginCallMeidos += (_, _) =>
@@ -457,7 +459,9 @@ public class MeidoPhotoStudio : BaseUnityPlugin
             return;
 
         if (!initialized)
+        {
             Initialize();
+        }
         else
         {
             meidoManager.Activate();
@@ -474,7 +478,9 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         active = true;
 
         if (EditMode)
+        {
             meidoManager.CallMeidos();
+        }
         else
         {
             // TODO: Rework this to not use null propagation (UNT008)
@@ -506,9 +512,10 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         }
 
         sysDialog.Show(
-            string.Format(Translation.Get("systemMessage", "exitConfirm"), pluginName), SystemDialog.TYPE.OK_CANCEL,
-            Exit, Resume
-        );
+            string.Format(Translation.Get("systemMessage", "exitConfirm"), PluginName),
+            SystemDialog.TYPE.OK_CANCEL,
+            Exit,
+            Resume);
 
         void Resume()
         {
@@ -547,12 +554,3 @@ public class MeidoPhotoStudio : BaseUnityPlugin
         }
     }
 }
-
-public class ScreenshotEventArgs : EventArgs
-{
-    public string Path { get; set; } = string.Empty;
-    public int SuperSize { get; set; } = -1;
-    public bool HideMaids { get; set; }
-    public bool InMemory { get; set; } = false;
-    public Texture2D Screenshot { get; set; }
-}

+ 14 - 14
src/MeidoPhotoStudio.Plugin/MenuFileCache.cs

@@ -9,14 +9,22 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class MenuFileCache
 {
-    private const int cacheVersion = 765;
+    public static readonly string CachePath = Path.Combine(Constants.ConfigPath, "cache.dat");
 
-    public static readonly string cachePath = Path.Combine(Constants.configPath, "cache.dat");
+    private const int CacheVersion = 765;
 
     private readonly Dictionary<string, ModItem> modItems;
 
     private bool rebuild;
 
+    public MenuFileCache()
+    {
+        modItems = new();
+
+        if (File.Exists(CachePath))
+            Deserialize();
+    }
+
     public ModItem this[string menu]
     {
         get => modItems[menu];
@@ -30,14 +38,6 @@ public class MenuFileCache
         }
     }
 
-    public MenuFileCache()
-    {
-        modItems = new();
-
-        if (File.Exists(cachePath))
-            Deserialize();
-    }
-
     public bool Has(string menuFileName) =>
         modItems.ContainsKey(menuFileName);
 
@@ -46,9 +46,9 @@ public class MenuFileCache
         if (!rebuild)
             return;
 
-        using var binaryWriter = new BinaryWriter(File.OpenWrite(cachePath));
+        using var binaryWriter = new BinaryWriter(File.OpenWrite(CachePath));
 
-        binaryWriter.Write(cacheVersion);
+        binaryWriter.Write(CacheVersion);
 
         foreach (var item in modItems.Values)
             item.Serialize(binaryWriter);
@@ -56,9 +56,9 @@ public class MenuFileCache
 
     private void Deserialize()
     {
-        using var binaryReader = new BinaryReader(File.OpenRead(cachePath));
+        using var binaryReader = new BinaryReader(File.OpenRead(CachePath));
 
-        if (binaryReader.ReadInt32() is not cacheVersion)
+        if (binaryReader.ReadInt32() is not CacheVersion)
         {
             Utility.LogInfo("Cache version out of date. Rebuilding");
 

+ 20 - 12
src/MeidoPhotoStudio.Plugin/MenuFileUtility.cs

@@ -4,34 +4,37 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public static class MenuFileUtility
 {
-    public const string noCategory = "noCategory";
+    // TODO: What's this for?
+    public const string NoCategory = "noCategory";
 
     public static readonly string[] MenuCategories =
     {
-        noCategory, "acchat", "headset", "wear", "skirt", "onepiece", "mizugi", "bra", "panz", "stkg", "shoes",
+        NoCategory, "acchat", "headset", "wear", "skirt", "onepiece", "mizugi", "bra", "panz", "stkg", "shoes",
         "acckami", "megane", "acchead", "acchana", "accmimi", "glove", "acckubi", "acckubiwa", "acckamisub", "accnip",
         "accude", "accheso", "accashi", "accsenaka", "accshippo", "accxxx",
     };
 
-    private static readonly HashSet<string> accMpn = new(StringComparer.InvariantCultureIgnoreCase);
-
-    public static event EventHandler MenuFilesReadyChange;
-    public static bool MenuFilesReady { get; private set; }
+    private static readonly HashSet<string> AccMpn = new(StringComparer.InvariantCultureIgnoreCase);
 
     private static byte[] fileBuffer;
 
     static MenuFileUtility()
     {
-        accMpn.UnionWith(MenuCategories.Skip(1));
+        AccMpn.UnionWith(MenuCategories.Skip(1));
         GameMain.Instance.StartCoroutine(CheckMenuDataBaseJob());
     }
 
+    public static event EventHandler MenuFilesReadyChange;
+
+    public static bool MenuFilesReady { get; private set; }
+
     public static byte[] ReadAFileBase(string filename)
     {
         using var aFileBase = GameUty.FileOpen(filename);
@@ -80,7 +83,7 @@ public static class MenuFileUtility
 
         modItem.Category = menuDataBase.GetCategoryMpnText();
 
-        if (!accMpn.Contains(modItem.Category))
+        if (!AccMpn.Contains(modItem.Category))
             return false;
 
         modItem.MenuFile = menuDataBase.GetMenuFileName().ToLower();
@@ -151,7 +154,7 @@ public static class MenuFileUtility
                 {
                     modItem.Category = menuProps[1];
 
-                    if (!accMpn.Contains(modItem.Category))
+                    if (!AccMpn.Contains(modItem.Category))
                         return;
                 }
                 else if (header is "icons" or "icon")
@@ -161,7 +164,9 @@ public static class MenuFileUtility
                     break;
                 }
                 else if (header is "priority")
+                {
                     modItem.Priority = float.Parse(menuProps[1]);
+                }
             }
         }
         catch (Exception e)
@@ -204,7 +209,7 @@ public static class MenuFileUtility
             modItem.Name = binaryReader.ReadString();
             modItem.Category = binaryReader.ReadString();
 
-            if (!accMpn.Contains(modItem.Category))
+            if (!AccMpn.Contains(modItem.Category))
                 return false;
 
             binaryReader.ReadString();
@@ -216,7 +221,10 @@ public static class MenuFileUtility
             {
                 mpn = (MPN)Enum.Parse(typeof(MPN), mpnValue, true);
             }
-            catch { /* ignored */ }
+            catch
+            {
+                // Ignored.
+            }
 
             if (mpn is not MPN.null_mpn)
                 binaryReader.ReadString();
@@ -253,7 +261,7 @@ public static class MenuFileUtility
     }
 
     public static bool ValidBG2MenuFile(ModItem modItem) =>
-        accMpn.Contains(modItem.Category) && ValidBG2MenuFile(modItem.MenuFile);
+        AccMpn.Contains(modItem.Category) && ValidBG2MenuFile(modItem.MenuFile);
 
     public static bool ValidBG2MenuFile(string menu)
     {

+ 18 - 0
src/MeidoPhotoStudio.Plugin/MenuFilesEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class MenuFilesEventArgs : EventArgs
+{
+    public MenuFilesEventArgs(EventType type) =>
+        Type = type;
+
+    public enum EventType
+    {
+        HandItems,
+        MenuFiles,
+        MpnAttach,
+    }
+
+    public EventType Type { get; }
+}

+ 1 - 74
src/MeidoPhotoStudio.Plugin/MenuItem.cs

@@ -1,5 +1,3 @@
-using System.Globalization;
-using System.IO;
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -7,77 +5,6 @@ namespace MeidoPhotoStudio.Plugin;
 public abstract class MenuItem
 {
     public string IconFile { get; set; }
-    public Texture2D Icon { get; set; }
-}
-
-public class ModItem : MenuItem
-{
-    public string MenuFile { get; set; }
-    public string BaseMenuFile { get; set; }
-    public string Name { get; set; }
-    public string Category { get; set; }
-    public float Priority { get; set; }
-    public bool IsMod { get; private set; }
-    public bool IsOfficialMod { get; private set; }
-
-    public static ModItem OfficialMod(string menuFile) =>
-        new()
-        {
-            MenuFile = menuFile,
-            IsMod = true,
-            IsOfficialMod = true,
-            Priority = 1000f,
-        };
-
-    public static ModItem Mod(string menuFile) =>
-        new()
-        {
-            MenuFile = menuFile,
-            IsMod = true,
-        };
-
-    public static ModItem Deserialize(BinaryReader binaryReader) =>
-        new()
-        {
-            MenuFile = binaryReader.ReadNullableString(),
-            BaseMenuFile = binaryReader.ReadNullableString(),
-            IconFile = binaryReader.ReadNullableString(),
-            Name = binaryReader.ReadNullableString(),
-            Category = binaryReader.ReadNullableString(),
-            Priority = float.Parse(binaryReader.ReadNullableString()),
-            IsMod = binaryReader.ReadBoolean(),
-            IsOfficialMod = binaryReader.ReadBoolean(),
-        };
-
-    public ModItem() { }
 
-    public ModItem(string menuFile) =>
-        MenuFile = menuFile;
-
-    public override string ToString() =>
-        IsOfficialMod ? $"{Path.GetFileName(MenuFile)}#{BaseMenuFile}" : MenuFile;
-
-    public void Serialize(BinaryWriter binaryWriter)
-    {
-        if (IsOfficialMod)
-            return;
-
-        binaryWriter.WriteNullableString(MenuFile);
-        binaryWriter.WriteNullableString(BaseMenuFile);
-        binaryWriter.WriteNullableString(IconFile);
-        binaryWriter.WriteNullableString(Name);
-        binaryWriter.WriteNullableString(Category);
-        binaryWriter.WriteNullableString(Priority.ToString(CultureInfo.InvariantCulture));
-        binaryWriter.Write(IsMod);
-        binaryWriter.Write(IsOfficialMod);
-    }
-}
-
-public class MyRoomItem : MenuItem
-{
-    public int ID { get; set; }
-    public string PrefabName { get; set; }
-
-    public override string ToString() =>
-        $"MYR_{ID}#{PrefabName}";
+    public Texture2D Icon { get; set; }
 }

+ 75 - 0
src/MeidoPhotoStudio.Plugin/ModItem.cs

@@ -0,0 +1,75 @@
+using System.Globalization;
+using System.IO;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class ModItem : MenuItem
+{
+    public ModItem()
+    {
+    }
+
+    public ModItem(string menuFile) =>
+        MenuFile = menuFile;
+
+    public string MenuFile { get; set; }
+
+    public string BaseMenuFile { get; set; }
+
+    public string Name { get; set; }
+
+    public string Category { get; set; }
+
+    public float Priority { get; set; }
+
+    public bool IsMod { get; private set; }
+
+    public bool IsOfficialMod { get; private set; }
+
+    public static ModItem OfficialMod(string menuFile) =>
+        new()
+        {
+            MenuFile = menuFile,
+            IsMod = true,
+            IsOfficialMod = true,
+            Priority = 1000f,
+        };
+
+    public static ModItem Mod(string menuFile) =>
+        new()
+        {
+            MenuFile = menuFile,
+            IsMod = true,
+        };
+
+    public static ModItem Deserialize(BinaryReader binaryReader) =>
+        new()
+        {
+            MenuFile = binaryReader.ReadNullableString(),
+            BaseMenuFile = binaryReader.ReadNullableString(),
+            IconFile = binaryReader.ReadNullableString(),
+            Name = binaryReader.ReadNullableString(),
+            Category = binaryReader.ReadNullableString(),
+            Priority = float.Parse(binaryReader.ReadNullableString()),
+            IsMod = binaryReader.ReadBoolean(),
+            IsOfficialMod = binaryReader.ReadBoolean(),
+        };
+
+    public override string ToString() =>
+        IsOfficialMod ? $"{Path.GetFileName(MenuFile)}#{BaseMenuFile}" : MenuFile;
+
+    public void Serialize(BinaryWriter binaryWriter)
+    {
+        if (IsOfficialMod)
+            return;
+
+        binaryWriter.WriteNullableString(MenuFile);
+        binaryWriter.WriteNullableString(BaseMenuFile);
+        binaryWriter.WriteNullableString(IconFile);
+        binaryWriter.WriteNullableString(Name);
+        binaryWriter.WriteNullableString(Category);
+        binaryWriter.WriteNullableString(Priority.ToString(CultureInfo.InvariantCulture));
+        binaryWriter.Write(IsMod);
+        binaryWriter.Write(IsOfficialMod);
+    }
+}

+ 41 - 20
src/MeidoPhotoStudio.Plugin/ModelUtility.cs

@@ -3,19 +3,27 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
+
 using UnityEngine;
 using UnityEngine.Rendering;
+
 using static MeidoPhotoStudio.Plugin.MenuFileUtility;
+
 using Object = UnityEngine.Object;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public static class ModelUtility
 {
-    private enum IMode { None, ItemChange, TexChange }
-
     private static GameObject deploymentObject;
 
+    private enum IMode
+    {
+        None,
+        ItemChange,
+        TexChange,
+    }
+
     public static GameObject LoadMyRoomModel(MyRoomItem item)
     {
         var data = MyRoomCustom.PlacementData.GetData(item.ID);
@@ -137,8 +145,7 @@ public static class ModelUtility
             foreach (var renderer in renderers)
                 if (matChange.MaterialIndex < renderer.materials.Length)
                     renderer.materials[matChange.MaterialIndex] = ImportCM.LoadMaterial(
-                        matChange.MaterialFile, null, renderer.materials[matChange.MaterialIndex]
-                    );
+                        matChange.MaterialFile, null, renderer.materials[matChange.MaterialIndex]);
 
         if (!modItem.IsOfficialMod)
             return finalModel;
@@ -150,8 +157,7 @@ public static class ModelUtility
         catch (Exception e)
         {
             Utility.LogError(
-                $"Could not read mod menu file '{modItem.MenuFile}' because {e.Message}\n{e.StackTrace}"
-            );
+                $"Could not read mod menu file '{modItem.MenuFile}' because {e.Message}\n{e.StackTrace}");
 
             return null;
         }
@@ -287,7 +293,9 @@ public static class ModelUtility
                 var boneName = binaryReader.ReadString();
 
                 if (!boneDict.ContainsKey(boneName))
+                {
                     Debug.LogError("nullbone= " + boneName);
+                }
                 else
                 {
                     var keyName = boneName + "$_SCL_";
@@ -429,7 +437,9 @@ public static class ModelUtility
                 var menuProps = UTY.GetStringList(menuPropString);
 
                 if (header is "end")
+                {
                     break;
+                }
                 else if (header is "マテリアル変更")
                 {
                     var matNo = int.Parse(menuProps[2]);
@@ -438,7 +448,9 @@ public static class ModelUtility
                     modelInfo.MaterialChanges.Add(new MaterialChange(matNo, materialFile));
                 }
                 else if (header is "additem")
+                {
                     modelInfo.ModelFile = menuProps[1];
+                }
             }
         }
         catch
@@ -473,7 +485,10 @@ public static class ModelUtility
             {
                 mpn = (MPN)Enum.Parse(typeof(MPN), serializedMpn, true);
             }
-            catch { /* ignored */ }
+            catch
+            {
+                // Ignored.
+            }
 
             if (mpn is not MPN.null_mpn)
                 binaryReader.ReadString();
@@ -543,10 +558,10 @@ public static class ModelUtility
                     material.SetColor(
                         data[1],
                         new(
-                            float.Parse(data[2]) / 255f, float.Parse(data[3]) / 255f, float.Parse(data[4]) / 255f,
-                            float.Parse(data[5]) / 255f
-                        )
-                    );
+                            float.Parse(data[2]) / 255f,
+                            float.Parse(data[3]) / 255f,
+                            float.Parse(data[4]) / 255f,
+                            float.Parse(data[5]) / 255f));
                 else if (header is "数値設定")
                     material.SetFloat(data[1], float.Parse(data[2]));
             }
@@ -557,9 +572,13 @@ public static class ModelUtility
                 ChangeTex(matno, data[3], data[4].ToLower());
             }
             else if (mode is IMode.None)
+            {
                 continue;
+            }
             else
+            {
                 throw new ArgumentOutOfRangeException(nameof(mode));
+            }
         }
 
         void ChangeTex(int matno, string prop, string filename)
@@ -581,21 +600,23 @@ public static class ModelUtility
         }
     }
 
-    private class ModelInfo
-    {
-        public List<MaterialChange> MaterialChanges { get; } = new();
-        public string ModelFile { get; set; }
-    }
-
     private readonly struct MaterialChange
     {
-        public int MaterialIndex { get; }
-        public string MaterialFile { get; }
-
         public MaterialChange(int materialIndex, string materialFile)
         {
             MaterialIndex = materialIndex;
             MaterialFile = materialFile;
         }
+
+        public int MaterialIndex { get; }
+
+        public string MaterialFile { get; }
+    }
+
+    private class ModelInfo
+    {
+        public List<MaterialChange> MaterialChanges { get; } = new();
+
+        public string ModelFile { get; set; }
     }
 }

+ 31 - 0
src/MeidoPhotoStudio.Plugin/MousePosition.cs

@@ -0,0 +1,31 @@
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class MousePosition : MonoBehaviour
+{
+    private Vector3 mousePosition;
+
+    public Vector3 Position =>
+        mousePosition;
+
+    private void Awake()
+    {
+        DontDestroyOnLoad(this);
+
+        mousePosition = Input.mousePosition;
+    }
+
+    private void Update()
+    {
+        if (Input.GetMouseButton(0))
+        {
+            mousePosition.x += Input.GetAxis("Mouse X") * 20;
+            mousePosition.y += Input.GetAxis("Mouse Y") * 20;
+        }
+        else
+        {
+            mousePosition = Input.mousePosition;
+        }
+    }
+}

+ 14 - 0
src/MeidoPhotoStudio.Plugin/MpnAttachProp.cs

@@ -0,0 +1,14 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public readonly struct MpnAttachProp
+{
+    public MpnAttachProp(MPN tag, string menuFile)
+    {
+        Tag = tag;
+        MenuFile = menuFile;
+    }
+
+    public MPN Tag { get; }
+
+    public string MenuFile { get; }
+}

+ 16 - 16
src/MeidoPhotoStudio.Plugin/MyGui.cs

@@ -5,8 +5,8 @@ namespace MeidoPhotoStudio.Plugin;
 public static class MpsGui
 {
     public static readonly GUILayoutOption HalfSlider = GUILayout.Width(98);
-    public static readonly Texture2D white = Utility.MakeTex(2, 2, Color.white);
-    public static readonly Texture2D transparentBlack = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.8f));
+    public static readonly Texture2D White = Utility.MakeTex(2, 2, Color.white);
+    public static readonly Texture2D TransparentBlack = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.8f));
     public static readonly GUIStyle SliderLabelStyle;
     public static readonly GUIStyle SliderStyle;
     public static readonly GUIStyle SliderStyleNoLabel;
@@ -14,36 +14,36 @@ public static class MpsGui
     public static readonly GUIStyle SliderThumbStyle;
     public static readonly GUIStyle SliderResetButtonStyle;
 
-    private static readonly GUIStyle lineStyleWhite;
-    private static readonly GUIStyle lineStyleBlack;
-    private static readonly GUIStyle textureBoxStyle;
-    private static readonly GUIStyle headerLabelStyle;
+    private static readonly GUIStyle LineStyleWhite;
+    private static readonly GUIStyle LineStyleBlack;
+    private static readonly GUIStyle TextureBoxStyle;
+    private static readonly GUIStyle HeaderLabelStyle;
 
     static MpsGui()
     {
         GUI.skin = null;
 
-        lineStyleWhite = new(GUI.skin.box)
+        LineStyleWhite = new(GUI.skin.box)
         {
             margin = new(0, 0, 8, 8),
             normal = { background = Utility.MakeTex(2, 2, new(1f, 1f, 1f, 0.2f)) },
         };
 
-        lineStyleWhite.padding = lineStyleWhite.border = new(0, 0, 1, 1);
+        LineStyleWhite.padding = LineStyleWhite.border = new(0, 0, 1, 1);
 
-        lineStyleBlack = new(lineStyleWhite)
+        LineStyleBlack = new(LineStyleWhite)
         {
             normal = { background = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0.3f)) },
         };
 
-        textureBoxStyle = new(GUI.skin.box)
+        TextureBoxStyle = new(GUI.skin.box)
         {
             normal = { background = Utility.MakeTex(2, 2, new(0f, 0f, 0f, 0f)) },
         };
 
-        textureBoxStyle.padding = textureBoxStyle.margin = new(0, 0, 0, 0);
+        TextureBoxStyle.padding = TextureBoxStyle.margin = new(0, 0, 0, 0);
 
-        headerLabelStyle = new(GUI.skin.label)
+        HeaderLabelStyle = new(GUI.skin.label)
         {
             padding = new(7, 0, 0, -5),
             normal = { textColor = Color.white },
@@ -78,19 +78,19 @@ public static class MpsGui
     }
 
     public static void WhiteLine() =>
-        Line(lineStyleWhite);
+        Line(LineStyleWhite);
 
     public static void BlackLine() =>
-        Line(lineStyleBlack);
+        Line(LineStyleBlack);
 
     public static void DrawTexture(Texture texture, params GUILayoutOption[] layoutOptions) =>
-        GUILayout.Box(texture, textureBoxStyle, layoutOptions);
+        GUILayout.Box(texture, TextureBoxStyle, layoutOptions);
 
     public static int ClampFont(int size, int min, int max) =>
         Mathf.Clamp(Utility.GetPix(size), min, max);
 
     public static void Header(string text, params GUILayoutOption[] layoutOptions) =>
-        GUILayout.Label(text, headerLabelStyle, layoutOptions);
+        GUILayout.Label(text, HeaderLabelStyle, layoutOptions);
 
     private static void Line(GUIStyle style) =>
         GUILayout.Box(GUIContent.none, style, GUILayout.Height(1));

+ 11 - 0
src/MeidoPhotoStudio.Plugin/MyRoomItem.cs

@@ -0,0 +1,11 @@
+namespace MeidoPhotoStudio.Plugin;
+
+public class MyRoomItem : MenuItem
+{
+    public int ID { get; set; }
+
+    public string PrefabName { get; set; }
+
+    public override string ToString() =>
+        $"MYR_{ID}#{PrefabName}";
+}

+ 4 - 9
src/MeidoPhotoStudio.Plugin/Patchers/AllProcPropSeqStartPatcher.cs

@@ -1,8 +1,10 @@
 using System;
+using System.Diagnostics.CodeAnalysis;
+
 using HarmonyLib;
 
 namespace MeidoPhotoStudio.Plugin;
-//
+
 // TODO: Extend this further to potentially reduce the need for coroutines that wait for maid proc state
 public static class AllProcPropSeqStartPatcher
 {
@@ -10,14 +12,7 @@ public static class AllProcPropSeqStartPatcher
 
     [HarmonyPatch(typeof(Maid), nameof(Maid.AllProcPropSeqStart))]
     [HarmonyPostfix]
+    [SuppressMessage("StyleCop.Analyzers.NamingRules", "SA1313", Justification = "Harmony parameter")]
     private static void NotifyProcStart(Maid __instance) =>
         SequenceStart?.Invoke(null, new(__instance));
 }
-
-public class ProcStartEventArgs : EventArgs
-{
-    public readonly Maid maid;
-
-    public ProcStartEventArgs(Maid maid) =>
-        this.maid = maid;
-}

+ 2 - 0
src/MeidoPhotoStudio.Plugin/Patchers/BgMgrPatcher.cs

@@ -1,4 +1,5 @@
 using System;
+
 using HarmonyLib;
 
 namespace MeidoPhotoStudio.Plugin;
@@ -6,6 +7,7 @@ namespace MeidoPhotoStudio.Plugin;
 public static class BgMgrPatcher
 {
     public static event EventHandler ChangeBgBegin;
+
     public static event EventHandler ChangeBgEnd;
 
     [HarmonyPatch(typeof(BgMgr), nameof(BgMgr.ChangeBg))]

+ 11 - 0
src/MeidoPhotoStudio.Plugin/Patchers/ProcStartEventArgs.cs

@@ -0,0 +1,11 @@
+using System;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class ProcStartEventArgs : EventArgs
+{
+    public readonly Maid Maid;
+
+    public ProcStartEventArgs(Maid maid) =>
+        Maid = maid;
+}

+ 18 - 0
src/MeidoPhotoStudio.Plugin/PresetChangeEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class PresetChangeEventArgs : EventArgs
+{
+    public PresetChangeEventArgs(string path, string category)
+    {
+        Path = path;
+        Category = category;
+    }
+
+    public static new PresetChangeEventArgs Empty { get; } = new(string.Empty, string.Empty);
+
+    public string Category { get; }
+
+    public string Path { get; }
+}

+ 18 - 0
src/MeidoPhotoStudio.Plugin/ScreenshotEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class ScreenshotEventArgs : EventArgs
+{
+    public string Path { get; set; } = string.Empty;
+
+    public int SuperSize { get; set; } = -1;
+
+    public bool HideMaids { get; set; }
+
+    public bool InMemory { get; set; } = false;
+
+    public Texture2D Screenshot { get; set; }
+}

+ 1 - 0
src/MeidoPhotoStudio.Plugin/Serialization/ISerializer.cs

@@ -5,5 +5,6 @@ namespace MeidoPhotoStudio.Plugin;
 public interface ISerializer
 {
     void Serialize(object thing, BinaryWriter writer);
+
     void Deserialize(object thing, BinaryReader reader, SceneMetadata metadata);
 }

+ 1 - 0
src/MeidoPhotoStudio.Plugin/Serialization/ISimpleSerializer.cs

@@ -5,5 +5,6 @@ namespace MeidoPhotoStudio.Plugin;
 public interface ISimpleSerializer
 {
     void Serialize(object obj, BinaryWriter writer);
+
     object Deserialize(BinaryReader reader, SceneMetadata metadata);
 }

+ 4 - 1
src/MeidoPhotoStudio.Plugin/Serialization/SceneMetadata.cs

@@ -5,8 +5,11 @@ namespace MeidoPhotoStudio.Plugin;
 public class SceneMetadata
 {
     public short Version { get; set; }
+
     public bool Environment { get; set; }
+
     public int MaidCount { get; set; }
+
     public bool MMConverted { get; set; }
 
     public static SceneMetadata ReadMetadata(BinaryReader reader) =>
@@ -15,7 +18,7 @@ public class SceneMetadata
             Version = reader.ReadVersion(),
             Environment = reader.ReadBoolean(),
             MaidCount = reader.ReadInt32(),
-            MMConverted = reader.ReadBoolean()
+            MMConverted = reader.ReadBoolean(),
         };
 
     public void WriteMetadata(BinaryWriter writer)

+ 2 - 2
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/AttachPointInfoSerializer.cs

@@ -4,11 +4,11 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class AttachPointInfoSerializer : SimpleSerializer<AttachPointInfo>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(AttachPointInfo info, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write((int)info.AttachPoint);
         writer.Write(info.MaidIndex);

+ 2 - 2
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/CameraInfoSerializer.cs

@@ -4,11 +4,11 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class CameraInfoSerializer : Serializer<CameraInfo>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(CameraInfo info, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write(info.TargetPos);
         writer.Write(info.Angle);

+ 5 - 5
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/DragPointLightSerializer.cs

@@ -4,17 +4,14 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointLightSerializer : Serializer<DragPointLight>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static Serializer<LightProperty> LightPropertySerializer =>
         Serialization.Get<LightProperty>();
 
-    private static LightProperty[] GetLightProperties(DragPointLight light) =>
-        Utility.GetFieldValue<DragPointLight, LightProperty[]>(light, "LightProperties");
-
     public override void Serialize(DragPointLight light, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         var lightList = GetLightProperties(light);
 
@@ -41,4 +38,7 @@ public class DragPointLightSerializer : Serializer<DragPointLight>
         light.IsColourMode = reader.ReadBoolean();
         light.IsDisabled = reader.ReadBoolean();
     }
+
+    private static LightProperty[] GetLightProperties(DragPointLight light) =>
+        Utility.GetFieldValue<DragPointLight, LightProperty[]>(light, "LightProperties");
 }

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/BloomEffectSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class BloomEffectSerializer : Serializer<BloomEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(BloomEffectManager effect, BinaryWriter writer)
     {
-        writer.Write(BloomEffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(BloomEffectManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(effect.Active);
         writer.Write(effect.BloomValue);

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/BlurEffectSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class BlurEffectSerializer : Serializer<BlurEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(BlurEffectManager effect, BinaryWriter writer)
     {
-        writer.Write(BlurEffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(BlurEffectManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(effect.Active);
         writer.Write(effect.BlurSize);

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/DepthOfFieldEffectSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DepthOfFieldEffectSerializer : Serializer<DepthOfFieldEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(DepthOfFieldEffectManager effect, BinaryWriter writer)
     {
-        writer.Write(DepthOfFieldEffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(DepthOfFieldEffectManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(effect.Active);
         writer.Write(effect.FocalLength);

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/FogEffectSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class FogEffectSerializer : Serializer<FogEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(FogEffectManager effect, BinaryWriter writer)
     {
-        writer.Write(FogEffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(FogEffectManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(effect.Active);
         writer.Write(effect.Distance);

+ 6 - 6
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/SepiaToneEffectSerializer.cs

@@ -2,20 +2,20 @@ using System.IO;
 
 namespace MeidoPhotoStudio.Plugin;
 
-public class SepiaToneEffectSerializer : Serializer<SepiaToneEffectManger>
+public class SepiaToneEffectSerializer : Serializer<SepiaToneEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
-    public override void Serialize(SepiaToneEffectManger effect, BinaryWriter writer)
+    public override void Serialize(SepiaToneEffectManager effect, BinaryWriter writer)
     {
-        writer.Write(SepiaToneEffectManger.header);
+        writer.Write(SepiaToneEffectManager.Header);
 
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write(effect.Active);
     }
 
-    public override void Deserialize(SepiaToneEffectManger effect, BinaryReader reader, SceneMetadata metadata)
+    public override void Deserialize(SepiaToneEffectManager effect, BinaryReader reader, SceneMetadata metadata)
     {
         _ = reader.ReadVersion();
 

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/EffectSerializers/VignetteEffectSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class VignetteEffectSerializer : Serializer<VignetteEffectManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(VignetteEffectManager manager, BinaryWriter writer)
     {
-        writer.Write(VignetteEffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(VignetteEffectManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(manager.Active);
         writer.Write(manager.Intensity);

+ 2 - 2
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/LightPropertySerializer.cs

@@ -4,11 +4,11 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class LightPropertySerializer : Serializer<LightProperty>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(LightProperty prop, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write(prop.Rotation);
         writer.Write(prop.Intensity);

+ 8 - 8
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/CameraManagerSerializer.cs

@@ -4,20 +4,17 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class CameraManagerSerializer : Serializer<CameraManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
-    private static readonly CameraInfo dummyInfo = new();
+    private static readonly CameraInfo DummyInfo = new();
 
     private static Serializer<CameraInfo> InfoSerializer =>
         Serialization.Get<CameraInfo>();
 
-    private static CameraInfo[] GetCameraInfos(CameraManager manager) =>
-        Utility.GetFieldValue<CameraManager, CameraInfo[]>(manager, "cameraInfos");
-
     public override void Serialize(CameraManager manager, BinaryWriter writer)
     {
-        writer.Write(CameraManager.header);
-        writer.WriteVersion(version);
+        writer.Write(CameraManager.Header);
+        writer.WriteVersion(Version);
 
         var cameraInfos = GetCameraInfos(manager);
 
@@ -44,7 +41,7 @@ public class CameraManagerSerializer : Serializer<CameraManager>
         var cameraInfos = GetCameraInfos(manager);
 
         for (var i = 0; i < cameraCount; i++)
-            InfoSerializer.Deserialize(i >= manager.CameraCount ? dummyInfo : cameraInfos[i], reader, metadata);
+            InfoSerializer.Deserialize(i >= manager.CameraCount ? DummyInfo : cameraInfos[i], reader, metadata);
 
         if (metadata.Environment)
             return;
@@ -53,4 +50,7 @@ public class CameraManagerSerializer : Serializer<CameraManager>
 
         CameraUtility.StopAll();
     }
+
+    private static CameraInfo[] GetCameraInfos(CameraManager manager) =>
+        Utility.GetFieldValue<CameraManager, CameraInfo[]>(manager, "cameraInfos");
 }

+ 9 - 10
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/EffectManagerSerializer.cs

@@ -7,20 +7,17 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class EffectManagerSerializer : Serializer<EffectManager>
 {
-    private const short version = 1;
-
-    private static Dictionary<Type, IEffectManager> GetEffectManagers(EffectManager manager) =>
-        Utility.GetFieldValue<EffectManager, Dictionary<Type, IEffectManager>>(manager, "EffectManagers");
+    private const short Version = 1;
 
     public override void Serialize(EffectManager manager, BinaryWriter writer)
     {
-        writer.Write(EffectManager.header);
-        writer.WriteVersion(version);
+        writer.Write(EffectManager.Header);
+        writer.WriteVersion(Version);
 
         foreach (var effectManager in GetEffectManagers(manager).Values)
             Serialization.Get(effectManager.GetType()).Serialize(effectManager, writer);
 
-        writer.Write(EffectManager.footer);
+        writer.Write(EffectManager.Footer);
     }
 
     public override void Deserialize(EffectManager manager, BinaryReader reader, SceneMetadata metadata)
@@ -30,16 +27,18 @@ public class EffectManagerSerializer : Serializer<EffectManager>
         var headerToManager =
             GetEffectManagers(manager).ToDictionary(
                 x => (string)x.Key.GetField("header").GetValue(null),
-                y => y.Value
-            );
+                y => y.Value);
 
         string header;
 
-        while ((header = reader.ReadString()) is not EffectManager.footer)
+        while ((header = reader.ReadString()) is not EffectManager.Footer)
         {
             var effectManager = headerToManager[header];
 
             Serialization.Get(effectManager.GetType()).Deserialize(effectManager, reader, metadata);
         }
     }
+
+    private static Dictionary<Type, IEffectManager> GetEffectManagers(EffectManager manager) =>
+        Utility.GetFieldValue<EffectManager, Dictionary<Type, IEffectManager>>(manager, "EffectManagers");
 }

+ 10 - 7
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/EnvironmentManagerSerializer.cs

@@ -1,23 +1,21 @@
 using System;
 using System.IO;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class EnvironmentManagerSerializer : Serializer<EnvironmentManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static SimpleSerializer<TransformDTO> TransformDtoSerializer =>
         Serialization.GetSimple<TransformDTO>();
 
-    private static Transform GetBgTransform(EnvironmentManager manager) =>
-        Utility.GetFieldValue<EnvironmentManager, Transform>(manager, "bg");
-
     public override void Serialize(EnvironmentManager manager, BinaryWriter writer)
     {
-        writer.Write(EnvironmentManager.header);
-        writer.WriteVersion(version);
+        writer.Write(EnvironmentManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(manager.CurrentBgAsset);
 
@@ -45,12 +43,14 @@ public class EnvironmentManagerSerializer : Serializer<EnvironmentManager>
         var validBg = assetIndex >= 0;
 
         if (validBg)
+        {
             bgAsset = bgList[assetIndex];
+        }
         else
         {
             Utility.LogWarning($"Could not load BG '{bgAsset}'");
             creativeBg = false;
-            bgAsset = EnvironmentManager.defaultBg;
+            bgAsset = EnvironmentManager.DefaultBg;
         }
 
         manager.ChangeBackground(bgAsset, creativeBg);
@@ -68,4 +68,7 @@ public class EnvironmentManagerSerializer : Serializer<EnvironmentManager>
         bg.rotation = transformDto.Rotation;
         bg.localScale = transformDto.LocalScale;
     }
+
+    private static Transform GetBgTransform(EnvironmentManager manager) =>
+        Utility.GetFieldValue<EnvironmentManager, Transform>(manager, "bg");
 }

+ 6 - 6
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/LightManagerSerializer.cs

@@ -5,18 +5,15 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class LightManagerSerializer : Serializer<LightManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static Serializer<DragPointLight> LightSerializer =>
         Serialization.Get<DragPointLight>();
 
-    private static List<DragPointLight> GetLightList(LightManager manager) =>
-        Utility.GetFieldValue<LightManager, List<DragPointLight>>(manager, "lightList");
-
     public override void Serialize(LightManager manager, BinaryWriter writer)
     {
-        writer.Write(LightManager.header);
-        writer.WriteVersion(version);
+        writer.Write(LightManager.Header);
+        writer.WriteVersion(Version);
 
         var list = GetLightList(manager);
 
@@ -43,4 +40,7 @@ public class LightManagerSerializer : Serializer<LightManager>
             LightSerializer.Deserialize(list[i], reader, metadata);
         }
     }
+
+    private static List<DragPointLight> GetLightList(LightManager manager) =>
+        Utility.GetFieldValue<LightManager, List<DragPointLight>>(manager, "lightList");
 }

+ 4 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/MeidoManagerSerializer.cs

@@ -1,19 +1,20 @@
 using System.IO;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class MeidoManagerSerializer : Serializer<MeidoManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static Serializer<Meido> MeidoSerializer =>
         Serialization.Get<Meido>();
 
     public override void Serialize(MeidoManager manager, BinaryWriter writer)
     {
-        writer.Write(MeidoManager.header);
-        writer.WriteVersion(version);
+        writer.Write(MeidoManager.Header);
+        writer.WriteVersion(Version);
 
         var meidoList = manager.ActiveMeidoList;
 

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/MessageWindowManagerSerializer.cs

@@ -4,12 +4,12 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class MessageWindowManagerSerializer : Serializer<MessageWindowManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(MessageWindowManager manager, BinaryWriter writer)
     {
-        writer.Write(MessageWindowManager.header);
-        writer.WriteVersion(version);
+        writer.Write(MessageWindowManager.Header);
+        writer.WriteVersion(Version);
 
         writer.Write(manager.ShowingMessage);
         writer.Write(manager.FontSize);

+ 27 - 27
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/ManagerSerializers/PropManagerSerializer.cs

@@ -5,39 +5,15 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class PropManagerSerializer : Serializer<PropManager>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static SimpleSerializer<DragPointPropDTO> DragPointDtoSerializer =>
         Serialization.GetSimple<DragPointPropDTO>();
 
-    private static List<DragPointProp> GetPropList(PropManager manager) =>
-        Utility.GetFieldValue<PropManager, List<DragPointProp>>(manager, "propList");
-
-    private static void Apply(PropManager manager, DragPointProp prop, DragPointPropDTO dto)
-    {
-        var (transformDto, attachPointInfo, shadowCasting) = dto;
-
-        prop.ShadowCasting = shadowCasting;
-
-        var transform = prop.MyObject;
-
-        if (attachPointInfo.AttachPoint is not AttachPoint.None)
-        {
-            manager.AttachProp(prop, attachPointInfo.AttachPoint, attachPointInfo.MaidIndex);
-            transform.localPosition = transformDto.LocalPosition;
-            transform.localRotation = transformDto.LocalRotation;
-        }
-
-        // TODO: Use transform.SetRotationAndPosition or whatever it's called.
-        transform.position = transformDto.Position;
-        transform.rotation = transformDto.Rotation;
-        transform.localScale = transformDto.LocalScale;
-    }
-
     public override void Serialize(PropManager manager, BinaryWriter writer)
     {
-        writer.Write(PropManager.header);
-        writer.WriteVersion(version);
+        writer.Write(PropManager.Header);
+        writer.WriteVersion(Version);
 
         var propList = GetPropList(manager);
 
@@ -69,4 +45,28 @@ public class PropManagerSerializer : Serializer<PropManager>
             propIndex++;
         }
     }
+
+    private static List<DragPointProp> GetPropList(PropManager manager) =>
+        Utility.GetFieldValue<PropManager, List<DragPointProp>>(manager, "propList");
+
+    private static void Apply(PropManager manager, DragPointProp prop, DragPointPropDTO dto)
+    {
+        var (transformDto, attachPointInfo, shadowCasting) = dto;
+
+        prop.ShadowCasting = shadowCasting;
+
+        var transform = prop.MyObject;
+
+        if (attachPointInfo.AttachPoint is not AttachPoint.None)
+        {
+            manager.AttachProp(prop, attachPointInfo.AttachPoint, attachPointInfo.MaidIndex);
+            transform.localPosition = transformDto.LocalPosition;
+            transform.localRotation = transformDto.LocalRotation;
+        }
+
+        // TODO: Use transform.SetRotationAndPosition or whatever it's called.
+        transform.position = transformDto.Position;
+        transform.rotation = transformDto.Rotation;
+        transform.localScale = transformDto.LocalScale;
+    }
 }

+ 21 - 14
src/MeidoPhotoStudio.Plugin/Serialization/Serializers/MeidoSerializer.cs

@@ -2,16 +2,17 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class MeidoSerializer : Serializer<Meido>
 {
-    private const short version = 1;
-    private const short headVersion = 1;
-    private const short bodyVersion = 2;
-    private const short clothingVersion = 1;
+    private const short Version = 1;
+    private const short HeadVersion = 1;
+    private const short BodyVersion = 2;
+    private const short ClothingVersion = 1;
 
     private static SimpleSerializer<PoseInfo> PoseInfoSerializer =>
         Serialization.GetSimple<PoseInfo>();
@@ -26,7 +27,7 @@ public class MeidoSerializer : Serializer<Meido>
         using var memoryStream = new MemoryStream();
         using var tempWriter = new BinaryWriter(memoryStream, Encoding.UTF8);
 
-        tempWriter.WriteVersion(version);
+        tempWriter.WriteVersion(Version);
 
         TransformDtoSerializer.Serialize(new TransformDTO(maid.transform), tempWriter);
 
@@ -43,7 +44,7 @@ public class MeidoSerializer : Serializer<Meido>
         {
             var body = meido.Body;
 
-            writer.WriteVersion(headVersion);
+            writer.WriteVersion(HeadVersion);
 
             // eye direction
             writer.WriteQuaternion(body.quaDefEyeL * Quaternion.Inverse(meido.DefaultEyeRotL));
@@ -72,7 +73,7 @@ public class MeidoSerializer : Serializer<Meido>
 
         static void SerializeBody(Meido meido, BinaryWriter writer)
         {
-            writer.WriteVersion(bodyVersion);
+            writer.WriteVersion(BodyVersion);
 
             // pose
             var poseBuffer = meido.SerializePose(true);
@@ -83,13 +84,13 @@ public class MeidoSerializer : Serializer<Meido>
             PoseInfoSerializer.Serialize(meido.CachedPose, writer);
 
             // TODO: Think about how to indicate code for new versions of serialization.
-            #region v2
-            // sub mune rotation
-            var body = meido.Body;
+            { // V2
+                // sub mune rotation
+                var body = meido.Body;
 
-            writer.WriteQuaternion(body.GetBone("Mune_L_sub").localRotation);
-            writer.WriteQuaternion(body.GetBone("Mune_R_sub").localRotation);
-            #endregion
+                writer.WriteQuaternion(body.GetBone("Mune_L_sub").localRotation);
+                writer.WriteQuaternion(body.GetBone("Mune_R_sub").localRotation);
+            }
         }
 
         static void SerializeClothing(Meido meido, BinaryWriter writer)
@@ -97,7 +98,7 @@ public class MeidoSerializer : Serializer<Meido>
             var maid = meido.Maid;
             var body = meido.Body;
 
-            writer.WriteVersion(clothingVersion);
+            writer.WriteVersion(ClothingVersion);
 
             // body visible
             writer.Write(body.GetMask(TBody.SlotID.body));
@@ -120,7 +121,9 @@ public class MeidoSerializer : Serializer<Meido>
                         value = slots.Any(slot => body.GetMask(slot));
                 }
                 else if (body.GetSlotLoaded(clothingSlot))
+                {
                     value = body.GetMask(clothingSlot);
+                }
 
                 writer.Write(value);
             }
@@ -232,7 +235,9 @@ public class MeidoSerializer : Serializer<Meido>
             var muneSetting = new KeyValuePair<bool, bool>(true, true);
 
             if (metadata.MMConverted)
+            {
                 meido.IKManager.Deserialize(reader);
+            }
             else
             {
                 var poseBufferLength = reader.ReadInt32();
@@ -290,7 +295,9 @@ public class MeidoSerializer : Serializer<Meido>
                     body.SetMask(TBody.SlotID.accHead, value);
                 }
                 else if (body.GetSlotLoaded(clothingSlot))
+                {
                     body.SetMask(clothingSlot, value);
+                }
             }
 
             // zurashi and mekure

+ 32 - 0
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/DragPointPropDTO.cs

@@ -0,0 +1,32 @@
+namespace MeidoPhotoStudio.Plugin;
+
+// TODO: Extract other classes to another file
+public class DragPointPropDTO
+{
+    public DragPointPropDTO()
+    {
+    }
+
+    public DragPointPropDTO(DragPointProp dragPoint)
+    {
+        TransformDTO = new(dragPoint.MyObject.transform);
+        ShadowCasting = dragPoint.ShadowCasting;
+        AttachPointInfo = dragPoint.AttachPointInfo;
+        PropInfo = dragPoint.Info;
+    }
+
+    public TransformDTO TransformDTO { get; set; }
+
+    public AttachPointInfo AttachPointInfo { get; set; }
+
+    public PropInfo PropInfo { get; set; }
+
+    public bool ShadowCasting { get; set; }
+
+    public void Deconstruct(out TransformDTO transform, out AttachPointInfo attachPointInfo, out bool shadowCasting)
+    {
+        transform = TransformDTO;
+        attachPointInfo = AttachPointInfo;
+        shadowCasting = ShadowCasting;
+    }
+}

+ 3 - 29
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/DragPointPropDTOSerializer.cs

@@ -4,7 +4,7 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class DragPointPropDTOSerializer : SimpleSerializer<DragPointPropDTO>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     private static SimpleSerializer<PropInfo> PropInfoSerializer =>
         Serialization.GetSimple<PropInfo>();
@@ -17,7 +17,7 @@ public class DragPointPropDTOSerializer : SimpleSerializer<DragPointPropDTO>
 
     public override void Serialize(DragPointPropDTO dragPointDto, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         PropInfoSerializer.Serialize(dragPointDto.PropInfo, writer);
 
@@ -37,33 +37,7 @@ public class DragPointPropDTOSerializer : SimpleSerializer<DragPointPropDTO>
             PropInfo = PropInfoSerializer.Deserialize(reader, metadata),
             TransformDTO = TransformSerializer.Deserialize(reader, metadata),
             AttachPointInfo = AttachPointSerializer.Deserialize(reader, metadata),
-            ShadowCasting = reader.ReadBoolean()
+            ShadowCasting = reader.ReadBoolean(),
         };
     }
 }
-
-// TODO: Extract other classes to another file
-public class DragPointPropDTO
-{
-    public TransformDTO TransformDTO { get; set; }
-    public AttachPointInfo AttachPointInfo { get; set; }
-    public PropInfo PropInfo { get; set; }
-    public bool ShadowCasting { get; set; }
-
-    public DragPointPropDTO() { }
-
-    public DragPointPropDTO(DragPointProp dragPoint)
-    {
-        TransformDTO = new(dragPoint.MyObject.transform);
-        ShadowCasting = dragPoint.ShadowCasting;
-        AttachPointInfo = dragPoint.AttachPointInfo;
-        PropInfo = dragPoint.Info;
-    }
-
-    public void Deconstruct(out TransformDTO transform, out AttachPointInfo attachPointInfo, out bool shadowCasting)
-    {
-        transform = TransformDTO;
-        attachPointInfo = AttachPointInfo;
-        shadowCasting = ShadowCasting;
-    }
-}

+ 2 - 2
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/PoseInfoSerializer.cs

@@ -4,11 +4,11 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class PoseInfoSerializer : SimpleSerializer<PoseInfo>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(PoseInfo obj, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write(obj.PoseGroup);
         writer.Write(obj.Pose);

+ 3 - 3
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/PropInfoSerializer.cs

@@ -4,11 +4,11 @@ namespace MeidoPhotoStudio.Plugin;
 
 public class PropInfoSerializer : SimpleSerializer<PropInfo>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(PropInfo info, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write((int)info.Type);
         writer.WriteNullableString(info.Filename);
@@ -26,7 +26,7 @@ public class PropInfoSerializer : SimpleSerializer<PropInfo>
             Filename = reader.ReadNullableString(),
             SubFilename = reader.ReadNullableString(),
             MyRoomID = reader.ReadInt32(),
-            IconFile = reader.ReadNullableString()
+            IconFile = reader.ReadNullableString(),
         };
     }
 }

+ 29 - 0
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/TransformDTO.cs

@@ -0,0 +1,29 @@
+using UnityEngine;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public class TransformDTO
+{
+    public TransformDTO()
+    {
+    }
+
+    public TransformDTO(Transform transform)
+    {
+        Position = transform.position;
+        LocalPosition = transform.localPosition;
+        Rotation = transform.rotation;
+        LocalRotation = transform.localRotation;
+        LocalScale = transform.localScale;
+    }
+
+    public Vector3 Position { get; set; }
+
+    public Vector3 LocalPosition { get; set; }
+
+    public Quaternion Rotation { get; set; } = Quaternion.identity;
+
+    public Quaternion LocalRotation { get; set; } = Quaternion.identity;
+
+    public Vector3 LocalScale { get; set; } = Vector3.one;
+}

+ 3 - 24
src/MeidoPhotoStudio.Plugin/Serialization/SimpleSerializers/TransformDTOSerializer.cs

@@ -1,15 +1,14 @@
 using System.IO;
-using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public class TransformDTOSerializer : SimpleSerializer<TransformDTO>
 {
-    private const short version = 1;
+    private const short Version = 1;
 
     public override void Serialize(TransformDTO transform, BinaryWriter writer)
     {
-        writer.WriteVersion(version);
+        writer.WriteVersion(Version);
 
         writer.Write(transform.Position);
         writer.Write(transform.Rotation);
@@ -28,27 +27,7 @@ public class TransformDTOSerializer : SimpleSerializer<TransformDTO>
             Rotation = reader.ReadQuaternion(),
             LocalPosition = reader.ReadVector3(),
             LocalRotation = reader.ReadQuaternion(),
-            LocalScale = reader.ReadVector3()
+            LocalScale = reader.ReadVector3(),
         };
     }
 }
-
-public class TransformDTO
-{
-    public Vector3 Position { get; set; }
-    public Vector3 LocalPosition { get; set; }
-    public Quaternion Rotation { get; set; } = Quaternion.identity;
-    public Quaternion LocalRotation { get; set; } = Quaternion.identity;
-    public Vector3 LocalScale { get; set; } = Vector3.one;
-
-    public TransformDTO() { }
-
-    public TransformDTO(Transform transform)
-    {
-        Position = transform.position;
-        LocalPosition = transform.localPosition;
-        Rotation = transform.rotation;
-        LocalRotation = transform.localRotation;
-        LocalScale = transform.localScale;
-    }
-}

+ 35 - 0
src/MeidoPhotoStudio.Plugin/StreamExtensions.cs

@@ -0,0 +1,35 @@
+using System.IO;
+
+using Ionic.Zlib;
+
+namespace MeidoPhotoStudio.Plugin;
+
+public static class StreamExtensions
+{
+    public static void CopyTo(this Stream stream, Stream outStream)
+    {
+        var buf = new byte[1024 * 32];
+
+        int length;
+
+        while ((length = stream.Read(buf, 0, buf.Length)) > 0)
+            outStream.Write(buf, 0, length);
+    }
+
+    public static MemoryStream Decompress(this MemoryStream stream)
+    {
+        var dataMemoryStream = new MemoryStream();
+
+        using var compressionStream = new DeflateStream(stream, CompressionMode.Decompress, true);
+
+        compressionStream.CopyTo(dataMemoryStream);
+        compressionStream.Flush();
+
+        dataMemoryStream.Position = 0L;
+
+        return dataMemoryStream;
+    }
+
+    public static DeflateStream GetCompressionStream(this MemoryStream stream) =>
+        new(stream, CompressionMode.Compress);
+}

+ 32 - 36
src/MeidoPhotoStudio.Plugin/Translation.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+
 using BepInEx.Configuration;
 using Newtonsoft.Json.Linq;
 
@@ -9,56 +10,54 @@ namespace MeidoPhotoStudio.Plugin;
 
 public static class Translation
 {
-    private const string settingsHeader = "Translation";
+    private const string SettingsHeader = "Translation";
+
+    private static readonly string[] Props = { "ui", "props", "bg", "face" };
+    private static readonly ConfigEntry<string> CurrentLanguageConfig = Configuration.Config.Bind(
+        SettingsHeader,
+        "Language",
+        "en",
+        "Directory to pull translations from\nTranslations are found in the 'Translations' folder");
+
+    private static readonly ConfigEntry<bool> SuppressWarningsConfig = Configuration.Config.Bind(
+        SettingsHeader,
+        "SuppressWarnings",
+        false,
+        "Suppress translation warnings from showing up in the console");
+
+    private static Dictionary<string, Dictionary<string, string>> translations;
+    private static bool forceSuppressWarnings;
+    private static bool suppressWarningsCached;
 
-    private static readonly string[] props = { "ui", "props", "bg", "face" };
-    private static readonly ConfigEntry<string> currentLanguage;
-    private static readonly ConfigEntry<bool> suppressWarnings;
+    static Translation() =>
+        suppressWarningsCached = !SuppressWarningsConfig.Value;
 
     public static event EventHandler ReloadTranslationEvent;
 
-    private static Dictionary<string, Dictionary<string, string>> Translations;
-    private static bool forceSuppressWarnings;
-    private static bool suppressWarningsCached;
-
     public static bool SuppressWarnings
     {
         get => suppressWarningsCached;
         set
         {
             suppressWarningsCached = value;
-            suppressWarnings.Value = value;
+            SuppressWarningsConfig.Value = value;
         }
     }
 
     public static string CurrentLanguage
     {
-        get => currentLanguage.Value;
-        set => currentLanguage.Value = value;
-    }
-
-    static Translation()
-    {
-        currentLanguage = Configuration.Config.Bind(
-            settingsHeader, "Language", "en",
-            "Directory to pull translations from\nTranslations are found in the 'Translations' folder"
-        );
-
-        suppressWarnings = Configuration.Config.Bind(
-            settingsHeader, "SuppressWarnings", false, "Suppress translation warnings from showing up in the console"
-        );
-
-        suppressWarningsCached = !suppressWarnings.Value;
+        get => CurrentLanguageConfig.Value;
+        set => CurrentLanguageConfig.Value = value;
     }
 
     public static void Initialize(string language)
     {
         forceSuppressWarnings = false;
 
-        var rootTranslationPath = Path.Combine(Constants.configPath, Constants.translationDirectory);
+        var rootTranslationPath = Path.Combine(Constants.ConfigPath, Constants.TranslationDirectory);
         var currentTranslationPath = Path.Combine(rootTranslationPath, language);
 
-        Translations = new(StringComparer.InvariantCultureIgnoreCase);
+        translations = new(StringComparer.InvariantCultureIgnoreCase);
 
         if (!Directory.Exists(currentTranslationPath))
         {
@@ -68,7 +67,7 @@ public static class Translation
             return;
         }
 
-        foreach (var prop in props)
+        foreach (var prop in Props)
         {
             var translationFile = $"translation.{prop}.json";
 
@@ -82,7 +81,7 @@ public static class Translation
                 {
                     var token = translationProp.Value;
 
-                    Translations[translationProp.Path] =
+                    translations[translationProp.Path] =
                         new(token.ToObject<Dictionary<string, string>>(), StringComparer.InvariantCultureIgnoreCase);
                 }
             }
@@ -104,7 +103,7 @@ public static class Translation
     {
         warn = !forceSuppressWarnings && !SuppressWarnings && warn;
 
-        if (!Translations.ContainsKey(category))
+        if (!translations.ContainsKey(category))
         {
             if (warn)
                 Utility.LogWarning($"Could not translate '{text}': category '{category}' was not found");
@@ -112,14 +111,11 @@ public static class Translation
             return false;
         }
 
-        if (!Translations[category].ContainsKey(text))
+        if (!translations[category].ContainsKey(text))
         {
             if (warn)
-            {
                 Utility.LogWarning(
-                    $"Could not translate '{text}': '{text}' was not found in category '{category}'"
-                );
-            }
+                    $"Could not translate '{text}': '{text}' was not found in category '{category}'");
 
             return false;
         }
@@ -128,7 +124,7 @@ public static class Translation
     }
 
     public static string Get(string category, string text, bool warn = true) =>
-        Has(category, text, warn) ? Translations[category][text] : text;
+        Has(category, text, warn) ? translations[category][text] : text;
 
     public static string[] GetArray(string category, IEnumerable<string> list) =>
         GetList(category, list).ToArray();

+ 37 - 220
src/MeidoPhotoStudio.Plugin/Utility.cs

@@ -4,40 +4,49 @@ using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
-using Ionic.Zlib;
+
 using UnityEngine;
 
 namespace MeidoPhotoStudio.Plugin;
 
 public static class Utility
 {
-    public enum ModKey { Control, Shift, Alt }
-
-    private const BindingFlags bindingFlags =
-        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
-
     public static readonly BepInEx.Logging.ManualLogSource Logger =
-        BepInEx.Logging.Logger.CreateLogSource(MeidoPhotoStudio.pluginName);
+        ManualLogSource;
 
-    internal static readonly byte[] pngHeader = { 137, 80, 78, 71, 13, 10, 26, 10 };
-    internal static readonly byte[] pngEnd = System.Text.Encoding.ASCII.GetBytes("IEND");
-    internal static readonly Regex guidRegEx =
+    internal static readonly byte[] PngHeader = { 137, 80, 78, 71, 13, 10, 26, 10 };
+    internal static readonly byte[] PngEnd = System.Text.Encoding.ASCII.GetBytes("IEND");
+    internal static readonly Regex GuidRegEx =
         new(@"^[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}$", RegexOptions.IgnoreCase);
-    internal static readonly GameObject mousePositionGo;
-    internal static readonly MousePosition mousePosition;
 
-    public static string Timestamp =>
-        $"{DateTime.Now:yyyyMMddHHmmss}";
+    internal static readonly GameObject MousePositionGameObject;
+    internal static readonly MousePosition MousePositionValue;
 
-    public static Vector3 MousePosition =>
-        mousePosition.Position;
+    private const BindingFlags ReflectionFlags =
+        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
+
+    private static readonly BepInEx.Logging.ManualLogSource ManualLogSource =
+        BepInEx.Logging.Logger.CreateLogSource(MeidoPhotoStudio.PluginName);
 
     static Utility()
     {
-        mousePositionGo = new();
-        mousePosition = mousePositionGo.AddComponent<MousePosition>();
+        MousePositionGameObject = new();
+        MousePositionValue = MousePositionGameObject.AddComponent<MousePosition>();
     }
 
+    public enum ModKey
+    {
+        Control,
+        Shift,
+        Alt,
+    }
+
+    public static string Timestamp =>
+        $"{DateTime.Now:yyyyMMddHHmmss}";
+
+    public static Vector3 MousePosition =>
+        MousePositionValue.Position;
+
     public static void LogInfo(object data) =>
         Logger.LogInfo(data);
 
@@ -85,7 +94,7 @@ public static class Utility
     }
 
     public static FieldInfo GetFieldInfo<T>(string field) =>
-        typeof(T).GetField(field, bindingFlags);
+        typeof(T).GetField(field, ReflectionFlags);
 
     public static TValue GetFieldValue<TType, TValue>(TType instance, string field)
     {
@@ -100,7 +109,7 @@ public static class Utility
         GetFieldInfo<TType>(name).SetValue(instance, value);
 
     public static PropertyInfo GetPropertyInfo<T>(string field) =>
-        typeof(T).GetProperty(field, bindingFlags);
+        typeof(T).GetProperty(field, ReflectionFlags);
 
     public static TValue GetPropertyValue<TType, TValue>(TType instance, string property)
     {
@@ -138,7 +147,7 @@ public static class Utility
     }
 
     public static bool IsGuidString(string guid) =>
-        !string.IsNullOrEmpty(guid) && guid.Length is 36 && guidRegEx.IsMatch(guid);
+        !string.IsNullOrEmpty(guid) && guid.Length is 36 && GuidRegEx.IsMatch(guid);
 
     public static string HandItemToOdogu(string menu)
     {
@@ -162,7 +171,7 @@ public static class Utility
         var invalid = Path.GetInvalidFileNameChars();
 
         path = path.Trim();
-        path = string.Join("_", path.Split(invalid)).Replace(".", "").Trim('_');
+        path = string.Join("_", path.Split(invalid)).Replace(".", string.Empty).Trim('_');
 
         return path;
     }
@@ -213,7 +222,7 @@ public static class Utility
 
         stream.Read(buffer, 0, 8);
 
-        return BytesEqual(buffer, pngHeader);
+        return BytesEqual(buffer, PngHeader);
     }
 
     public static bool SeekPngEnd(Stream stream)
@@ -222,7 +231,7 @@ public static class Utility
 
         stream.Read(buffer, 0, 8);
 
-        if (!BytesEqual(buffer, pngHeader))
+        if (!BytesEqual(buffer, PngHeader))
             return false;
 
         buffer = new byte[4];
@@ -238,7 +247,8 @@ public static class Utility
 
             stream.Read(buffer, 0, 4);
             stream.Seek(length + 4L, SeekOrigin.Current);
-        } while (!BytesEqual(buffer, pngEnd));
+        }
+        while (!BytesEqual(buffer, PngEnd));
 
         return true;
     }
@@ -248,202 +258,9 @@ public static class Utility
         if (Path.GetExtension(name) is not ".txt")
             name += ".txt";
 
-        File.WriteAllLines(Path.Combine(Constants.configPath, name), list.ToArray());
+        File.WriteAllLines(Path.Combine(Constants.ConfigPath, name), list.ToArray());
     }
 
     public static void WriteToFile(string name, byte[] data) =>
-        File.WriteAllBytes(Path.Combine(Constants.configPath, name), data);
-}
-
-public static class KeyValuePairExtensions
-{
-    public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
-    {
-        key = kvp.Key;
-        value = kvp.Value;
-    }
-}
-
-public static class StreamExtensions
-{
-    public static void CopyTo(this Stream stream, Stream outStream)
-    {
-        var buf = new byte[1024 * 32];
-
-        int length;
-
-        while ((length = stream.Read(buf, 0, buf.Length)) > 0)
-            outStream.Write(buf, 0, length);
-    }
-
-    public static MemoryStream Decompress(this MemoryStream stream)
-    {
-        var dataMemoryStream = new MemoryStream();
-
-        using var compressionStream = new DeflateStream(stream, CompressionMode.Decompress, true);
-
-        compressionStream.CopyTo(dataMemoryStream);
-        compressionStream.Flush();
-
-        dataMemoryStream.Position = 0L;
-
-        return dataMemoryStream;
-    }
-
-    public static DeflateStream GetCompressionStream(this MemoryStream stream) =>
-        new(stream, CompressionMode.Compress);
-}
-
-public static class CameraUtility
-{
-    public static CameraMain MainCamera =>
-        GameMain.Instance.MainCamera;
-
-    public static UltimateOrbitCamera UOCamera { get; } =
-        GameMain.Instance.MainCamera.GetComponent<UltimateOrbitCamera>();
-
-    public static void StopSpin()
-    {
-        Utility.SetFieldValue(UOCamera, "xVelocity", 0f);
-        Utility.SetFieldValue(UOCamera, "yVelocity", 0f);
-    }
-
-    public static void StopMovement() =>
-        MainCamera.SetTargetPos(MainCamera.GetTargetPos());
-
-    public static void StopAll()
-    {
-        StopSpin();
-        StopMovement();
-    }
-
-    public static void ForceCalcNearClip(this CameraMain camera)
-    {
-        camera.StopAllCoroutines();
-        camera.m_bCalcNearClip = false;
-        camera.camera.nearClipPlane = 0.01f;
-    }
-
-    public static void ResetCalcNearClip(this CameraMain camera)
-    {
-        if (camera.m_bCalcNearClip)
-            return;
-
-        camera.StopAllCoroutines();
-        camera.m_bCalcNearClip = true;
-        camera.Start();
-    }
-}
-
-public static class BinaryExtensions
-{
-    public static string ReadNullableString(this BinaryReader binaryReader) =>
-        binaryReader.ReadBoolean()
-            ? binaryReader.ReadString()
-            : null;
-
-    public static void WriteNullableString(this BinaryWriter binaryWriter, string str)
-    {
-        binaryWriter.Write(str is not null);
-
-        if (str is not null)
-            binaryWriter.Write(str);
-    }
-
-    public static void Write(this BinaryWriter binaryWriter, Vector3 vector3)
-    {
-        binaryWriter.Write(vector3.x);
-        binaryWriter.Write(vector3.y);
-        binaryWriter.Write(vector3.z);
-    }
-
-    public static void WriteVector3(this BinaryWriter binaryWriter, Vector3 vector3)
-    {
-        binaryWriter.Write(vector3.x);
-        binaryWriter.Write(vector3.y);
-        binaryWriter.Write(vector3.z);
-    }
-
-    public static Vector2 ReadVector2(this BinaryReader binaryReader) =>
-        new(binaryReader.ReadSingle(), binaryReader.ReadSingle());
-
-    public static Vector3 ReadVector3(this BinaryReader binaryReader) =>
-        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
-
-    public static Vector4 ReadVector4(this BinaryReader binaryReader) =>
-        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
-
-    public static void Write(this BinaryWriter binaryWriter, Quaternion quaternion)
-    {
-        binaryWriter.Write(quaternion.x);
-        binaryWriter.Write(quaternion.y);
-        binaryWriter.Write(quaternion.z);
-        binaryWriter.Write(quaternion.w);
-    }
-
-    public static void WriteQuaternion(this BinaryWriter binaryWriter, Quaternion quaternion)
-    {
-        binaryWriter.Write(quaternion.x);
-        binaryWriter.Write(quaternion.y);
-        binaryWriter.Write(quaternion.z);
-        binaryWriter.Write(quaternion.w);
-    }
-
-    public static Quaternion ReadQuaternion(this BinaryReader binaryReader) =>
-        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
-
-    public static void Write(this BinaryWriter binaryWriter, Color colour)
-    {
-        binaryWriter.Write(colour.r);
-        binaryWriter.Write(colour.g);
-        binaryWriter.Write(colour.b);
-        binaryWriter.Write(colour.a);
-    }
-
-    public static void WriteColour(this BinaryWriter binaryWriter, Color colour)
-    {
-        binaryWriter.Write(colour.r);
-        binaryWriter.Write(colour.g);
-        binaryWriter.Write(colour.b);
-        binaryWriter.Write(colour.a);
-    }
-
-    public static Color ReadColour(this BinaryReader binaryReader) =>
-        new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
-
-    public static Matrix4x4 ReadMatrix4x4(this BinaryReader binaryReader)
-    {
-        Matrix4x4 matrix = default;
-
-        for (var i = 0; i < 16; i++)
-            matrix[i] = binaryReader.ReadSingle();
-
-        return matrix;
-    }
-}
-
-public class MousePosition : MonoBehaviour
-{
-    private Vector3 mousePosition;
-
-    public Vector3 Position =>
-        mousePosition;
-
-    private void Awake()
-    {
-        DontDestroyOnLoad(this);
-
-        mousePosition = Input.mousePosition;
-    }
-
-    private void Update()
-    {
-        if (Input.GetMouseButton(0))
-        {
-            mousePosition.x += Input.GetAxis("Mouse X") * 20;
-            mousePosition.y += Input.GetAxis("Mouse Y") * 20;
-        }
-        else
-            mousePosition = Input.mousePosition;
-    }
+        File.WriteAllBytes(Path.Combine(Constants.ConfigPath, name), data);
 }

+ 16 - 0
stylecop.json

@@ -0,0 +1,16 @@
+{
+  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+  "settings": {
+    "layoutRules": {
+      "newlineAtEndOfFile": "require"
+    },
+    "indentation": {
+      "indentationSize": 4,
+      "useTabs": false
+    },
+    "orderingRules": {
+      "usingDirectivesPlacement": "outsideNamespace",
+      "blankLinesBetweenUsingGroups": "require"
+    }
+  }
+}