@@ -31,58 +31,76 @@ extern "C" {
3131#include < shellapi.h> // CommandLineToArgvW()
3232#include < appnotify.h>
3333
34- // Pop up an out of memory message, returns to Windows
35- static BOOL OutOfMemory (void )
34+ static int OutOfMemory (void )
3635{
3736 SDL_ShowSimpleMessageBox (SDL_MESSAGEBOX_ERROR, " Fatal Error" , " Out of memory - aborting" , NULL );
38- return FALSE ;
37+ return -1 ;
38+ }
39+
40+ static int ErrorProcessingCommandLine (void )
41+ {
42+ SDL_ShowSimpleMessageBox (SDL_MESSAGEBOX_ERROR, " Fatal Error" , " Error processing command line arguments" , NULL );
43+ return -1 ;
3944}
4045
41- /* Gets the arguments with GetCommandLine, converts them to argc and argv
42- and calls SDL_main */
4346extern " C"
44- int SDL_RunApp (int , char ** , SDL_main_func mainFunction, void *)
47+ int SDL_RunApp (int caller_argc , char * caller_argv[] , SDL_main_func mainFunction, void *)
4548{
46- LPWSTR *argvw ;
47- char **argv ;
48- int i, argc, result ;
49+ int result, argc ;
50+ LPWSTR *argvw = NULL ;
51+ char **argv = NULL ;
4952 HRESULT hr;
5053 XTaskQueueHandle taskQueue;
5154
52- argvw = CommandLineToArgvW (GetCommandLineW (), &argc);
53- if (argvw == NULL ) {
54- return OutOfMemory ();
55- }
55+ // Note that we need to be careful about how we allocate/free memory in this function. If the application calls
56+ // SDL_SetMemoryFunctions(), we can't rely on SDL_free() to use the same allocator after SDL_main() returns.
5657
57- /* Note that we need to be careful about how we allocate/free memory here.
58- * If the application calls SDL_SetMemoryFunctions(), we can't rely on
59- * SDL_free() to use the same allocator after SDL_main() returns.
60- */
58+ if (!caller_argv || caller_argc < 0 ) {
59+ // If the passed argv is NULL or argc is negative, the user expects SDL to get the command line arguments
60+ // using GetCommandLineW() and convert them to argc and argv before calling mainFunction().
6161
62- // Parse it into argv and argc
63- argv = (char **)HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, (argc + 1 ) * sizeof (*argv));
64- if (argv == NULL ) {
65- return OutOfMemory ();
66- }
67- for (i = 0 ; i < argc; ++i) {
68- const int utf8size = WideCharToMultiByte (CP_UTF8, 0 , argvw[i], -1 , NULL , 0 , NULL , NULL );
69- if (!utf8size) { // uhoh?
70- SDL_ShowSimpleMessageBox (SDL_MESSAGEBOX_ERROR, " Fatal Error" , " Error processing command line arguments" , NULL );
71- return -1 ;
62+ // Because of how the Windows command line works, we know for sure that the buffer size required to store all
63+ // argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal to the
64+ // size of the original command line string converted to UTF-8.
65+ const int argdata_size = WideCharToMultiByte (CP_UTF8, 0 , GetCommandLineW (), -1 , NULL , 0 , NULL , NULL ); // Includes the null terminator
66+ if (!argdata_size) {
67+ result = ErrorProcessingCommandLine ();
68+ goto cleanup;
7269 }
7370
74- argv[i] = (char *)HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, utf8size); // this size includes the null-terminator character.
75- if (!argv[i]) {
76- return OutOfMemory ();
71+ argvw = CommandLineToArgvW (GetCommandLineW (), &argc);
72+ if (!argvw || argc < 0 ) {
73+ result = OutOfMemory ();
74+ goto cleanup;
7775 }
7876
79- if (WideCharToMultiByte (CP_UTF8, 0 , argvw[i], -1 , argv[i], utf8size, NULL , NULL ) == 0 ) { // failed? uhoh!
80- SDL_ShowSimpleMessageBox (SDL_MESSAGEBOX_ERROR, " Fatal Error" , " Error processing command line arguments" , NULL );
81- return -1 ;
77+ // Allocate argv followed by the argument string buffer as one contiguous allocation.
78+ argv = (char **)HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, (argc + 1 ) * sizeof (*argv) + argdata_size);
79+ if (!argv) {
80+ result = OutOfMemory ();
81+ goto cleanup;
82+ }
83+ char *argdata = ((char *)argv) + (argc + 1 ) * sizeof (*argv);
84+ int argdata_index = 0 ;
85+
86+ for (int i = 0 ; i < argc; ++i) {
87+ const int bytes_written = WideCharToMultiByte (CP_UTF8, 0 , argvw[i], -1 , argdata + argdata_index, argdata_size - argdata_index, NULL , NULL );
88+ if (!bytes_written) {
89+ result = ErrorProcessingCommandLine ();
90+ goto cleanup;
91+ }
92+ argv[i] = argdata + argdata_index;
93+ argdata_index += bytes_written;
8294 }
95+ argv[argc] = NULL ;
96+
97+ argvw = NULL ;
98+
99+ caller_argc = argc;
100+ caller_argv = argv;
83101 }
84- argv[i] = NULL ;
85- LocalFree (argvw);
102+
103+ // !!! FIXME: This function does not currently properly deinitialize GDK resources on failure.
86104
87105 hr = XGameRuntimeInitialize ();
88106
@@ -100,6 +118,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
100118 xblArgs.queue = taskQueue;
101119 SDL_snprintf (scidBuffer, 64 , " 00000000-0000-0000-0000-0000%08X" , titleid);
102120 xblArgs.scid = scidBuffer;
121+ // !!! FIXME: XblInitialize() should have a corresponding call to XblCleanup() according to the docs.
103122 hr = XblInitialize (&xblArgs);
104123 } else {
105124 SDL_SetError (" [GDK] Unable to get titleid. Will not call XblInitialize. Check MicrosoftGame.config!" );
@@ -111,8 +130,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
111130 return -1 ;
112131 }
113132
114- // Run the application main() code
115- result = SDL_CallMain (argc, argv, mainFunction);
133+ result = mainFunction (caller_argc, caller_argv); // No need for SDL_CallMain(); we already know that we have a valid argv
116134
117135 GDK_UnregisterChangeNotifications ();
118136
@@ -138,12 +156,10 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
138156 result = -1 ;
139157 }
140158
141- // Free argv, to avoid memory leak
142- for (i = 0 ; i < argc; ++i) {
143- HeapFree (GetProcessHeap (), 0 , argv[i]);
144- }
159+ cleanup:
160+
145161 HeapFree (GetProcessHeap (), 0 , argv);
162+ LocalFree (argvw);
146163
147164 return result;
148165}
149-
0 commit comments