[NTVDM]
[reactos.git] / subsystems / ntvdm / dos.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos.c
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 #include "ntvdm.h"
10
11 WORD CurrentPsp = SYSTEM_PSP, LastError = 0;
12
13 static VOID DosCombineFreeBlocks()
14 {
15 WORD Segment = FIRST_MCB_SEGMENT;
16 PDOS_MCB CurrentMcb, NextMcb;
17
18 /* Loop through all the blocks */
19 while (TRUE)
20 {
21 /* Get a pointer to the MCB */
22 CurrentMcb = SEGMENT_TO_MCB(Segment);
23
24 /* Ignore the last block */
25 if (CurrentMcb->BlockType == 'Z') break;
26
27 /* Get a pointer to the next MCB */
28 NextMcb = SEGMENT_TO_MCB(Segment + CurrentMcb->Size + 1);
29
30 /* If both this block and the next one are free, combine them */
31 if ((CurrentMcb->OwnerPsp == 0) && (NextMcb->OwnerPsp == 0))
32 {
33 CurrentMcb->Size += NextMcb->Size + 1;
34 CurrentMcb->BlockType = NextMcb->BlockType;
35
36 /* Invalidate the next MCB */
37 NextMcb->BlockType = 'I';
38
39 /* Try to combine the current block again with the next one */
40 continue;
41 }
42
43 /* Update the segment and continue */
44 Segment += CurrentMcb->Size + 1;
45 }
46 }
47
48 static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
49 {
50 PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
51 ULONG TotalSize = 0;
52 WORD DestSegment;
53
54 Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
55
56 /* Calculate the size of the environment block */
57 while (*Ptr)
58 {
59 TotalSize += strlen(Ptr) + 1;
60 Ptr += strlen(Ptr) + 1;
61 }
62 TotalSize++;
63
64 /* Allocate the memory for the environment block */
65 DestSegment = DosAllocateMemory((TotalSize + 0x0F) >> 4);
66 if (!DestSegment) return 0;
67
68 Ptr = SourceBuffer;
69
70 DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
71 while (*Ptr)
72 {
73 /* Copy the string */
74 strcpy(DestBuffer, Ptr);
75
76 /* Advance to the next string */
77 Ptr += strlen(Ptr) + 1;
78 DestBuffer += strlen(Ptr) + 1;
79 }
80
81 /* Set the final zero */
82 *DestBuffer = 0;
83
84 return DestSegment;
85 }
86
87 WORD DosAllocateMemory(WORD Size)
88 {
89 WORD Result = 0, Segment = FIRST_MCB_SEGMENT;
90 PDOS_MCB CurrentMcb, NextMcb;
91
92 /* Find an unallocated block */
93 while (TRUE)
94 {
95 /* Get a pointer to the MCB */
96 CurrentMcb = SEGMENT_TO_MCB(Segment);
97
98 /* Make sure it's valid */
99 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
100 {
101 return 0;
102 }
103
104 /* Only check free blocks */
105 if (CurrentMcb->OwnerPsp != 0) goto Next;
106
107 /* Check if the block is big enough */
108 if (CurrentMcb->Size < Size) goto Next;
109
110 /* It is, update the smallest found so far */
111 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
112 {
113 Result = Segment;
114 }
115
116 Next:
117 /* If this was the last MCB in the chain, quit. */
118 if (CurrentMcb->BlockType == 'Z') break;
119
120 /* Otherwise, update the segment and continue */
121 Segment += CurrentMcb->Size + 1;
122 }
123
124 /* If we didn't find a free block, return zero */
125 if (Result == 0) return 0;
126
127 /* Get a pointer to the MCB */
128 CurrentMcb = SEGMENT_TO_MCB(Result);
129
130 /* Check if the block is larger than requested */
131 if (CurrentMcb->Size > Size)
132 {
133 /* It is, split it into two blocks */
134 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
135
136 /* Initialize the new MCB structure */
137 NextMcb->BlockType = CurrentMcb->BlockType;
138 NextMcb->Size = Size - CurrentMcb->Size - 1;
139 NextMcb->OwnerPsp = 0;
140
141 /* Update the current block */
142 CurrentMcb->BlockType = 'M';
143 CurrentMcb->Size = Size;
144
145 /* Combine consecutive free blocks into larger blocks */
146 DosCombineFreeBlocks();
147 }
148
149 /* Take ownership of the block */
150 CurrentMcb->OwnerPsp = CurrentPsp;
151
152 return Result;
153 }
154
155 WORD DosResizeMemory(WORD Segment, WORD NewSize)
156 {
157 WORD ReturnSize = 0, CurrentSeg;
158 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), CurrentMcb;
159 BOOLEAN FinalBlockUsed = FALSE;
160
161 /* We can't expand the last block */
162 if (Mcb->BlockType != 'M') return 0;
163
164 /* Check if need to expand or contract the block */
165 if (NewSize > Mcb->Size)
166 {
167 ReturnSize = Mcb->Size;
168
169 /* Get the segment of the next MCB */
170 CurrentSeg = Segment + Mcb->Size + 1;
171
172 /* Calculate the maximum amount of memory this block could expand to */
173 while (ReturnSize < NewSize)
174 {
175 /* Get the MCB */
176 CurrentMcb = SEGMENT_TO_MCB(CurrentSeg);
177
178 /* We can't expand the block over an allocated block */
179 if (CurrentMcb->OwnerPsp != 0) break;
180
181 ReturnSize += CurrentMcb->Size + 1;
182
183 /* Check if this is the last block */
184 if (CurrentMcb->BlockType == 'Z')
185 {
186 FinalBlockUsed = TRUE;
187 break;
188 }
189
190 /* Update the segment and continue */
191 CurrentSeg += CurrentMcb->Size + 1;
192 }
193
194 /* Check if we need to split the last block */
195 if (ReturnSize > NewSize)
196 {
197 /* Initialize the new MCB structure */
198 CurrentMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
199 CurrentMcb->BlockType = (FinalBlockUsed) ? 'Z' : 'M';
200 CurrentMcb->Size = ReturnSize - NewSize - 1;
201 CurrentMcb->OwnerPsp = 0;
202 }
203
204 /* Calculate the new size of the block */
205 ReturnSize = min(ReturnSize, NewSize);
206
207 /* Update the MCB */
208 if (FinalBlockUsed) Mcb->BlockType = 'Z';
209 Mcb->Size = ReturnSize;
210 }
211 else if (NewSize < Mcb->Size)
212 {
213 /* Just split the block */
214 CurrentMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
215 CurrentMcb->BlockType = Mcb->BlockType;
216 CurrentMcb->Size = Mcb->Size - NewSize - 1;
217 CurrentMcb->OwnerPsp = 0;
218
219 /* Update the MCB */
220 Mcb->BlockType = 'M';
221 Mcb->Size = NewSize;
222
223 ReturnSize = NewSize;
224 }
225
226 /* Combine consecutive free blocks into larger blocks */
227 DosCombineFreeBlocks();
228
229 return ReturnSize;
230 }
231
232 BOOLEAN DosFreeMemory(WORD Segment)
233 {
234 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
235
236 /* Make sure the MCB is valid */
237 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') return FALSE;
238
239 /* Mark the block as free */
240 Mcb->OwnerPsp = 0;
241
242 /* Combine consecutive free blocks into larger blocks */
243 DosCombineFreeBlocks();
244
245 return TRUE;
246 }
247
248 WORD DosCreateFile(LPCSTR FilePath)
249 {
250 // TODO: NOT IMPLEMENTED
251 return 0;
252 }
253
254 WORD DosOpenFile(LPCSTR FilePath)
255 {
256 // TODO: NOT IMPLEMENTED
257 return 0;
258 }
259
260 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
261 {
262 INT i;
263 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
264 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
265
266 ZeroMemory(PspBlock, sizeof(DOS_PSP));
267
268 /* Set the exit interrupt */
269 PspBlock->Exit[0] = 0xCD; // int 0x20
270 PspBlock->Exit[1] = 0x20;
271
272 /* Set the program size */
273 PspBlock->MemSize = ProgramSize;
274
275 /* Save the interrupt vectors */
276 PspBlock->TerminateAddress = IntVecTable[0x22];
277 PspBlock->BreakAddress = IntVecTable[0x23];
278 PspBlock->CriticalAddress = IntVecTable[0x24];
279
280 /* Set the parent PSP */
281 PspBlock->ParentPsp = CurrentPsp;
282
283 /* Initialize the handle table */
284 for (i = 0; i < 20; i++) PspBlock->HandleTable[i] = 0xFF;
285
286 /* Did we get an environment segment? */
287 if (!Environment)
288 {
289 /* No, copy the one from the parent */
290 Environment = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
291 ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
292 : SYSTEM_ENV_BLOCK);
293 }
294
295 PspBlock->EnvBlock = Environment;
296
297 /* Set the handle table pointers to the internal handle table */
298 PspBlock->HandleTableSize = 20;
299 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
300
301 /* Set the DOS version */
302 PspBlock->DosVersion = DOS_VERSION;
303
304 /* Set the far call opcodes */
305 PspBlock->FarCall[0] = 0xCD; // int 0x21
306 PspBlock->FarCall[1] = 0x21;
307 PspBlock->FarCall[2] = 0xCB; // retf
308
309 /* Set the command line */
310 PspBlock->CommandLineSize = strlen(CommandLine);
311 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
312 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
313 }
314
315 BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
316 {
317 BOOLEAN Success = FALSE;
318 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
319 LPBYTE Address = NULL;
320 LPSTR ProgramFilePath, Parameters[128];
321 CHAR CommandLineCopy[128];
322 INT ParamCount = 0;
323 WORD Segment, FileSize;
324
325 /* Save a copy of the command line */
326 strcpy(CommandLineCopy, CommandLine);
327
328 /* Get the file name of the executable */
329 ProgramFilePath = strtok(CommandLineCopy, " \t");
330
331 /* Load the parameters in the local array */
332 while ((ParamCount < 256)
333 && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
334 {
335 ParamCount++;
336 }
337
338 /* Open a handle to the executable */
339 FileHandle = CreateFileA(ProgramFilePath,
340 GENERIC_READ,
341 0,
342 NULL,
343 OPEN_EXISTING,
344 FILE_ATTRIBUTE_NORMAL,
345 NULL);
346 if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
347
348 /* Get the file size */
349 FileSize = GetFileSize(FileHandle, NULL);
350
351 /* Create a mapping object for the file */
352 FileMapping = CreateFileMapping(FileHandle,
353 NULL,
354 PAGE_READONLY,
355 0,
356 0,
357 NULL);
358 if (FileMapping == NULL) goto Cleanup;
359
360 /* Map the file into memory */
361 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
362 if (Address == NULL) goto Cleanup;
363
364 /* Check if this is an EXE file or a COM file */
365 if (Address[0] == 'M' && Address[1] == 'Z')
366 {
367 /* EXE file */
368
369 // TODO: NOT IMPLEMENTED
370 DisplayMessage(L"EXE files are not yet supported!");
371 }
372 else
373 {
374 /* COM file */
375
376 /* Allocate memory for the whole program and the PSP */
377 Segment = DosAllocateMemory((FileSize + sizeof(DOS_PSP)) >> 4);
378 if (Segment == 0) goto Cleanup;
379
380 /* Copy the program to Segment:0100 */
381 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
382 + TO_LINEAR(Segment, 0x100)),
383 Address,
384 FileSize);
385
386 /* Initialize the PSP */
387 DosInitializePsp(Segment,
388 CommandLine,
389 (FileSize + sizeof(DOS_PSP)) >> 4,
390 EnvBlock);
391
392 /* Set the initial segment registers */
393 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
394 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
395
396 /* Set the stack to the last word of the segment */
397 EmulatorSetStack(Segment, 0xFFFE);
398
399 /* Execute */
400 CurrentPsp = Segment;
401 EmulatorExecute(Segment, 0x100);
402
403 Success = TRUE;
404 }
405
406 Cleanup:
407 /* Unmap the file*/
408 if (Address != NULL) UnmapViewOfFile(Address);
409
410 /* Close the file mapping object */
411 if (FileMapping != NULL) CloseHandle(FileMapping);
412
413 /* Close the file handle */
414 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
415
416 return Success;
417 }
418
419 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
420 {
421 WORD McbSegment = FIRST_MCB_SEGMENT;
422 PDOS_MCB CurrentMcb;
423 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
424 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
425
426 /* Check if this PSP is it's own parent */
427 if (PspBlock->ParentPsp == Psp) goto Done;
428
429 // TODO: Close all handles opened by the process
430
431 /* Free the memory used by the process */
432 while (TRUE)
433 {
434 /* Get a pointer to the MCB */
435 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
436
437 /* Make sure the MCB is valid */
438 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
439
440 /* If this block was allocated by the process, free it */
441 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment);
442
443 /* If this was the last block, quit */
444 if (CurrentMcb->BlockType == 'Z') break;
445
446 /* Update the segment and continue */
447 McbSegment += CurrentMcb->Size + 1;
448 }
449
450 Done:
451 /* Restore the interrupt vectors */
452 IntVecTable[0x22] = PspBlock->TerminateAddress;
453 IntVecTable[0x23] = PspBlock->BreakAddress;
454 IntVecTable[0x24] = PspBlock->CriticalAddress;
455
456 /* Update the current PSP */
457 if (Psp == CurrentPsp)
458 {
459 CurrentPsp = PspBlock->ParentPsp;
460 if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
461 }
462
463 /* Return control to the parent process */
464 EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
465 LOWORD(PspBlock->TerminateAddress));
466 }
467
468 CHAR DosReadCharacter()
469 {
470 // TODO: STDIN can be redirected under DOS 2.0+
471 return _getch();
472 }
473
474 VOID DosPrintCharacter(CHAR Character)
475 {
476 // TODO: STDOUT can be redirected under DOS 2.0+
477 if (Character == '\r') Character = '\n';
478 putchar(Character);
479 }
480
481 VOID DosInt20h(WORD CodeSegment)
482 {
483 /* This is the exit interrupt */
484 DosTerminateProcess(CodeSegment, 0);
485 }
486
487 VOID DosInt21h(WORD CodeSegment)
488 {
489 INT i;
490 CHAR Character;
491 SYSTEMTIME SystemTime;
492 PCHAR String;
493 PDOS_INPUT_BUFFER InputBuffer;
494 DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
495 DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
496 DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
497 DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
498 WORD DataSegment = EmulatorGetRegister(EMULATOR_REG_DS);
499 WORD ExtSegment = EmulatorGetRegister(EMULATOR_REG_ES);
500
501 /* Check the value in the AH register */
502 switch (HIBYTE(Eax))
503 {
504 /* Terminate Program */
505 case 0x00:
506 {
507 DosTerminateProcess(CodeSegment, 0);
508 break;
509 }
510
511 /* Read Character And Echo */
512 case 0x01:
513 {
514 Character = DosReadCharacter();
515 DosPrintCharacter(Character);
516 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
517 break;
518 }
519
520 /* Print Character */
521 case 0x02:
522 {
523 DosPrintCharacter(LOBYTE(Edx));
524 break;
525 }
526
527 /* Read Character Without Echo */
528 case 0x08:
529 {
530 EmulatorSetRegister(EMULATOR_REG_AX,
531 (Eax & 0xFFFFFF00) | DosReadCharacter());
532 break;
533 }
534
535 /* Print String */
536 case 0x09:
537 {
538 String = (PCHAR)((ULONG_PTR)BaseAddress
539 + TO_LINEAR(DataSegment, LOWORD(Edx)));
540
541 while ((*String) != '$')
542 {
543 DosPrintCharacter(*String);
544 String++;
545 }
546
547 break;
548 }
549
550 /* Read Buffered Input */
551 case 0x0A:
552 {
553 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
554 + TO_LINEAR(DataSegment,
555 LOWORD(Edx)));
556
557 InputBuffer->Length = 0;
558 for (i = 0; i < InputBuffer->MaxLength; i ++)
559 {
560 Character = DosReadCharacter();
561 DosPrintCharacter(Character);
562 InputBuffer->Buffer[InputBuffer->Length] = Character;
563 if (Character == '\r') break;
564 InputBuffer->Length++;
565 }
566
567 break;
568 }
569
570 /* Get system date */
571 case 0x2A:
572 {
573 GetLocalTime(&SystemTime);
574 EmulatorSetRegister(EMULATOR_REG_CX,
575 (Ecx & 0xFFFF0000) | SystemTime.wYear);
576 EmulatorSetRegister(EMULATOR_REG_DX,
577 (Edx & 0xFFFF0000)
578 | (SystemTime.wMonth << 8)
579 | SystemTime.wDay);
580 EmulatorSetRegister(EMULATOR_REG_AX,
581 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
582 break;
583 }
584
585 /* Set system date */
586 case 0x2B:
587 {
588 GetLocalTime(&SystemTime);
589 SystemTime.wYear = LOWORD(Ecx);
590 SystemTime.wMonth = HIBYTE(Edx);
591 SystemTime.wDay = LOBYTE(Edx);
592
593 if (SetLocalTime(&SystemTime))
594 {
595 /* Return success */
596 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
597 }
598 else
599 {
600 /* Return failure */
601 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
602 }
603
604 break;
605 }
606
607 /* Get system time */
608 case 0x2C:
609 {
610 GetLocalTime(&SystemTime);
611 EmulatorSetRegister(EMULATOR_REG_CX,
612 (Ecx & 0xFFFF0000)
613 | (SystemTime.wHour << 8)
614 | SystemTime.wMinute);
615 EmulatorSetRegister(EMULATOR_REG_DX,
616 (Edx & 0xFFFF0000)
617 | (SystemTime.wSecond << 8)
618 | (SystemTime.wMilliseconds / 10));
619 break;
620 }
621
622 /* Set system time */
623 case 0x2D:
624 {
625 GetLocalTime(&SystemTime);
626 SystemTime.wHour = HIBYTE(Ecx);
627 SystemTime.wMinute = LOBYTE(Ecx);
628 SystemTime.wSecond = HIBYTE(Edx);
629 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
630
631 if (SetLocalTime(&SystemTime))
632 {
633 /* Return success */
634 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
635 }
636 else
637 {
638 /* Return failure */
639 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
640 }
641
642 break;
643 }
644
645 /* Allocate Memory */
646 case 0x48:
647 {
648 WORD Segment = DosAllocateMemory(LOWORD(Ebx));
649 if (Segment != 0)
650 {
651 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
652 EmulatorClearFlag(EMULATOR_FLAG_CF);
653 }
654 else EmulatorSetFlag(EMULATOR_FLAG_CF);
655
656 break;
657 }
658
659 /* Free Memory */
660 case 0x49:
661 {
662 if (DosFreeMemory(ExtSegment))
663 {
664 EmulatorClearFlag(EMULATOR_FLAG_CF);
665 }
666 else EmulatorSetFlag(EMULATOR_FLAG_CF);
667
668 break;
669 }
670
671 /* Resize Memory Block */
672 case 0x4A:
673 {
674 WORD Size = DosResizeMemory(ExtSegment, LOWORD(Ebx));
675
676 if (Size != 0)
677 {
678 EmulatorSetRegister(EMULATOR_REG_BX, Size);
679 EmulatorClearFlag(EMULATOR_FLAG_CF);
680 }
681 else EmulatorSetFlag(EMULATOR_FLAG_CF);
682
683 break;
684 }
685
686 /* Terminate With Return Code */
687 case 0x4C:
688 {
689 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
690 break;
691 }
692
693 /* Unsupported */
694 default:
695 {
696 EmulatorSetFlag(EMULATOR_FLAG_CF);
697 }
698 }
699 }
700
701 VOID DosBreakInterrupt()
702 {
703 VdmRunning = FALSE;
704 }
705
706 BOOLEAN DosInitialize()
707 {
708 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
709 FILE *Stream;
710 WCHAR Buffer[256];
711 LPWSTR SourcePtr, Environment;
712 LPSTR AsciiString;
713 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
714 DWORD AsciiSize;
715
716 /* Initialize the MCB */
717 Mcb->BlockType = 'Z';
718 Mcb->Size = (WORD)USER_MEMORY_SIZE;
719 Mcb->OwnerPsp = 0;
720
721 /* Get the environment strings */
722 SourcePtr = Environment = GetEnvironmentStringsW();
723 if (Environment == NULL) return FALSE;
724
725 /* Fill the DOS system environment block */
726 while (*SourcePtr)
727 {
728 /* Get the size of the ASCII string */
729 AsciiSize = WideCharToMultiByte(CP_ACP,
730 0,
731 SourcePtr,
732 -1,
733 NULL,
734 0,
735 NULL,
736 NULL);
737
738 /* Allocate memory for the ASCII string */
739 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
740 if (AsciiString == NULL)
741 {
742 FreeEnvironmentStringsW(Environment);
743 return FALSE;
744 }
745
746 /* Convert to ASCII */
747 WideCharToMultiByte(CP_ACP,
748 0,
749 SourcePtr,
750 -1,
751 AsciiString,
752 AsciiSize,
753 NULL,
754 NULL);
755
756 /* Copy the string into DOS memory */
757 strcpy(DestPtr, AsciiString);
758
759 /* Free the memory */
760 HeapFree(GetProcessHeap(), 0, AsciiString);
761
762 /* Move to the next string */
763 SourcePtr += wcslen(SourcePtr) + 1;
764 DestPtr += strlen(AsciiString) + 1;
765 }
766
767 /* Free the memory allocated for environment strings */
768 FreeEnvironmentStringsW(Environment);
769
770 /* Read CONFIG.SYS */
771 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
772 if (Stream != NULL)
773 {
774 while (fgetws(Buffer, 256, Stream))
775 {
776 // TODO: Parse the line
777 }
778 fclose(Stream);
779 }
780
781 return TRUE;
782 }
783
784 /* EOF */