|  | @@ -0,0 +1,230 @@
 | 
	
		
			
				|  |  | +using System;
 | 
	
		
			
				|  |  | +using System.Collections;
 | 
	
		
			
				|  |  | +using System.Collections.Generic;
 | 
	
		
			
				|  |  | +using System.Globalization;
 | 
	
		
			
				|  |  | +using System.IO;
 | 
	
		
			
				|  |  | +using System.Reflection;
 | 
	
		
			
				|  |  | +using ExIni;
 | 
	
		
			
				|  |  | +using UnityEngine;
 | 
	
		
			
				|  |  | +using UnityInjector;
 | 
	
		
			
				|  |  | +using UnityInjector.Attributes;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace CM3D2.ToukaScreenShot.Plugin
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    [PluginVersion("0.1.0.2")]
 | 
	
		
			
				|  |  | +    [PluginName("Com3d2.ToukaScreenShot.Plugin")]
 | 
	
		
			
				|  |  | +    [PluginFilter("COM3D2x64")]
 | 
	
		
			
				|  |  | +    public class ToukaScreenShot : PluginBase
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        private readonly bool[] bLayerMask = new bool[32];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private readonly FieldInfo fBloom =
 | 
	
		
			
				|  |  | +            typeof(CameraMain).GetField("m_gcBloom", BindingFlags.Instance | BindingFlags.NonPublic);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private bool altKey;
 | 
	
		
			
				|  |  | +        private bool bgActiveBack = true;
 | 
	
		
			
				|  |  | +        private Color bgColorBack = Color.black;
 | 
	
		
			
				|  |  | +        private bool bgVisible = true;
 | 
	
		
			
				|  |  | +        private bool bloomBack;
 | 
	
		
			
				|  |  | +        private bool bloomOff = true;
 | 
	
		
			
				|  |  | +        private bool ctrlKey = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private string fileNameEnd = "";
 | 
	
		
			
				|  |  | +        private string fileNameHead = "img";
 | 
	
		
			
				|  |  | +        private string folderName = "ScreenShot";
 | 
	
		
			
				|  |  | +        private Camera mainCamera;
 | 
	
		
			
				|  |  | +        private int maskBack;
 | 
	
		
			
				|  |  | +        private bool noConfigFlg;
 | 
	
		
			
				|  |  | +        private bool shiftKey;
 | 
	
		
			
				|  |  | +        private IniFile sPreferences;
 | 
	
		
			
				|  |  | +        private string triggerKey = "s";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void Start()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            sPreferences = Preferences;
 | 
	
		
			
				|  |  | +            var defaultVisibleLayers = new HashSet<int>
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                0,
 | 
	
		
			
				|  |  | +                1,
 | 
	
		
			
				|  |  | +                10
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +            for (var i = 0; i < bLayerMask.Length; i++)
 | 
	
		
			
				|  |  | +                bLayerMask[i] = GetValueIni("Visible", $"Layer{i}", defaultVisibleLayers.Contains(i));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            folderName = GetValueIni("File", "Folder", "ScreenShot");
 | 
	
		
			
				|  |  | +            fileNameHead = GetValueIni("File", "Head", "img");
 | 
	
		
			
				|  |  | +            fileNameEnd = GetValueIni("File", "End", "");
 | 
	
		
			
				|  |  | +            bgVisible = GetValueIni("Visible", "BG", false);
 | 
	
		
			
				|  |  | +            bloomOff = GetValueIni("Effect", "BloomOff", true);
 | 
	
		
			
				|  |  | +            altKey = GetValueIni("Command", "Alt", false);
 | 
	
		
			
				|  |  | +            ctrlKey = GetValueIni("Command", "Ctrl", true);
 | 
	
		
			
				|  |  | +            shiftKey = GetValueIni("Command", "Shift", false);
 | 
	
		
			
				|  |  | +            triggerKey = GetValueIni("Command", "Trigger", "s");
 | 
	
		
			
				|  |  | +            if (noConfigFlg) SaveConfig();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private T GetValueIni<T>(string section, string key, T @default)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            bool TryParse(string value, out T resultVal)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                try
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    resultVal = (T) Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  | +                    return true;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                catch (Exception e)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    Debug.LogWarning($"Failed to parse {section} => {key} value {value} because {e.Message}");
 | 
	
		
			
				|  |  | +                    resultVal = default;
 | 
	
		
			
				|  |  | +                    return false;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            var iniKey = sPreferences[section][key];
 | 
	
		
			
				|  |  | +            if (iniKey != null && !string.IsNullOrEmpty(iniKey.Value) &&
 | 
	
		
			
				|  |  | +                TryParse(iniKey.Value, out var result)) return result;
 | 
	
		
			
				|  |  | +            iniKey.Value = Convert.ToString(@default, CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  | +            result = @default;
 | 
	
		
			
				|  |  | +            noConfigFlg = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return result;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void Update()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var pressedAlt = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
 | 
	
		
			
				|  |  | +            var pressedCtrl = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
 | 
	
		
			
				|  |  | +            var pressedShift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
 | 
	
		
			
				|  |  | +            if (altKey == pressedAlt && ctrlKey == pressedCtrl && shiftKey == pressedShift && Input.GetKeyDown(triggerKey))
 | 
	
		
			
				|  |  | +                StartCoroutine(ExecScreenShot());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private IEnumerator ExecScreenShot()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            yield return new WaitForEndOfFrame();
 | 
	
		
			
				|  |  | +            mainCamera = Camera.main;
 | 
	
		
			
				|  |  | +            BackUpCamera();
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            var ss = GetToukaScreenShot();
 | 
	
		
			
				|  |  | +            File.WriteAllBytes(GetTimeFileName(), ss.EncodeToPNG());
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            // Actually unload textures and GC unused stuff to free up memory
 | 
	
		
			
				|  |  | +            yield return Resources.UnloadUnusedAssets();
 | 
	
		
			
				|  |  | +            GC.Collect();
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            GameMain.Instance.SoundMgr.PlaySe("se022.ogg", false);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        private Texture2D GetToukaScreenShot()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var ss = GameMain.Instance.CMSystem.ScreenShotSuperSize switch
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                CMSystem.SSSuperSizeType.X1 => 1,
 | 
	
		
			
				|  |  | +                CMSystem.SSSuperSizeType.X2 => 2,
 | 
	
		
			
				|  |  | +                CMSystem.SSSuperSizeType.X4 => 4,
 | 
	
		
			
				|  |  | +                _ => 1
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var aa = GameMain.Instance.CMSystem.Antialias switch
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                CMSystem.AntiAliasType.None => 0,
 | 
	
		
			
				|  |  | +                CMSystem.AntiAliasType.X2 => 1,
 | 
	
		
			
				|  |  | +                CMSystem.AntiAliasType.X4 => 4,
 | 
	
		
			
				|  |  | +                CMSystem.AntiAliasType.X8 => 8,
 | 
	
		
			
				|  |  | +                _ => 8,
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var w = Screen.width * ss;
 | 
	
		
			
				|  |  | +            var h = Screen.height * ss;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var rt = RenderTexture.GetTemporary(w, h, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB, aa);
 | 
	
		
			
				|  |  | +            rt.filterMode = FilterMode.Bilinear;
 | 
	
		
			
				|  |  | +            SetCameraMask();
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            mainCamera.backgroundColor = new Color(0f, 0f, 0f, 1f);
 | 
	
		
			
				|  |  | +            var blackBgTex = RenderScreenShot(rt, w, h);
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            mainCamera.backgroundColor = new Color(1f, 1f, 1f, 1f);
 | 
	
		
			
				|  |  | +            var whiteBgTex = RenderScreenShot(rt, w, h);
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            ResetCamera();
 | 
	
		
			
				|  |  | +            mainCamera.targetTexture = null;
 | 
	
		
			
				|  |  | +            RenderTexture.ReleaseTemporary(rt);
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            var blackPix = blackBgTex.GetPixels32();
 | 
	
		
			
				|  |  | +            var whitePix = whiteBgTex.GetPixels32();
 | 
	
		
			
				|  |  | +            var resultPix = new Color32[blackPix.Length];
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            for (var i = 0; i < resultPix.Length; i++)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var wp = whitePix[i];
 | 
	
		
			
				|  |  | +                var bp = blackPix[i];
 | 
	
		
			
				|  |  | +                var diff = wp.r - bp.r;
 | 
	
		
			
				|  |  | +                if (diff < 0) diff = 0;
 | 
	
		
			
				|  |  | +                resultPix[i] = new Color32((byte)((wp.r + bp.r) >> 1), (byte)((wp.g + bp.g) >> 1), (byte)((wp.b + bp.b) >> 1), (byte) ~diff);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            var result = new Texture2D(w, h);
 | 
	
		
			
				|  |  | +            result.SetPixels32(resultPix);
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            // Mark both for destroying so they can be unloaded by UnloadUnusedAssets
 | 
	
		
			
				|  |  | +            Destroy(blackBgTex);
 | 
	
		
			
				|  |  | +            Destroy(whiteBgTex);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return result;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private string GetTimeFileName(string postfix = "")
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var targetFolder = Path.Combine(UTY.gameProjectPath, folderName);
 | 
	
		
			
				|  |  | +            if (!Directory.Exists(targetFolder)) Directory.CreateDirectory(targetFolder);
 | 
	
		
			
				|  |  | +            return Path.Combine(targetFolder, $"{fileNameHead}{DateTime.Now:yyyyMMddHHmmss}{postfix}{fileNameEnd}.png");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void BackUpCamera()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            maskBack = mainCamera.cullingMask;
 | 
	
		
			
				|  |  | +            bgColorBack = mainCamera.backgroundColor;
 | 
	
		
			
				|  |  | +            if (!bgVisible) bgActiveBack = GameMain.Instance.BgMgr.current_bg_object.activeSelf;
 | 
	
		
			
				|  |  | +            if (!bloomOff) return;
 | 
	
		
			
				|  |  | +            var bloom = (Bloom) fBloom.GetValue(GameMain.Instance.MainCamera);
 | 
	
		
			
				|  |  | +            bloomBack = bloom.enabled;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ResetCamera()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            mainCamera.cullingMask = maskBack;
 | 
	
		
			
				|  |  | +            mainCamera.backgroundColor = bgColorBack;
 | 
	
		
			
				|  |  | +            if (!bgVisible) GameMain.Instance.BgMgr.current_bg_object.SetActive(bgActiveBack);
 | 
	
		
			
				|  |  | +            if (!bloomOff) return;
 | 
	
		
			
				|  |  | +            var bloom = (Bloom) fBloom.GetValue(GameMain.Instance.MainCamera);
 | 
	
		
			
				|  |  | +            bloom.enabled = bloomBack;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void SetCameraMask()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var num = 0;
 | 
	
		
			
				|  |  | +            for (var i = 0; i < bLayerMask.Length; i++)
 | 
	
		
			
				|  |  | +                if (bLayerMask[i])
 | 
	
		
			
				|  |  | +                    num += 1 << i;
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            mainCamera.cullingMask = num;
 | 
	
		
			
				|  |  | +            if (!bgVisible) GameMain.Instance.BgMgr.current_bg_object.SetActive(false);
 | 
	
		
			
				|  |  | +            if (!bloomOff) return;
 | 
	
		
			
				|  |  | +            var bloom = (Bloom) fBloom.GetValue(GameMain.Instance.MainCamera);
 | 
	
		
			
				|  |  | +            bloom.enabled = false;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private Texture2D RenderScreenShot(RenderTexture rt, int w, int h)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            mainCamera.targetTexture = rt;
 | 
	
		
			
				|  |  | +            mainCamera.Render();
 | 
	
		
			
				|  |  | +            var texture2D = new Texture2D(w, h, TextureFormat.ARGB32, false);
 | 
	
		
			
				|  |  | +            var prev = RenderTexture.active;
 | 
	
		
			
				|  |  | +            RenderTexture.active = rt;
 | 
	
		
			
				|  |  | +            texture2D.ReadPixels(new Rect(0f, 0f, w, h), 0, 0, false);
 | 
	
		
			
				|  |  | +            RenderTexture.active = prev;
 | 
	
		
			
				|  |  | +            return texture2D;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |