1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Diagnostics ;
4+ using System . Globalization ;
5+ using AquaMai . Config . Attributes ;
6+ using AquaMai . Core . Helpers ;
7+ using AquaMai . Core . Resources ;
8+ using AquaMai . Mods . GameSystem ;
9+ using HarmonyLib ;
10+ using IO ;
11+ using MAI2 . Util ;
12+ using MAI2System ;
13+ using Main ;
14+ using Manager ;
15+ using MelonLoader ;
16+ using Monitor . Error ;
17+ using Process ;
18+ using Process . Error ;
19+ using TMPro ;
20+ using UnityEngine ;
21+
22+ namespace AquaMai . Mods . UX ;
23+
24+ [ ConfigSection (
25+ en : "Custom hardware alert, you can configure to display an error frame upon hardware failure. Toggle the switches below to define the required hardware." ,
26+ zh : "自定义硬件警告,可配置在指定硬件自检失败时阻止游戏启动并显示报错画面,开启下方的错误类型以配置需要关注的错误。" ) ]
27+ public class HardwareAlert
28+ {
29+ [ ConfigEntry (
30+ en : "If enabled, all the in-game hardware warnings will be displayed in game's original language, like Japanese for SDEZ. If you have used any translation pack, you should disable this setting." ,
31+ zh : "如果启用,所有硬件警告将会使用游戏原本的语言显示,例如 SDEZ 就会用日文显示报错。如果你安装了任何汉化包,你应该关闭这个选项。" ) ]
32+ private static readonly bool UseOriginalGameLanguage = true ;
33+ [ ConfigEntry (
34+ en : "1P Touch Sensor" ,
35+ zh : "1P 触摸屏" ) ]
36+ private static readonly bool TouchSensor_1P = false ; // Error 3300, 3301
37+ [ ConfigEntry (
38+ en : "2P Touch Sensor" ,
39+ zh : "2P 触摸屏" ) ]
40+ private static readonly bool TouchSensor_2P = false ; // Error 3302, 3303
41+ [ ConfigEntry (
42+ en : "1P LED" ,
43+ zh : "1P LED" ) ]
44+ private static readonly bool LED_1P = false ; // custom 3400
45+ [ ConfigEntry (
46+ en : "2P LED" ,
47+ zh : "2P LED" ) ]
48+ private static readonly bool LED_2P = false ; // custom 3401
49+ [ ConfigEntry (
50+ en : "Player Camera" ,
51+ zh : "玩家摄像机" ) ]
52+ private static readonly bool PlayerCamera = false ; // 3102
53+ [ ConfigEntry (
54+ en : "DX Pass 1P" ,
55+ zh : "DX Pass 1P" ) ]
56+ private static readonly bool CodeReader_1P = false ; // 3101
57+ [ ConfigEntry (
58+ en : "DX Pass 2P" ,
59+ zh : "DX Pass 2P" ) ]
60+ private static readonly bool CodeReader_2P = false ; // 3101
61+ [ ConfigEntry (
62+ en : "WeChat QRCode Camera" ,
63+ zh : "二维码扫描摄像头" ) ]
64+ public static readonly bool ChimeCamera = false ; // 3100
65+
66+ private static readonly List < string > CameraTypeList = [ "QRLeft" , "QRRight" , "Photo" , "Chime" ] ;
67+ private static SortedDictionary < CameraTypeEnumInner , int > _cameraIndex = [ ] ;
68+ private static bool _isInitialized = false ;
69+
70+ private enum CameraTypeEnumInner
71+ {
72+ QRLeft ,
73+ QRRight ,
74+ Photo ,
75+ Chime ,
76+ }
77+
78+ [ HarmonyPostfix ]
79+ [ HarmonyPatch ( typeof ( CameraManager ) , "CameraInitialize" ) ]
80+ public static void PostCameraInitialize ( CameraManager __instance )
81+ {
82+ if ( _isInitialized )
83+ {
84+ return ;
85+ }
86+
87+ var curCamIdx = 0 ;
88+ foreach ( var cameraTypeName in CameraTypeList )
89+ {
90+ if ( Enum . TryParse < CameraTypeEnumInner > ( cameraTypeName , out var cameraType ) )
91+ {
92+ MelonLogger . Msg ( $ "[HardwareAlert] Identified camera type { cameraType } for current game version on idx { curCamIdx } ") ;
93+ _cameraIndex [ cameraType ] = curCamIdx ;
94+ curCamIdx ++ ;
95+ }
96+ }
97+
98+ _isInitialized = true ;
99+ }
100+
101+ [ HarmonyPostfix ]
102+ [ HarmonyPatch ( typeof ( StartupProcess ) , "OnUpdate" ) ]
103+ public static void OnPostUpdate ( StartupProcess __instance )
104+ {
105+ // get current startup state
106+ var tv = Traverse . Create ( __instance ) ;
107+ // var state = tv.Field("_state").GetValue<byte>();
108+ var statusSubMsg = tv . Field ( "_statusSubMsg" ) . GetValue < string [ ] > ( ) ;
109+
110+ // Touch sensor check
111+ // The built-in AMDaemon errors are not stable, and cannot be localized.
112+ // So we decided to use another approach to check it.
113+ if ( TouchSensor_1P && statusSubMsg [ 0 ] == ConstParameter . TestString_Bad )
114+ {
115+ ErrorFrame . Show ( __instance , 3300 , FaultTouchSensor1P [ GetLocale ( ) ] ) ;
116+ return ;
117+ }
118+ if ( TouchSensor_2P && statusSubMsg [ 1 ] == ConstParameter . TestString_Bad )
119+ {
120+ ErrorFrame . Show ( __instance , 3302 , FaultTouchSensor2P [ GetLocale ( ) ] ) ;
121+ return ;
122+ }
123+
124+ // LED check
125+ if ( LED_1P && statusSubMsg [ 2 ] == ConstParameter . TestString_Bad )
126+ {
127+ ErrorFrame . Show ( __instance , 3400 , FaultLED1P [ GetLocale ( ) ] ) ;
128+ return ;
129+ }
130+ if ( LED_2P && statusSubMsg [ 3 ] == ConstParameter . TestString_Bad )
131+ {
132+ ErrorFrame . Show ( __instance , 3401 , FaultLED2P [ GetLocale ( ) ] ) ;
133+ return ;
134+ }
135+
136+ // Camera Check
137+ if ( CameraManager . IsReady )
138+ {
139+ var nCam = CameraManager . IsAvailableCameras . Length ;
140+
141+ var pcIdx = _cameraIndex [ CameraTypeEnumInner . Photo ] ;
142+ if ( PlayerCamera && pcIdx < nCam && ! CameraManager . IsAvailableCameras [ pcIdx ] )
143+ {
144+ ErrorFrame . Show ( __instance , 3102 , FaultPlayerCamera [ GetLocale ( ) ] ) ;
145+ return ;
146+ }
147+
148+ var cr1PIdx = _cameraIndex [ CameraTypeEnumInner . QRLeft ] ;
149+ if ( CodeReader_1P && cr1PIdx < nCam && ! CameraManager . IsAvailableCameras [ cr1PIdx ] )
150+ {
151+ ErrorFrame . Show ( __instance , 3101 , FaultQR1P [ GetLocale ( ) ] ) ;
152+ return ;
153+ }
154+
155+ var cr2PIdx = _cameraIndex [ CameraTypeEnumInner . QRRight ] ;
156+ if ( CodeReader_2P && cr2PIdx < nCam && ! CameraManager . IsAvailableCameras [ cr2PIdx ] )
157+ {
158+ ErrorFrame . Show ( __instance , 3101 , FaultQR2P [ GetLocale ( ) ] ) ;
159+ return ;
160+ }
161+
162+ var chimeIdx = _cameraIndex [ CameraTypeEnumInner . Chime ] ;
163+ if ( ChimeCamera && chimeIdx < nCam && ! CameraManager . IsAvailableCameras [ chimeIdx ] )
164+ {
165+ ErrorFrame . Show ( __instance , 3100 , FaultChime [ GetLocale ( ) ] ) ;
166+ return ;
167+ }
168+ }
169+ }
170+
171+ private static string GetLocale ( )
172+ {
173+ if ( UseOriginalGameLanguage )
174+ {
175+ return GameVersionToLocale [ GameInfo . GameId ] ;
176+ }
177+ MelonLogger . Msg ( $ "[HardwareAlert] Using locale '{ Locale . Culture . TwoLetterISOLanguageName } '") ;
178+ // MelonLogger.Msg($"[HardwareAlert] Using locale '{Locale.Culture.Name}'");
179+ return Locale . Culture . TwoLetterISOLanguageName . Equals ( "zh" ,
180+ StringComparison . OrdinalIgnoreCase )
181+ ? "zh"
182+ : "en" ;
183+ }
184+
185+ private static readonly Dictionary < string , string > GameVersionToLocale = new ( )
186+ {
187+ [ "SDEZ" ] = "jp" ,
188+ [ "SDGA" ] = "en" ,
189+ [ "SDGB" ] = "zh" ,
190+ } ;
191+ private static readonly Dictionary < string , string > FaultTouchSensor1P = new ( )
192+ {
193+ [ "jp" ] = "タッチセンサ(1P)はご利用いただけません" ,
194+ [ "en" ] = "Touch Sensor (1P) not available" ,
195+ [ "zh" ] = "触摸传感器(1P)不可用" ,
196+ } ;
197+ private static readonly Dictionary < string , string > FaultTouchSensor2P = new ( )
198+ {
199+ [ "jp" ] = "タッチセンサ(2P)はご利用いただけません" ,
200+ [ "en" ] = "Touch Sensor (2P) not available" ,
201+ [ "zh" ] = "触摸传感器(2P)不可用" ,
202+ } ;
203+ private static readonly Dictionary < string , string > FaultLED1P = new ( )
204+ {
205+ [ "jp" ] = "LED(1P)はご利用いただけません" ,
206+ [ "en" ] = "LED(1P) not available" ,
207+ [ "zh" ] = "LED(1P)不可用" ,
208+ } ;
209+ private static readonly Dictionary < string , string > FaultLED2P = new ( )
210+ {
211+ [ "jp" ] = "LED(2P)はご利用いただけません" ,
212+ [ "en" ] = "LED(2P) not available" ,
213+ [ "zh" ] = "LED(2P)不可用" ,
214+ } ;
215+ private static readonly Dictionary < string , string > FaultQR1P = new ( )
216+ {
217+ [ "jp" ] = "コードリーダー(1P)はご利用いただけません" ,
218+ [ "en" ] = "Code Reader (1P) not available" ,
219+ [ "zh" ] = "DX Pass 二维码相机(1P)不可用" , // This thing does not exist...
220+ } ;
221+ private static readonly Dictionary < string , string > FaultQR2P = new ( )
222+ {
223+ [ "jp" ] = "コードリーダー(2P)はご利用いただけません" ,
224+ [ "en" ] = "Code Reader (2P) not available" ,
225+ [ "zh" ] = "DX Pass 二维码相机(2P)不可用" , // This thing does not exist...
226+ } ;
227+ private static readonly Dictionary < string , string > FaultPlayerCamera = new ( )
228+ {
229+ [ "jp" ] = "プレイヤーカメラはご利用いただけません" ,
230+ [ "en" ] = "Player Camera not available" ,
231+ [ "zh" ] = "玩家相机不可用" ,
232+ } ;
233+ private static readonly Dictionary < string , string > FaultChime = new ( )
234+ {
235+ [ "jp" ] = "コードリーダーは(Chime)ご利用いただけません" , // This thing does not exist...
236+ [ "en" ] = "Code Reader (Chime) not available" , // This thing does not exist...
237+ [ "zh" ] = "二维码相机不可用" ,
238+ } ;
239+ }
0 commit comments