Skip to content

Commit 3f306df

Browse files
committed
Addendum to be10139: "Refactor Main.cpp (loader) with improved reliability and code quality"
1 parent be10139 commit 3f306df

File tree

1 file changed

+143
-92
lines changed

1 file changed

+143
-92
lines changed

Client/loader/Main.cpp

Lines changed: 143 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,63 @@
2323
#include <version.h>
2424
#include <memory>
2525
#include <algorithm>
26+
#include <cassert>
27+
28+
#if __cplusplus >= 201703L
29+
#define MAYBE_UNUSED [[maybe_unused]]
30+
#else
31+
#define MAYBE_UNUSED
32+
#endif
2633

2734
namespace {
28-
// Constants
35+
// Error codes enum for better maintainability
36+
enum ErrorCode : int {
37+
ERROR_NULL_INSTANCE = -1,
38+
ERROR_NULL_INSTALL_MANAGER = -2,
39+
ERROR_LAUNCH_EXCEPTION = -3,
40+
ERROR_INSTALL_CONTINUE = -4
41+
};
42+
43+
// Command line constants
2944
constexpr size_t MAX_CMD_LINE_LENGTH = 4096;
30-
constexpr int ERROR_NULL_INSTANCE = -1;
31-
constexpr int ERROR_NULL_INSTALL_MANAGER = -2;
32-
constexpr int ERROR_LAUNCH_EXCEPTION = -3;
33-
constexpr int ERROR_INSTALL_CONTINUE = -4;
3445

3546
// Report log IDs
3647
constexpr int LOG_ID_END = 1044;
3748
constexpr int LOG_ID_CONTINUE_EXCEPTION = 1045;
3849
constexpr int LOG_ID_LAUNCH_EXCEPTION = 1046;
50+
51+
// Compile-time checks
52+
static_assert(MAX_CMD_LINE_LENGTH > 0, "Command line buffer size must be positive");
53+
static_assert(MAX_CMD_LINE_LENGTH <= 65536, "Command line buffer size seems unreasonably large");
54+
static_assert(sizeof(DWORD) >= sizeof(int), "DWORD must be at least as large as int");
3955

4056
class Utf8FileHooksGuard {
57+
private:
58+
bool m_released = false;
59+
4160
public:
42-
Utf8FileHooksGuard() { AddUtf8FileHooks(); }
43-
~Utf8FileHooksGuard() { RemoveUtf8FileHooks(); }
61+
Utf8FileHooksGuard() {
62+
AddUtf8FileHooks();
63+
}
64+
65+
~Utf8FileHooksGuard() noexcept {
66+
if (!m_released) {
67+
RemoveUtf8FileHooks();
68+
}
69+
}
70+
71+
// Called when we want to keep hooks active (early return with error)
72+
void release() noexcept {
73+
m_released = true;
74+
}
75+
76+
// Called when we want to remove hooks early (on GetInstallManager failure)
77+
void removeNow() noexcept {
78+
if (!m_released) {
79+
RemoveUtf8FileHooks();
80+
m_released = true;
81+
}
82+
}
4483

4584
// Disable copy and move
4685
Utf8FileHooksGuard(const Utf8FileHooksGuard&) = delete;
@@ -49,8 +88,17 @@ namespace {
4988
Utf8FileHooksGuard& operator=(Utf8FileHooksGuard&&) = delete;
5089
};
5190

52-
inline void SafeCopyCommandLine(LPSTR lpCmdLine, char* safeCmdLine, size_t bufferSize) {
53-
if (!lpCmdLine || !safeCmdLine || bufferSize == 0) {
91+
inline void SafeCopyCommandLine(LPSTR lpCmdLine, char* safeCmdLine, size_t bufferSize) noexcept {
92+
// Preconditions (only in debug builds)
93+
assert(safeCmdLine != nullptr && "Destination buffer must not be null");
94+
assert(bufferSize > 0 && "Buffer size must be positive");
95+
96+
if (!safeCmdLine || bufferSize == 0) {
97+
return;
98+
}
99+
100+
// If source is null, destination remains zero-initialized
101+
if (!lpCmdLine) {
54102
return;
55103
}
56104

@@ -61,7 +109,6 @@ namespace {
61109
safeCmdLine[cmdLineLen] = '\0';
62110
}
63111

64-
65112
inline DWORD GetSafeProcessId() noexcept {
66113
try {
67114
return GetCurrentProcessId();
@@ -71,67 +118,28 @@ namespace {
71118
}
72119
}
73120

74-
bool PerformInitialization(const char* safeCmdLine) {
121+
CInstallManager* PerformEarlyInitialization(const char* safeCmdLine) {
75122
auto* pInstallManager = GetInstallManager();
76123
if (!pInstallManager) {
77-
return false;
124+
return nullptr;
78125
}
79126

80-
// Configure install manager
127+
// Let install manager figure out what MTASA path to use
81128
pInstallManager->SetMTASAPathSource(safeCmdLine);
82129

83-
// Initialize logging
130+
// Start logging.....now
84131
BeginEventLog();
85132

86-
// Start localization (non-critical, continue on failure)
133+
// Start localization if possible
87134
InitLocalization(false);
88135

89-
// Handle installer commands
136+
// Handle commands from the installer
90137
HandleSpecialLaunchOptions();
91138

92-
// Ensure single instance
139+
// Check MTA is launched only once
93140
HandleDuplicateLaunching();
94141

95-
// Clear any pending operations
96-
ClearPendingBrowseToSolution();
97-
98-
// Validate GTA installation
99-
ValidateGTAPath();
100-
101-
return true;
102-
}
103-
104-
void PerformPreLaunchSetup(HINSTANCE hInstance) {
105-
// Ensure localization is fully initialized
106-
InitLocalization(true);
107-
108-
// Initialize monitoring systems
109-
PreLaunchWatchDogs();
110-
111-
// Handle custom configurations
112-
HandleCustomStartMessage();
113-
114-
#if !defined(MTA_DEBUG) && MTASA_VERSION_TYPE != VERSION_TYPE_CUSTOM
115-
ForbodenProgramsMessage();
116-
#endif
117-
118-
// Maintenance operations
119-
CycleEventLog();
120-
BsodDetectionPreLaunch();
121-
MaybeShowCopySettingsDialog();
122-
123-
// Check for conflicts
124-
HandleIfGTAIsAlreadyRunning();
125-
126-
// Maybe warn user if no anti-virus running
127-
CheckAntiVirusStatus();
128-
129-
// Show splash screen
130-
ShowSplash(hInstance);
131-
132-
// Verify integrity
133-
CheckDataFiles();
134-
CheckLibVersions();
142+
return pInstallManager;
135143
}
136144

137145
SString ContinueUpdateProcedure(CInstallManager* pInstallManager) {
@@ -148,13 +156,14 @@ namespace {
148156
}
149157
}
150158

159+
// Launch the game with exception handling
151160
int LaunchGameSafely(const SString& strCmdLine) {
152161
try {
153162
return LaunchGame(strCmdLine);
154163
}
155164
catch (...) {
156165
AddReportLog(LOG_ID_LAUNCH_EXCEPTION, "Exception in LaunchGame()");
157-
return ERROR_LAUNCH_EXCEPTION;
166+
return static_cast<int>(ERROR_LAUNCH_EXCEPTION);
158167
}
159168
}
160169
}
@@ -171,64 +180,106 @@ namespace {
171180
// (Which may then call it again as admin)
172181
//
173182
///////////////////////////////////////////////////////////////
174-
MTAEXPORT int DoWinMain(HINSTANCE hLauncherInstance, HINSTANCE hPrevInstance,
175-
LPSTR lpCmdLine, int nCmdShow)
183+
MTAEXPORT int DoWinMain(HINSTANCE hLauncherInstance, MAYBE_UNUSED HINSTANCE hPrevInstance,
184+
LPSTR lpCmdLine, MAYBE_UNUSED int nCmdShow)
176185
{
177-
// Validate critical parameters
186+
// Silence unused parameter warnings for older compilers
187+
#if __cplusplus < 201703L
188+
(void)hPrevInstance;
189+
(void)nCmdShow;
190+
#endif
191+
192+
// Check for null parameters before use
178193
if (!hLauncherInstance) {
179-
return ERROR_NULL_INSTANCE;
194+
return static_cast<int>(ERROR_NULL_INSTANCE);
180195
}
181196

197+
char safeCmdLine[MAX_CMD_LINE_LENGTH] = {0};
198+
SafeCopyCommandLine(lpCmdLine, safeCmdLine, sizeof(safeCmdLine));
199+
182200
// RAII guard for UTF8 file hooks
183201
Utf8FileHooksGuard utf8Guard;
184202

185-
// Run debug tests if in debug mode
186203
#if defined(MTA_DEBUG)
187204
SharedUtil_Tests();
188205
#endif
189206

190-
// Prepare safe command line buffer
191-
char safeCmdLine[MAX_CMD_LINE_LENGTH] = {0};
192-
SafeCopyCommandLine(lpCmdLine, safeCmdLine, sizeof(safeCmdLine));
193-
194207
//
195-
// Initialization Phase
208+
// Init
196209
//
197-
if (!PerformInitialization(safeCmdLine)) {
198-
return ERROR_NULL_INSTALL_MANAGER;
210+
211+
auto* pInstallManager = PerformEarlyInitialization(safeCmdLine);
212+
if (!pInstallManager) {
213+
// Remove hooks when install manager fails
214+
utf8Guard.removeNow();
215+
return static_cast<int>(ERROR_NULL_INSTALL_MANAGER);
199216
}
200217

201-
// Show initial splash screen
202-
ShowSplash(hLauncherInstance);
218+
HINSTANCE hInstanceToUse = hLauncherInstance;
219+
220+
// Show logo
221+
ShowSplash(hInstanceToUse);
203222

204-
//
205-
// Update Phase
206-
//
207-
auto* pInstallManager = GetInstallManager();
208-
const SString strCmdLine = ContinueUpdateProcedure(pInstallManager);
223+
// Other init stuff
224+
ClearPendingBrowseToSolution();
209225

210-
//
211-
// Pre-Launch Phase
212-
//
213-
PerformPreLaunchSetup(hLauncherInstance);
226+
// Find GTA path to use
227+
ValidateGTAPath();
228+
229+
230+
// Continue any update procedure
231+
SString strCmdLine = ContinueUpdateProcedure(pInstallManager);
232+
233+
// Ensure localization is started
234+
InitLocalization(true);
235+
236+
// Setup/test various counters and flags for monitoring problems
237+
PreLaunchWatchDogs();
238+
239+
// Stuff
240+
HandleCustomStartMessage();
241+
242+
#if !defined(MTA_DEBUG) && MTASA_VERSION_TYPE != VERSION_TYPE_CUSTOM
243+
ForbodenProgramsMessage();
244+
#endif
245+
246+
CycleEventLog();
247+
BsodDetectionPreLaunch();
248+
MaybeShowCopySettingsDialog();
249+
250+
// Make sure GTA is not running
251+
HandleIfGTAIsAlreadyRunning();
252+
253+
// Maybe warn user if no anti-virus running
254+
CheckAntiVirusStatus();
255+
256+
// Ensure logo is showing
257+
ShowSplash(hInstanceToUse);
258+
259+
// Check MTA files look good
260+
CheckDataFiles();
261+
CheckLibVersions();
262+
263+
// Go for launch
264+
// Initialize return code with safe default
265+
int iReturnCode = 0;
266+
iReturnCode = LaunchGameSafely(strCmdLine);
214267

215-
//
216-
// Launch Phase
217-
//
218-
const int iReturnCode = LaunchGameSafely(strCmdLine);
219-
220-
// Post-launch monitoring
221268
PostRunWatchDogs(iReturnCode);
222269

223270
//
224-
// Cleanup Phase
271+
// Quit
225272
//
273+
226274
HandleOnQuitCommand();
275+
276+
// Maybe show help if trouble was encountered
227277
ProcessPendingBrowseToSolution();
228278

229-
// Log termination details
230-
const DWORD currentPid = GetSafeProcessId();
231-
AddReportLog(LOG_ID_END, SString("* End (0x%X)* pid:%d", iReturnCode, currentPid));
279+
// Get current process ID for logging
280+
DWORD currentPid = GetSafeProcessId();
232281

282+
AddReportLog(LOG_ID_END, SString("* End (0x%X)* pid:%d", iReturnCode, currentPid));
283+
233284
return iReturnCode;
234-
}
285+
}

0 commit comments

Comments
 (0)