[BASESRV][NTVDM][TESTVDD] Improve the FILE header section. Brought to you by Adam...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / ntvdm.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/ntvdm.c
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15
16 #include "bios/bios.h"
17 #include "cpu/cpu.h"
18
19 #include "dos/dem.h"
20
21 #include "resource.h"
22
23 /* VARIABLES ******************************************************************/
24
25 static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
26 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
27 static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
28
29 // Command line of NTVDM
30 INT NtVdmArgc;
31 WCHAR** NtVdmArgv;
32
33
34 static HMENU hConsoleMenu = NULL;
35 static INT VdmMenuPos = -1;
36 static BOOLEAN ShowPointer = FALSE;
37
38 /*
39 * Those menu helpers were taken from the GUI frontend in winsrv.dll
40 */
41 typedef struct _VDM_MENUITEM
42 {
43 UINT uID;
44 const struct _VDM_MENUITEM *SubMenu;
45 WORD wCmdID;
46 } VDM_MENUITEM, *PVDM_MENUITEM;
47
48 static const VDM_MENUITEM VdmMenuItems[] =
49 {
50 { IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
51 { IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
52 { IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
53
54 { 0, NULL, 0 } /* End of list */
55 };
56
57 static const VDM_MENUITEM VdmMainMenuItems[] =
58 {
59 { -1, NULL, 0 }, /* Separator */
60 { IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* Hide mouse; can be renamed to Show mouse */
61 { IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
62
63 { 0, NULL, 0 } /* End of list */
64 };
65
66 static VOID
67 AppendMenuItems(HMENU hMenu,
68 const VDM_MENUITEM *Items)
69 {
70 UINT i = 0;
71 WCHAR szMenuString[255];
72 HMENU hSubMenu;
73
74 do
75 {
76 if (Items[i].uID != (UINT)-1)
77 {
78 if (LoadStringW(GetModuleHandle(NULL),
79 Items[i].uID,
80 szMenuString,
81 ARRAYSIZE(szMenuString)) > 0)
82 {
83 if (Items[i].SubMenu != NULL)
84 {
85 hSubMenu = CreatePopupMenu();
86 if (hSubMenu != NULL)
87 {
88 AppendMenuItems(hSubMenu, Items[i].SubMenu);
89
90 if (!AppendMenuW(hMenu,
91 MF_STRING | MF_POPUP,
92 (UINT_PTR)hSubMenu,
93 szMenuString))
94 {
95 DestroyMenu(hSubMenu);
96 }
97 }
98 }
99 else
100 {
101 AppendMenuW(hMenu,
102 MF_STRING,
103 Items[i].wCmdID,
104 szMenuString);
105 }
106 }
107 }
108 else
109 {
110 AppendMenuW(hMenu,
111 MF_SEPARATOR,
112 0,
113 NULL);
114 }
115 i++;
116 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
117 }
118
119 BOOL
120 VdmMenuExists(HMENU hConsoleMenu)
121 {
122 INT MenuPos, i;
123 MenuPos = GetMenuItemCount(hConsoleMenu);
124
125 /* Check for the presence of one of the VDM menu items */
126 for (i = 0; i <= MenuPos; i++)
127 {
128 if (GetMenuItemID(hConsoleMenu, i) == ID_SHOWHIDE_MOUSE)
129 {
130 /* Set VdmMenuPos to the position of the existing menu */
131 VdmMenuPos = i - 1;
132 return TRUE;
133 }
134 }
135 return FALSE;
136 }
137
138 /*static*/ VOID
139 CreateVdmMenu(HANDLE ConOutHandle)
140 {
141 hConsoleMenu = ConsoleMenuControl(ConOutHandle,
142 ID_SHOWHIDE_MOUSE,
143 ID_VDM_QUIT);
144 if (hConsoleMenu == NULL) return;
145
146 /* Get the position where we are going to insert our menu items */
147 VdmMenuPos = GetMenuItemCount(hConsoleMenu);
148
149 /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
150 if (!VdmMenuExists(hConsoleMenu))
151 {
152 AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
153 DrawMenuBar(GetConsoleWindow());
154 }
155 }
156
157 /*static*/ VOID
158 DestroyVdmMenu(VOID)
159 {
160 UINT i = 0;
161 const VDM_MENUITEM *Items = VdmMainMenuItems;
162
163 do
164 {
165 DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
166 i++;
167 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
168
169 DrawMenuBar(GetConsoleWindow());
170 }
171
172 static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
173 {
174 WCHAR szMenuString[255] = L"";
175
176 if (ShowPtr)
177 {
178 /* Be sure the cursor will be shown */
179 while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
180 }
181 else
182 {
183 /* Be sure the cursor will be hidden */
184 while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
185 }
186
187 if (LoadStringW(GetModuleHandle(NULL),
188 (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
189 szMenuString,
190 ARRAYSIZE(szMenuString)) > 0)
191 {
192 ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE,
193 MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
194 }
195 }
196
197 static VOID EnableExtraHardware(HANDLE ConsoleInput)
198 {
199 DWORD ConInMode;
200
201 if (GetConsoleMode(ConsoleInput, &ConInMode))
202 {
203 #if 0
204 // GetNumberOfConsoleMouseButtons();
205 // GetSystemMetrics(SM_CMOUSEBUTTONS);
206 // GetSystemMetrics(SM_MOUSEPRESENT);
207 if (MousePresent)
208 {
209 #endif
210 /* Support mouse input events if there is a mouse on the system */
211 ConInMode |= ENABLE_MOUSE_INPUT;
212 #if 0
213 }
214 else
215 {
216 /* Do not support mouse input events if there is no mouse on the system */
217 ConInMode &= ~ENABLE_MOUSE_INPUT;
218 }
219 #endif
220
221 SetConsoleMode(ConsoleInput, ConInMode);
222 }
223 }
224
225 /* PUBLIC FUNCTIONS ***********************************************************/
226
227 VOID
228 DisplayMessage(IN LPCWSTR Format, ...)
229 {
230 #ifndef WIN2K_COMPLIANT
231 WCHAR StaticBuffer[256];
232 LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
233 #else
234 WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
235 #endif
236 size_t MsgLen;
237 va_list Parameters;
238
239 va_start(Parameters, Format);
240
241 #ifndef WIN2K_COMPLIANT
242 /*
243 * Retrieve the message length and if it is too long, allocate
244 * an auxiliary buffer; otherwise use the static buffer.
245 * The string is built to be NULL-terminated.
246 */
247 MsgLen = _vscwprintf(Format, Parameters);
248 if (MsgLen >= ARRAYSIZE(StaticBuffer))
249 {
250 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
251 if (Buffer == NULL)
252 {
253 /* Allocation failed, use the static buffer and display a suitable error message */
254 Buffer = StaticBuffer;
255 Format = L"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
256 MsgLen = wcslen(Format);
257 }
258 }
259 #else
260 MsgLen = ARRAYSIZE(Buffer) - 1;
261 #endif
262
263 RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
264 _vsnwprintf(Buffer, MsgLen, Format, Parameters);
265
266 va_end(Parameters);
267
268 /* Display the message */
269 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
270 MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
271
272 #ifndef WIN2K_COMPLIANT
273 /* Free the buffer if needed */
274 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
275 #endif
276 }
277
278 static VOID
279 ConsoleCleanup(VOID);
280
281 static VOID
282 VdmShutdown(BOOLEAN Immediate)
283 {
284 /*
285 * Immediate = TRUE: Immediate shutdown;
286 * FALSE: Delayed shutdown.
287 */
288 BOOLEAN MustShutdown;
289
290 /* First notify DOS to see whether we can shut down now */
291 MustShutdown = DosShutdown(Immediate);
292 /*
293 * In case we perform an immediate shutdown, or the DOS says
294 * we can shut down, do it now.
295 */
296 MustShutdown = MustShutdown || Immediate;
297
298 if (MustShutdown)
299 {
300 EmulatorTerminate();
301
302 BiosCleanup();
303 EmulatorCleanup();
304 ConsoleCleanup();
305
306 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
307 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
308 ExitProcess(0);
309 }
310 }
311
312 static BOOL
313 WINAPI
314 ConsoleCtrlHandler(DWORD ControlType)
315 {
316 switch (ControlType)
317 {
318 case CTRL_LAST_CLOSE_EVENT:
319 {
320 /* Delayed shutdown */
321 DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
322 VdmShutdown(FALSE);
323 break;
324 }
325
326 default:
327 {
328 /* Stop the VDM if the user logs out or closes the console */
329 DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
330 VdmShutdown(TRUE);
331 }
332 }
333 return TRUE;
334 }
335
336 static VOID
337 ConsoleInitUI(VOID)
338 {
339 CreateVdmMenu(ConsoleOutput);
340 }
341
342 static VOID
343 ConsoleCleanupUI(VOID)
344 {
345 /* Display again properly the mouse pointer */
346 if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
347
348 DestroyVdmMenu();
349 }
350
351 BOOL
352 ConsoleAttach(VOID)
353 {
354 /* Save the original input and output console modes */
355 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
356 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
357 {
358 CloseHandle(ConsoleOutput);
359 CloseHandle(ConsoleInput);
360 wprintf(L"FATAL: Cannot save console in/out modes\n");
361 return FALSE;
362 }
363
364 /* Set the console input mode */
365 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
366 // upon console window events (screen buffer resize, ...).
367 SetConsoleMode(ConsoleInput, 0 /* | ENABLE_WINDOW_INPUT */);
368 EnableExtraHardware(ConsoleInput);
369
370 /* Set the console output mode */
371 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
372
373 /* Initialize the UI */
374 ConsoleInitUI();
375
376 return TRUE;
377 }
378
379 VOID
380 ConsoleDetach(VOID)
381 {
382 /* Cleanup the UI */
383 ConsoleCleanupUI();
384
385 /* Restore the original input and output console modes */
386 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
387 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
388 }
389
390 static BOOL
391 ConsoleInit(VOID)
392 {
393 /* Set the handler routine */
394 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
395
396 /* Enable the CTRL_LAST_CLOSE_EVENT */
397 SetLastConsoleEventActive();
398
399 /*
400 * NOTE: The CONIN$ and CONOUT$ "virtual" files
401 * always point to non-redirected console handles.
402 */
403
404 /* Get the input handle to the real console, and check for success */
405 ConsoleInput = CreateFileW(L"CONIN$",
406 GENERIC_READ | GENERIC_WRITE,
407 FILE_SHARE_READ | FILE_SHARE_WRITE,
408 NULL,
409 OPEN_EXISTING,
410 0,
411 NULL);
412 if (ConsoleInput == INVALID_HANDLE_VALUE)
413 {
414 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
415 return FALSE;
416 }
417
418 /* Get the output handle to the real console, and check for success */
419 ConsoleOutput = CreateFileW(L"CONOUT$",
420 GENERIC_READ | GENERIC_WRITE,
421 FILE_SHARE_READ | FILE_SHARE_WRITE,
422 NULL,
423 OPEN_EXISTING,
424 0,
425 NULL);
426 if (ConsoleOutput == INVALID_HANDLE_VALUE)
427 {
428 CloseHandle(ConsoleInput);
429 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
430 return FALSE;
431 }
432
433 /* Effectively attach to the console */
434 return ConsoleAttach();
435 }
436
437 static VOID
438 ConsoleCleanup(VOID)
439 {
440 /* Detach from the console */
441 ConsoleDetach();
442
443 /* Close the console handles */
444 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
445 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
446 }
447
448 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
449 {
450 switch (MenuEvent->dwCommandId)
451 {
452 case ID_SHOWHIDE_MOUSE:
453 ShowHideMousePointer(ConsoleOutput, ShowPointer);
454 ShowPointer = !ShowPointer;
455 break;
456
457 case ID_VDM_DUMPMEM_TXT:
458 DumpMemory(TRUE);
459 break;
460
461 case ID_VDM_DUMPMEM_BIN:
462 DumpMemory(FALSE);
463 break;
464
465 case ID_VDM_QUIT:
466 /* Stop the VDM */
467 // EmulatorTerminate();
468
469 /* Nothing runs, so exit immediately */
470 DPRINT1("Killing NTVDM via console menu!\n");
471 VdmShutdown(TRUE);
472 break;
473
474 default:
475 break;
476 }
477 }
478
479 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
480 {
481 DPRINT1("Focus events not handled\n");
482 }
483
484 static BOOL
485 LoadGlobalSettings(VOID)
486 {
487 // FIXME: These strings should be localized.
488 #define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
489 #define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid."
490 #define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization."
491
492 BOOL Success = TRUE;
493 LONG Error = 0;
494
495 HKEY hNTVDMKey;
496 LPCWSTR NTVDMKeyName = L"SYSTEM\\CurrentControlSet\\Control\\NTVDM";
497
498 /* Try to open the NTVDM registry key */
499 Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
500 NTVDMKeyName,
501 0,
502 KEY_QUERY_VALUE,
503 &hNTVDMKey);
504 if (Error == ERROR_FILE_NOT_FOUND)
505 {
506 /* If the key just doesn't exist, don't do anything else */
507 return TRUE;
508 }
509 else if (Error != ERROR_SUCCESS)
510 {
511 /* The key exists but there was an access error: display an error and quit */
512 DisplayMessage(ERROR_REGVDD);
513 return FALSE;
514 }
515
516 /*
517 * Now we can do:
518 * - CPU core choice
519 * - Video choice
520 * - Sound choice
521 * - Mem?
522 * - ...
523 * - Standalone mode?
524 * - Debug settings
525 */
526
527 // Quit:
528 RegCloseKey(hNTVDMKey);
529 return Success;
530 }
531
532 INT
533 wmain(INT argc, WCHAR *argv[])
534 {
535 NtVdmArgc = argc;
536 NtVdmArgv = argv;
537
538 #ifdef STANDALONE
539
540 if (argc < 2)
541 {
542 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
543 L"Usage: NTVDM <executable> [<parameters>]\n");
544 return 0;
545 }
546
547 #endif
548
549 #ifdef ADVANCED_DEBUGGING
550 {
551 INT i = 20;
552
553 printf("Waiting for debugger (10 secs)..");
554 while (i--)
555 {
556 printf(".");
557 if (IsDebuggerPresent())
558 {
559 DbgBreakPoint();
560 break;
561 }
562 Sleep(500);
563 }
564 printf("Continue\n");
565 }
566 #endif
567
568 /* Load global VDM settings */
569 LoadGlobalSettings();
570
571 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
572
573 /* Initialize the console */
574 if (!ConsoleInit())
575 {
576 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
577 goto Cleanup;
578 }
579
580 /* Initialize the emulator */
581 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
582 {
583 wprintf(L"FATAL: Failed to initialize the emulator\n");
584 goto Cleanup;
585 }
586
587 /* Initialize the system BIOS and option ROMs */
588 if (!BiosInitialize(NULL, NULL))
589 {
590 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
591 goto Cleanup;
592 }
593
594 /* Let's go! Start simulation */
595 CpuSimulate();
596
597 /* Quit the VDM */
598 Cleanup:
599 VdmShutdown(TRUE);
600 return 0;
601 }
602
603 /* EOF */