@@ -25,7 +25,7 @@ HMODULE thisModule;
2525
2626// Fix details
2727std::string sFixName = " FastLanesBowlingWidescreenFix" ;
28- std::string sFixVersion = " 1.0 " ;
28+ std::string sFixVersion = " 1.1 " ;
2929std::filesystem::path sFixPath ;
3030
3131// Ini
@@ -40,56 +40,42 @@ std::string sExeName;
4040
4141// Ini variables
4242bool bFixActive;
43- int iCurrentResX;
44- int iCurrentResY;
4543float fFOVFactor ;
4644
4745// Constants
4846constexpr float fOldAspectRatio = 4 .0f / 3 .0f ;
47+ constexpr float fOriginalCameraFOV = 0 .4f ;
4948
5049// Variables
5150float fNewAspectRatio ;
5251float fAspectRatioScale ;
5352float fNewAspectRatio4 ;
54- float fNewCameraFOV1 ;
55- float fNewCameraFOV2 ;
56- float fNewCameraFOV3 ;
57- float fNewCameraFOV4 ;
58- float fNewCameraFOV5 ;
53+ float fNewCameraFOV ;
5954
6055// Game detection
6156enum class Game
6257{
58+ FLB_RESCONFIG,
6359 FLB_DX9,
6460 FLB_DX8,
6561 Unknown
6662};
6763
68- enum ResolutionInstructionsScansIndices
69- {
70- RendererResScan,
71- Res2Scan,
72- Res3Scan,
73- Res4Scan,
74- Res5Scan,
75- Res6Scan
76- };
77-
7864enum AspectRatioInstructionsScansIndices
7965{
80- AR1Scan ,
81- AR2Scan ,
82- AR3Scan ,
83- AR4Scan
66+ AR1 ,
67+ AR2 ,
68+ AR3 ,
69+ AR4
8470};
8571
8672enum CameraFOVInstructionsScansIndices
8773{
88- FOV1Scan ,
89- FOV2Scan ,
90- FOV3Scan ,
91- FOV4Scan ,
92- FOV5Scan
74+ FOV1 ,
75+ FOV2 ,
76+ FOV3 ,
77+ FOV4 ,
78+ FOV5
9379};
9480
9581struct GameInfo
@@ -99,6 +85,7 @@ struct GameInfo
9985};
10086
10187const std::map<Game, GameInfo> kGames = {
88+ {Game::FLB_RESCONFIG, {" Fast Lanes Bowling (Resolution Configuration)" , " reschange.exe" }},
10289 {Game::FLB_DX9, {" Fast Lanes Bowling (DX9)" , " FastLanesDX9Test.exe" }},
10390 {Game::FLB_DX8, {" Fast Lanes Bowling (DX8)" , " pcbowl.exe" }}
10491};
@@ -180,25 +167,9 @@ void Configuration()
180167 spdlog_confparse (bFixActive);
181168
182169 // Load resolution from ini
183- inipp::get_value (ini.sections [" Settings" ], " Width" , iCurrentResX);
184- inipp::get_value (ini.sections [" Settings" ], " Height" , iCurrentResY);
185170 inipp::get_value (ini.sections [" Settings" ], " FOVFactor" , fFOVFactor );
186- spdlog_confparse (iCurrentResX);
187- spdlog_confparse (iCurrentResY);
188171 spdlog_confparse (fFOVFactor );
189172
190- // If resolution not specified, use desktop resolution
191- if (iCurrentResX <= 0 || iCurrentResY <= 0 )
192- {
193- spdlog::info (" Resolution not specified in ini file. Using desktop resolution." );
194- // Implement Util::GetPhysicalDesktopDimensions() accordingly
195- auto desktopDimensions = Util::GetPhysicalDesktopDimensions ();
196- iCurrentResX = desktopDimensions.first ;
197- iCurrentResY = desktopDimensions.second ;
198- spdlog_confparse (iCurrentResX);
199- spdlog_confparse (iCurrentResY);
200- }
201-
202173 spdlog::info (" ----------" );
203174}
204175
@@ -220,186 +191,120 @@ bool DetectGame()
220191 return false ;
221192}
222193
223- static SafetyHookMid RendererResolutionInstructionsHook{};
224- static SafetyHookMid ResolutionInstructions2Hook{};
225- static SafetyHookMid ResolutionInstructions3Hook{};
226- static SafetyHookMid ResolutionInstructions5Hook{};
227- static SafetyHookMid ResolutionWidth6Hook{};
228- static SafetyHookMid ResolutionHeight6Hook{};
194+ static SafetyHookMid ResolutionInstructionsHook{};
229195
230- void WidescreenFix ()
196+ void SetARAndFOV ()
231197{
232- if (bFixActive == true && (eGameType == Game::FLB_DX9 || eGameType == Game::FLB_DX8))
198+ std::vector<std::uint8_t *> AspectRatioInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" ,
199+ " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" , " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" ,
200+ " C7 44 24 ?? ?? ?? ?? ?? C7 44 24 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 8B 11" );
201+ if (Memory::AreAllSignaturesValid (AspectRatioInstructionsScansResult) == true )
233202 {
234- fNewAspectRatio = static_cast < float >(iCurrentResX) / static_cast < float >(iCurrentResY );
203+ spdlog::info ( " Aspect Ratio Instruction 1: Address is {:s}+{:x} " , sExeName . c_str (), AspectRatioInstructionsScansResult[AR1] - (std:: uint8_t *)exeModule );
235204
236- fAspectRatioScale = fNewAspectRatio / fOldAspectRatio ;
205+ spdlog::info ( " Aspect Ratio Instruction 2: Address is {:s}+{:x} " , sExeName . c_str (), AspectRatioInstructionsScansResult[AR2] - (std:: uint8_t *)exeModule) ;
237206
238- std::vector< std::uint8_t *> ResolutionInstructionsScansResult ;
207+ spdlog::info ( " Aspect Ratio Instruction 3: Address is {:s}+{:x} " , sExeName . c_str (), AspectRatioInstructionsScansResult[AR3] - ( std::uint8_t *)exeModule) ;
239208
240- if (eGameType == Game::FLB_DX9)
241- {
242- ResolutionInstructionsScansResult = Memory::PatternScan (exeModule, " 89 0D ?? ?? ?? ?? 89 15 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? EB" , " 8B 48 ?? 8B 50 ?? 89 2D" , " 8B 44 24 ?? 8B 4C 24 ?? 83 C4 ?? F6 C2" , " 39 4C 24 ?? 75 ?? A1" , " A3 ?? ?? ?? ?? 89 0D ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 8B 15" , " 8B 14 ?? 8B 8C 24" );
243- }
244- else if (eGameType == Game::FLB_DX8)
245- {
246- ResolutionInstructionsScansResult = Memory::PatternScan (exeModule, " 89 0D ?? ?? ?? ?? 89 15 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? EB" , " 8B 48 ?? 8B 50 ?? 89 2D" , " 8B 7C 24 ?? 8B 6C 24 ?? 89 3D" , " 3B F9 0F 85 ?? ?? ?? ?? A1" , " 89 3D ?? ?? ?? ?? 89 2D ?? ?? ?? ?? EB" , " 8B 14 ?? 8B 8C 24" );
247- }
248-
249- if (Memory::AreAllSignaturesValid (ResolutionInstructionsScansResult) == true )
250- {
251- spdlog::info (" Renderer Resolution Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionInstructionsScansResult[RendererResScan] - (std::uint8_t *)exeModule);
252-
253- spdlog::info (" Resolution 2 Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionInstructionsScansResult[Res2Scan] - (std::uint8_t *)exeModule);
209+ spdlog::info (" Aspect Ratio Instruction 4: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionsScansResult[AR4] - (std::uint8_t *)exeModule);
254210
255- spdlog::info ( " Resolution 3 Scan: Address is {:s}+{:x} " , sExeName . c_str (), ResolutionInstructionsScansResult[Res3Scan] - (std:: uint8_t *)exeModule );
211+ Memory::Write (AspectRatioInstructionsScansResult, AR1, AR3, 1 , fNewAspectRatio );
256212
257- spdlog::info ( " Resolution 4 Scan: Address is {:s}+{:x} " , sExeName . c_str (), ResolutionInstructionsScansResult[Res4Scan] - (std:: uint8_t *)exeModule) ;
213+ fNewAspectRatio4 = 0 . 4f * fAspectRatioScale ;
258214
259- spdlog::info (" Resolution 5 Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionInstructionsScansResult[Res5Scan] - (std::uint8_t *)exeModule);
260-
261- spdlog::info (" Resolution 6 Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionInstructionsScansResult[Res6Scan] - (std::uint8_t *)exeModule);
262-
263- // Renderer Resolution
264- RendererResolutionInstructionsHook = safetyhook::create_mid (ResolutionInstructionsScansResult[RendererResScan], [](SafetyHookContext& ctx)
265- {
266- ctx.ecx = std::bit_cast<uintptr_t >(iCurrentResX);
215+ Memory::Write (AspectRatioInstructionsScansResult[AR4] + 4 , fNewAspectRatio4 );
216+ }
267217
268- ctx.edx = std::bit_cast<uintptr_t >(iCurrentResY);
269- });
270-
271- // Resolution 2
272- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res2Scan], 6 );
218+ std::vector<std::uint8_t *> CameraFOVInstructionsScansResult;
273219
274- ResolutionInstructions2Hook = safetyhook::create_mid (ResolutionInstructionsScansResult[Res2Scan], [](SafetyHookContext& ctx)
275- {
276- ctx.ecx = std::bit_cast<uintptr_t >(iCurrentResX);
220+ if (eGameType == Game::FLB_DX9)
221+ {
222+ CameraFOVInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" ,
223+ " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" );
224+ }
225+ else if (eGameType == Game::FLB_DX8)
226+ {
227+ CameraFOVInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" ,
228+ " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" , " 68 ?? ?? ?? ?? 51 50 E8 ?? ?? ?? ?? 83 C4 ?? 8B 15" , " 68 ?? ?? ?? ?? 51 50 E8 ?? ?? ?? ?? 83 C4 ?? 39 74 24" );
229+ }
277230
278- ctx.edx = std::bit_cast<uintptr_t >(iCurrentResY);
279- });
231+ if (Memory::AreAllSignaturesValid (CameraFOVInstructionsScansResult) == true )
232+ {
233+ spdlog::info (" Camera FOV Instruction 1: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV1] - (std::uint8_t *)exeModule);
280234
281- ResolutionInstructions3Hook = safetyhook::create_mid (ResolutionInstructionsScansResult[Res3Scan], [](SafetyHookContext& ctx)
282- {
283- if (eGameType == Game::FLB_DX9)
284- {
285- Memory::ReadMem (ctx.esp + 0x40 ) = iCurrentResX;
235+ spdlog::info (" Camera FOV Instruction 2: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV2] - (std::uint8_t *)exeModule);
286236
287- Memory::ReadMem (ctx.esp + 0x44 ) = iCurrentResY;
288- }
289- else if (eGameType == Game::FLB_DX8)
290- {
291- Memory::ReadMem (ctx.esp + 0x10 ) = iCurrentResX;
237+ spdlog::info (" Camera FOV Instruction 3: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV3] - (std::uint8_t *)exeModule);
292238
293- Memory::ReadMem (ctx.esp + 0x14 ) = iCurrentResY;
294- }
295- });
239+ fNewCameraFOV = fOriginalCameraFOV * fAspectRatioScale ;
296240
297- if (eGameType == Game::FLB_DX9)
298- {
299- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res4Scan], 6 );
241+ Memory::Write (CameraFOVInstructionsScansResult[FOV1] + 1 , fNewCameraFOV );
300242
301- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res4Scan] + 11 , 6 );
302- }
303- else if (eGameType == Game::FLB_DX8)
304- {
305- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res4Scan], 8 );
243+ Memory::Write (CameraFOVInstructionsScansResult[FOV2] + 1 , fNewCameraFOV * fFOVFactor );
306244
307- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res4Scan] + 13 , 4 );
308- }
245+ Memory::Write (CameraFOVInstructionsScansResult[FOV3] + 1 , fNewCameraFOV );
309246
310- ResolutionInstructions5Hook = safetyhook::create_mid (ResolutionInstructionsScansResult[Res5Scan], [](SafetyHookContext& ctx)
311- {
312- if (eGameType == Game::FLB_DX9)
313- {
314- ctx.eax = std::bit_cast<uintptr_t >(iCurrentResX);
247+ if (eGameType == Game::FLB_DX8)
248+ {
249+ spdlog::info (" Camera FOV Instruction 4: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV4] - (std::uint8_t *)exeModule);
315250
316- ctx.ecx = std::bit_cast<uintptr_t >(iCurrentResY);
317- }
318- else if (eGameType == Game::FLB_DX8)
319- {
320- ctx.edi = std::bit_cast<uintptr_t >(iCurrentResX);
251+ spdlog::info (" Camera FOV Instruction 5: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV5] - (std::uint8_t *)exeModule);
321252
322- ctx.ebp = std::bit_cast<uintptr_t >(iCurrentResY);
323- }
324- });
253+ Memory::Write (CameraFOVInstructionsScansResult[FOV4] + 1 , fNewCameraFOV );
325254
326- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res6Scan], 3 );
255+ Memory::Write (CameraFOVInstructionsScansResult[FOV5] + 1 , fNewCameraFOV );
256+ }
257+ }
258+ }
327259
328- ResolutionWidth6Hook = safetyhook::create_mid (ResolutionInstructionsScansResult[Res6Scan], [](SafetyHookContext& ctx)
260+ void WidescreenFix ()
261+ {
262+ if (bFixActive == true )
263+ {
264+ if (eGameType == Game::FLB_RESCONFIG)
265+ {
266+ std::uint8_t * ResolutionListUnlockScanResult = Memory::PatternScan (exeModule, " 81 F9 ?? ?? ?? ?? 7C ?? 83 7C 24" );
267+ if (ResolutionListUnlockScanResult)
329268 {
330- ctx.edx = std::bit_cast<uintptr_t >(iCurrentResX);
331- });
269+ spdlog::info (" Resolution List Unlock Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionListUnlockScanResult - (std::uint8_t *)exeModule);
332270
333- Memory::WriteNOPs (ResolutionInstructionsScansResult[Res6Scan] + 18 , 4 );
334-
335- ResolutionHeight6Hook = safetyhook::create_mid (ResolutionInstructionsScansResult[Res6Scan] + 18 , [](SafetyHookContext& ctx)
271+ Memory::WriteNOPs (ResolutionListUnlockScanResult, 40 );
272+ }
273+ else
336274 {
337- ctx.edx = std::bit_cast<uintptr_t >(iCurrentResY);
338- });
339- }
340-
341- std::vector<std::uint8_t *> AspectRatioInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" , " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" , " 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" , " C7 44 24 ?? ?? ?? ?? ?? C7 44 24 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 8B 11" );
342- if (Memory::AreAllSignaturesValid (AspectRatioInstructionsScansResult) == true )
343- {
344- spdlog::info (" Aspect Ratio Instruction 1: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionsScansResult[AR1Scan] - (std::uint8_t *)exeModule);
345-
346- spdlog::info (" Aspect Ratio Instruction 2: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionsScansResult[AR2Scan] - (std::uint8_t *)exeModule);
347-
348- spdlog::info (" Aspect Ratio Instruction 3: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionsScansResult[AR3Scan] - (std::uint8_t *)exeModule);
349-
350- spdlog::info (" Aspect Ratio Instruction 4: Address is {:s}+{:x}" , sExeName .c_str (), AspectRatioInstructionsScansResult[AR4Scan] - (std::uint8_t *)exeModule);
351-
352- Memory::Write (AspectRatioInstructionsScansResult[AR1Scan] + 1 , fNewAspectRatio );
353-
354- Memory::Write (AspectRatioInstructionsScansResult[AR2Scan] + 1 , fNewAspectRatio );
355-
356- Memory::Write (AspectRatioInstructionsScansResult[AR3Scan] + 1 , fNewAspectRatio );
357-
358- fNewAspectRatio4 = 0 .4f * fAspectRatioScale ;
359-
360- Memory::Write (AspectRatioInstructionsScansResult[AR4Scan] + 4 , fNewAspectRatio4 );
275+ spdlog::error (" Failed to find resolution list unlock scan memory address." );
276+ return ;
277+ }
361278 }
362279
363- std::vector<std::uint8_t *> CameraFOVInstructionsScansResult;
364-
365- if (eGameType == Game::FLB_DX9)
280+ if (eGameType == Game::FLB_DX9 || eGameType == Game::FLB_DX8)
366281 {
367- CameraFOVInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" );
368- }
369- else if (eGameType == Game::FLB_DX8)
370- {
371- CameraFOVInstructionsScansResult = Memory::PatternScan (exeModule, " 68 ?? ?? ?? ?? 8D 4C 24 ?? C7 44 24" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 33 F6" , " 68 ?? ?? ?? ?? 8D 54 24 ?? 89 44 24 ?? 8B 47 ?? 52 50" , " 68 ?? ?? ?? ?? 51 50 E8 ?? ?? ?? ?? 83 C4 ?? 8B 15" , " 68 ?? ?? ?? ?? 51 50 E8 ?? ?? ?? ?? 83 C4 ?? 39 74 24" );
372- }
373-
374- if (Memory::AreAllSignaturesValid (CameraFOVInstructionsScansResult) == true )
375- {
376- spdlog::info (" Camera FOV Instruction 1: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV1Scan] - (std::uint8_t *)exeModule);
377-
378- spdlog::info (" Camera FOV Instruction 2: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV2Scan] - (std::uint8_t *)exeModule);
379-
380- spdlog::info (" Camera FOV Instruction 3: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV3Scan] - (std::uint8_t *)exeModule);
282+ std::uint8_t * ResolutionInstructionsScanResult = Memory::PatternScan (exeModule, " 89 0D ?? ?? ?? ?? 89 15 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? EB" );
283+ if (ResolutionInstructionsScanResult)
284+ {
285+ spdlog::info (" Resolution Instructions Scan: Address is {:s}+{:x}" , sExeName .c_str (), ResolutionInstructionsScanResult - (std::uint8_t *)exeModule);
381286
382- fNewCameraFOV1 = 0 .4f * fAspectRatioScale ;
287+ ResolutionInstructionsHook = safetyhook::create_mid (ResolutionInstructionsScanResult, [](SafetyHookContext& ctx)
288+ {
289+ const int & iCurrentWidth = Memory::ReadRegister (ctx.ecx );
383290
384- fNewCameraFOV2 = 0 . 4f * fAspectRatioScale * fFOVFactor ;
291+ const int & iCurrentHeight = Memory::ReadRegister (ctx. edx ) ;
385292
386- Memory::Write (CameraFOVInstructionsScansResult[FOV1Scan] + 1 , fNewCameraFOV1 );
293+ fNewAspectRatio = static_cast < float >(iCurrentWidth) / static_cast < float >(iCurrentHeight );
387294
388- Memory::Write (CameraFOVInstructionsScansResult[FOV2Scan] + 1 , fNewCameraFOV2 ) ;
295+ fAspectRatioScale = fNewAspectRatio / fOldAspectRatio ;
389296
390- Memory::Write (CameraFOVInstructionsScansResult[FOV3Scan] + 1 , fNewCameraFOV1 );
297+ SetARAndFOV ( );
391298
392- if (eGameType == Game::FLB_DX8)
299+ ResolutionInstructionsHook.disable ();
300+ });
301+ }
302+ else
393303 {
394- spdlog::info (" Camera FOV Instruction 4: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV4Scan] - (std::uint8_t *)exeModule);
395-
396- spdlog::info (" Camera FOV Instruction 5: Address is {:s}+{:x}" , sExeName .c_str (), CameraFOVInstructionsScansResult[FOV5Scan] - (std::uint8_t *)exeModule);
397-
398- Memory::Write (CameraFOVInstructionsScansResult[FOV4Scan] + 1 , fNewCameraFOV1 );
399-
400- Memory::Write (CameraFOVInstructionsScansResult[FOV5Scan] + 1 , fNewCameraFOV1 );
304+ spdlog::error (" Failed to find resolution instructions scan memory address." );
305+ return ;
401306 }
402- }
307+ }
403308 }
404309}
405310
0 commit comments