DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * Heavy improvements to various commands by the DOSBox-X Team 00019 * With major works from joncampbell123, Wengier, and rderooy 00020 * AUTOTYPE command Copyright (C) 2020 the dosbox-staging team 00021 */ 00022 00023 #include "dosbox.h" 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <ctype.h> 00027 #include <math.h> 00028 #include <algorithm> 00029 #include <string> 00030 #include <vector> 00031 #include "programs.h" 00032 #include "support.h" 00033 #include "drives.h" 00034 #include "cross.h" 00035 #include "regs.h" 00036 #include "ide.h" 00037 #include "cpu.h" 00038 #include "callback.h" 00039 #include "cdrom.h" 00040 #include "bios_disk.h" 00041 #include "dos_system.h" 00042 #include "dos_inc.h" 00043 #include "bios.h" 00044 #include "inout.h" 00045 #include "dma.h" 00046 #include "bios_disk.h" 00047 #include "qcow2_disk.h" 00048 #include "setup.h" 00049 #include "control.h" 00050 #include <time.h> 00051 #include "menu.h" 00052 #include "render.h" 00053 #include "mouse.h" 00054 #include "../ints/int10.h" 00055 #if defined(WIN32) 00056 #include "../dos/cdrom.h" 00057 #include <ShlObj.h> 00058 #if !defined(HX_DOS) 00059 #include <Commdlg.h> 00060 #endif 00061 #endif 00062 bool Mouse_Drv=true; 00063 bool Mouse_Vertical = false; 00064 bool force_nocachedir = false; 00065 bool freesizecap = true; 00066 bool wpcolon = true; 00067 00068 void DOS_EnableDriveMenu(char drv); 00069 00070 #if defined(OS2) 00071 #define INCL DOSFILEMGR 00072 #define INCL_DOSERRORS 00073 #include "os2.h" 00074 #endif 00075 00076 #if defined(WIN32) 00077 #ifndef S_ISDIR 00078 #define S_ISDIR(m) (((m)&S_IFMT)==S_IFDIR) 00079 #endif 00080 #endif 00081 00082 #if defined(RISCOS) 00083 #include <unixlib/local.h> 00084 #include <limits.h> 00085 #endif 00086 00087 #if C_DEBUG 00088 Bitu DEBUG_EnableDebugger(void); 00089 #endif 00090 00091 class MOUSE : public Program { 00092 public: 00093 void Run(void); 00094 }; 00095 00096 void MOUSE::Run(void) { 00097 if (cmd->FindExist("/?",false) || cmd->FindExist("/h",false)) { 00098 WriteOut(MSG_Get("PROGRAM_MOUSE_HELP")); 00099 return; 00100 } 00101 if (!Mouse_Drv) { 00102 if (cmd->FindExist("/u",false)) 00103 WriteOut(MSG_Get("PROGRAM_MOUSE_NOINSTALLED")); 00104 else { 00105 Mouse_Drv = true; 00106 mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu); 00107 WriteOut(MSG_Get("PROGRAM_MOUSE_INSTALL")); 00108 if (cmd->FindExist("/v",false)) { 00109 Mouse_Vertical = true; 00110 WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL")); 00111 } else { 00112 Mouse_Vertical = false; 00113 } 00114 mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu); 00115 } 00116 } 00117 else { 00118 if (cmd->FindExist("/u",false)) { 00119 Mouse_Drv = false; 00120 mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu); 00121 WriteOut(MSG_Get("PROGRAM_MOUSE_UNINSTALL")); 00122 } else 00123 if (cmd->FindExist("/v",false)) { 00124 if(!Mouse_Vertical) { 00125 Mouse_Vertical = true; 00126 WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL")); 00127 } else { 00128 Mouse_Vertical = false; 00129 WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL_BACK")); 00130 } 00131 mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu); 00132 } else 00133 WriteOut(MSG_Get("PROGRAM_MOUSE_ERROR")); 00134 } 00135 } 00136 00137 static void MOUSE_ProgramStart(Program * * make) { 00138 *make=new MOUSE; 00139 } 00140 00141 void MSCDEX_SetCDInterface(int intNr, int forceCD); 00142 Bit8u ZDRIVE_NUM = 25; 00143 00144 static const char* UnmountHelper(char umount) { 00145 int i_drive; 00146 if (umount < '0' || umount > 3+'0') 00147 i_drive = toupper(umount) - 'A'; 00148 else 00149 i_drive = umount - '0'; 00150 00151 if (i_drive >= DOS_DRIVES || i_drive < 0) 00152 return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); 00153 00154 if (i_drive < MAX_DISK_IMAGES && Drives[i_drive] == NULL && imageDiskList[i_drive] == NULL) 00155 return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); 00156 00157 if (i_drive >= MAX_DISK_IMAGES && Drives[i_drive] == NULL) 00158 return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); 00159 00160 if (Drives[i_drive]) { 00161 switch (DriveManager::UnmountDrive(i_drive)) { 00162 case 1: return MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL"); 00163 case 2: return MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"); 00164 } 00165 Drives[i_drive] = 0; 00166 DOS_EnableDriveMenu(i_drive+'A'); 00167 mem_writeb(Real2Phys(dos.tables.mediaid)+(unsigned int)i_drive*dos.tables.dpb_size,0); 00168 if (i_drive == DOS_GetDefaultDrive()) 00169 DOS_SetDrive(ZDRIVE_NUM); 00170 } 00171 00172 if (i_drive < MAX_DISK_IMAGES && imageDiskList[i_drive]) { 00173 delete imageDiskList[i_drive]; 00174 imageDiskList[i_drive] = NULL; 00175 } 00176 00177 return MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"); 00178 } 00179 00180 #if defined(WIN32) 00181 void MountHelper(char drive, const char drive2[DOS_PATHLENGTH], std::string drive_type) { 00182 std::vector<std::string> options; 00183 DOS_Drive * newdrive; 00184 std::string temp_line; 00185 std::string str_size; 00186 Bit16u sizes[4]; 00187 Bit8u mediaid; 00188 00189 if(drive_type=="CDROM") { 00190 mediaid=0xF8; /* Hard Disk */ 00191 str_size="2048,1,65535,0"; 00192 } else { 00193 if(drive_type=="FLOPPY") { 00194 mediaid=0xF0; /* Floppy 1.44 media */ 00195 str_size="512,1,2880,2880"; /* All space free */ 00196 } else if(drive_type=="LOCAL") { 00197 mediaid=0xF8; 00198 str_size="512,32,0,0"; 00199 } 00200 } 00201 00202 char number[20]; const char * scan=str_size.c_str(); 00203 Bitu index=0; Bitu count=0; 00204 while (*scan) { 00205 if (*scan==',') { 00206 number[index]=0;sizes[count++]=atoi(number); 00207 index=0; 00208 } else number[index++]=*scan; 00209 scan++; 00210 } 00211 number[index]=0; sizes[count++]=atoi(number); 00212 00213 temp_line = drive2; 00214 if(temp_line.size() > 3 && temp_line[temp_line.size()-1]=='\\') temp_line.erase(temp_line.size()-1,1); 00215 if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT; 00216 Bit8u bit8size=(Bit8u) sizes[1]; 00217 00218 if(drive_type=="CDROM") { 00219 int num = -1; 00220 int error; 00221 00222 int id, major, minor; 00223 DOSBox_CheckOS(id, major, minor); 00224 if ((id==VER_PLATFORM_WIN32_NT) && (major>5)) { 00225 // Vista/above 00226 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num); 00227 } else { 00228 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); 00229 } 00230 newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error,options); 00231 std::string errmsg; 00232 switch (error) { 00233 case 0 : errmsg=MSG_Get("MSCDEX_SUCCESS"); break; 00234 case 1 : errmsg=MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"); break; 00235 case 2 : errmsg=MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED"); break; 00236 case 3 : errmsg=MSG_Get("MSCDEX_ERROR_PATH"); break; 00237 case 4 : errmsg=MSG_Get("MSCDEX_TOO_MANY_DRIVES"); break; 00238 case 5 : errmsg=MSG_Get("MSCDEX_LIMITED_SUPPORT"); break; 00239 default : errmsg=MSG_Get("MSCDEX_UNKNOWN_ERROR"); break; 00240 } 00241 if (error) { 00242 MessageBox(GetHWND(),errmsg.c_str(),error==5?"Warning":"Error",MB_OK); 00243 if (error!=5) { 00244 delete newdrive; 00245 return; 00246 } 00247 } 00248 } else newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,options); 00249 00250 if (!newdrive) E_Exit("DOS:Can't create drive"); 00251 Drives[drive-'A']=newdrive; 00252 DOS_EnableDriveMenu(drive); 00253 mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*2,mediaid); 00254 if(drive_type=="CDROM") 00255 LOG_MSG("GUI: Drive %c is mounted as CD-ROM",drive); 00256 else 00257 LOG_MSG("GUI: Drive %c is mounted as local directory",drive); 00258 if(drive == drive2[0] && strlen(drive2) == 3) { 00259 // automatic mount 00260 } else { 00261 if(drive_type=="CDROM") return; 00262 std::string label; 00263 label = drive; 00264 if(drive_type=="LOCAL") 00265 label += "_DRIVE"; 00266 else 00267 label += "_FLOPPY"; 00268 newdrive->SetLabel(label.c_str(),false,true); 00269 } 00270 } 00271 00272 void MenuMountDrive(char drive, const char drive2[DOS_PATHLENGTH]) { 00273 std::vector<std::string> options; 00274 std::string str(1, drive); 00275 std::string drive_warn; 00276 if (Drives[drive-'A']) { 00277 drive_warn="Drive "+str+": is already mounted. Unmount it first, and then try again."; 00278 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00279 return; 00280 } 00281 if(control->SecureMode()) { 00282 MessageBox(GetHWND(),MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"),"Error",MB_OK); 00283 return; 00284 } 00285 DOS_Drive * newdrive; 00286 std::string temp_line; 00287 std::string str_size; 00288 Bit16u sizes[4]; 00289 Bit8u mediaid; 00290 drive_warn="Do you really want to give DOSBox-X access to"; 00291 int type=GetDriveType(drive2); 00292 if(type==DRIVE_NO_ROOT_DIR) { 00293 MessageBox(GetHWND(),("Drive "+str+": does not exist in the system.").c_str(),"Error",MB_OK); 00294 return; 00295 } else if(type==DRIVE_CDROM) 00296 drive_warn += " your real CD-ROM drive "; 00297 else if(type==DRIVE_REMOVABLE) 00298 drive_warn += drive=='A'||drive=='B'?" your real floppy drive ":" your real removable drive "; 00299 else if(type==DRIVE_REMOTE) 00300 drive_warn += " your real network drive "; 00301 else 00302 drive_warn += " everything on your real drive "; 00303 00304 if (MessageBox(GetHWND(),(drive_warn+str+"?").c_str(),"Warning",MB_YESNO)==IDNO) return; 00305 00306 if(type==DRIVE_CDROM) { 00307 mediaid=0xF8; /* Hard Disk */ 00308 str_size="2048,1,65535,0"; 00309 } else if(type==DRIVE_REMOVABLE && (drive=='A'||drive=='B')) { 00310 mediaid=0xF0; /* Floppy 1.44 media */ 00311 str_size="512,1,2880,2880"; /* All space free */ 00312 } else { 00313 mediaid=0xF8; 00314 str_size="512,32,0,0"; 00315 } 00316 00317 char number[20]; const char * scan=str_size.c_str(); 00318 Bitu index=0; Bitu count=0; 00319 while (*scan) { 00320 if (*scan==',') { 00321 number[index]=0;sizes[count++]=atoi(number); 00322 index=0; 00323 } else number[index++]=*scan; 00324 scan++; 00325 } 00326 number[index]=0; sizes[count++]=atoi(number); 00327 Bit8u bit8size=(Bit8u) sizes[1]; 00328 00329 temp_line = drive2; 00330 int error, num = -1; 00331 if(type==DRIVE_CDROM) { 00332 int id, major, minor; 00333 DOSBox_CheckOS(id, major, minor); 00334 00335 if ((id==VER_PLATFORM_WIN32_NT) && (major>5)) { 00336 // Vista/above 00337 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num); 00338 } else { 00339 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); 00340 } 00341 newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error,options); 00342 std::string errmsg; 00343 switch (error) { 00344 case 0 : errmsg=MSG_Get("MSCDEX_SUCCESS"); break; 00345 case 1 : errmsg=MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"); break; 00346 case 2 : errmsg=MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED"); break; 00347 case 3 : errmsg=MSG_Get("MSCDEX_ERROR_PATH"); break; 00348 case 4 : errmsg=MSG_Get("MSCDEX_TOO_MANY_DRIVES"); break; 00349 case 5 : errmsg=MSG_Get("MSCDEX_LIMITED_SUPPORT"); break; 00350 default : errmsg=MSG_Get("MSCDEX_UNKNOWN_ERROR"); break; 00351 } 00352 if (error) { 00353 MessageBox(GetHWND(),errmsg.c_str(),error==5?"Warning":"Error",MB_OK); 00354 if (error!=5) { 00355 delete newdrive; 00356 return; 00357 } 00358 } 00359 } else newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,options); 00360 00361 if (!newdrive) E_Exit("DOS:Can't create drive"); 00362 if(error && (type==DRIVE_CDROM)) return; 00363 Drives[drive-'A']=newdrive; 00364 DOS_EnableDriveMenu(drive); 00365 mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*2,mediaid); 00366 if(type==DRIVE_CDROM) LOG_MSG("GUI: Drive %c is mounted as CD-ROM %c:\\",drive,drive); 00367 else LOG_MSG("GUI: Drive %c is mounted as local directory %c:\\",drive,drive); 00368 if(drive == drive2[0] && strlen(drive2) == 3) { 00369 // automatic mount 00370 } else { 00371 if(type == DRIVE_CDROM) return; 00372 std::string label; 00373 label = drive; 00374 if(type==DRIVE_REMOVABLE && (drive=='A'||drive=='B')) 00375 label += "_FLOPPY"; 00376 else 00377 label += "_DRIVE"; 00378 newdrive->SetLabel(label.c_str(),false,true); 00379 } 00380 } 00381 00382 void MenuBrowseFolder(char drive, std::string drive_type) { 00383 if (Drives[drive-'A']) { 00384 std::string str(1, drive); 00385 std::string drive_warn="Drive "+str+": is already mounted. Unmount it first, and then try again."; 00386 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00387 return; 00388 } 00389 if(control->SecureMode()) { 00390 MessageBox(GetHWND(),MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"),"Error",MB_OK); 00391 return; 00392 } 00393 #if !defined(HX_DOS) 00394 std::string title = "Select a drive/directory to mount"; 00395 char path[MAX_PATH]; 00396 BROWSEINFO bi = { 0 }; 00397 if(drive_type=="CDROM") 00398 bi.lpszTitle = ( title + " CD-ROM\nMounting a directory as CD-ROM gives an limited support" ).c_str(); 00399 else if(drive_type=="FLOPPY") 00400 bi.lpszTitle = ( title + " as Floppy" ).c_str(); 00401 else if(drive_type=="LOCAL") 00402 bi.lpszTitle = ( title + " as Local").c_str(); 00403 else 00404 bi.lpszTitle = (title.c_str()); 00405 LPITEMIDLIST pidl = SHBrowseForFolder ( &bi ); 00406 00407 if ( pidl != 0 ) { 00408 SHGetPathFromIDList ( pidl, path ); 00409 // SetCurrentDirectory ( path ); 00410 WIN32_FIND_DATA FindFileData; 00411 HANDLE hFind; 00412 hFind = FindFirstFile ( "*.*", &FindFileData ); 00413 if ( hFind != INVALID_HANDLE_VALUE ) MountHelper(drive,path,drive_type); 00414 FindClose ( hFind ); 00415 IMalloc * imalloc = 0; 00416 if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) { 00417 imalloc->Free ( pidl ); 00418 imalloc->Release ( ); 00419 } 00420 } 00421 #endif 00422 } 00423 00424 void MenuBrowseImageFile(char drive, bool boot) { 00425 std::string str(1, drive); 00426 std::string drive_warn; 00427 if (Drives[drive-'A']&&!boot) { 00428 drive_warn="Drive "+str+": is already mounted. Unmount it first, and then try again."; 00429 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00430 return; 00431 } 00432 if(control->SecureMode()) { 00433 MessageBox(GetHWND(),MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"),"Error",MB_OK); 00434 return; 00435 } 00436 #if !defined(HX_DOS) 00437 OPENFILENAME OpenFileName; 00438 char szFile[MAX_PATH]; 00439 char CurrentDir[MAX_PATH]; 00440 const char * Temp_CurrentDir = CurrentDir; 00441 00442 szFile[0] = 0; 00443 GetCurrentDirectory( MAX_PATH, CurrentDir ); 00444 OpenFileName.lStructSize = sizeof( OPENFILENAME ); 00445 OpenFileName.hwndOwner = NULL; 00446 OpenFileName.lpstrFilter = "Image/Zip files(*.ima, *.img, *.iso, *.cue, *.bin, *.mdf, *.zip, *.7z)\0*.ima;*.img;*.iso;*.mdf;*.zip;*.cue;*.bin;*.7z\0All files(*.*)\0*.*\0"; 00447 OpenFileName.lpstrCustomFilter = NULL; 00448 OpenFileName.nMaxCustFilter = 0; 00449 OpenFileName.nFilterIndex = 0; 00450 OpenFileName.lpstrFile = szFile; 00451 OpenFileName.nMaxFile = sizeof( szFile ); 00452 OpenFileName.lpstrFileTitle = NULL; 00453 OpenFileName.nMaxFileTitle = 0; 00454 OpenFileName.lpstrInitialDir = CurrentDir; 00455 OpenFileName.lpstrTitle = "Select an image file"; 00456 OpenFileName.nFileOffset = 0; 00457 OpenFileName.nFileExtension = 0; 00458 OpenFileName.lpstrDefExt = NULL; 00459 OpenFileName.lCustData = 0; 00460 OpenFileName.lpfnHook = NULL; 00461 OpenFileName.lpTemplateName = NULL; 00462 OpenFileName.Flags = OFN_EXPLORER; 00463 00464 search: 00465 if(GetOpenFileName( &OpenFileName )) { 00466 WIN32_FIND_DATA FindFileData; 00467 HANDLE hFind; 00468 hFind = FindFirstFile(szFile, &FindFileData); 00469 if (hFind == INVALID_HANDLE_VALUE) goto search; 00470 char drive2 [_MAX_DRIVE]; 00471 char dir [_MAX_DIR]; 00472 char fname [_MAX_FNAME]; 00473 char ext [_MAX_EXT]; 00474 char * path = szFile; 00475 00476 _splitpath (path, drive2, dir, fname, ext); 00477 00478 char type[15]; 00479 if(!strcasecmp(ext,".ima")) 00480 strcpy(type,"-t floppy "); 00481 else if((!strcasecmp(ext,".iso")) || (!strcasecmp(ext,".cue")) || (!strcasecmp(ext,".bin")) || (!strcasecmp(ext,".mdf"))) 00482 strcpy(type,"-t iso "); 00483 else 00484 strcpy(type,""); 00485 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20]; 00486 strcpy(mountstring,"IMGMOUNT "); 00487 strcat(mountstring,type); 00488 char temp_str[3] = { 0,0,0 }; 00489 temp_str[0]=drive; 00490 temp_str[1]=' '; 00491 strcat(mountstring,temp_str); 00492 strcat(mountstring,path); 00493 if (boot) strcat(mountstring," -U"); 00494 strcat(mountstring," >nul"); 00495 DOS_Shell temp; 00496 temp.ParseLine(mountstring); 00497 if (!Drives[drive-'A']) { 00498 drive_warn="Drive "+str+": failed to mount."; 00499 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00500 return; 00501 } else if (boot) { 00502 char bootstring[DOS_PATHLENGTH+CROSS_LEN+20]; 00503 strcpy(bootstring,"BOOT -L "); 00504 strcat(bootstring,str.c_str()); 00505 strcat(bootstring," >nul"); 00506 DOS_Shell temp; 00507 temp.ParseLine(bootstring); 00508 std::string drive_warn="Drive "+str+": failed to boot."; 00509 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00510 } 00511 } 00512 SetCurrentDirectory( Temp_CurrentDir ); 00513 #endif 00514 } 00515 #endif 00516 00517 void MenuUnmountDrive(char drive) { 00518 if (!Drives[drive-'A']) { 00519 #if defined(WIN32) 00520 std::string drive_warn="Drive "+std::string(1, drive)+": is not yet mounted."; 00521 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00522 #endif 00523 return; 00524 } 00525 UnmountHelper(drive); 00526 } 00527 00528 void MenuBootDrive(char drive) { 00529 if(control->SecureMode()) { 00530 #if defined(WIN32) 00531 MessageBox(GetHWND(),MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"),"Error",MB_OK); 00532 #endif 00533 return; 00534 } 00535 std::string str(1, drive); 00536 char bootstring[DOS_PATHLENGTH+CROSS_LEN+20]; 00537 strcpy(bootstring,"BOOT -L "); 00538 strcat(bootstring,str.c_str()); 00539 strcat(bootstring," >nul"); 00540 DOS_Shell temp; 00541 temp.ParseLine(bootstring); 00542 std::string drive_warn="Drive "+str+": failed to boot."; 00543 #if defined(WIN32) 00544 MessageBox(GetHWND(),drive_warn.c_str(),"Error",MB_OK); 00545 #endif 00546 } 00547 00548 class MOUNT : public Program { 00549 public: 00550 std::vector<std::string> options; 00551 void ListMounts(void) { 00552 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH]; 00553 Bit32u size;Bit16u date;Bit16u time;Bit8u attr; 00554 /* Command uses dta so set it to our internal dta */ 00555 RealPt save_dta = dos.dta(); 00556 dos.dta(dos.tables.tempdta); 00557 DOS_DTA dta(dos.dta()); 00558 00559 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_1")); 00560 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label"); 00561 for(int p = 0;p < 8;p++) WriteOut("----------"); 00562 00563 for (int d = 0;d < DOS_DRIVES;d++) { 00564 if (!Drives[d]) continue; 00565 00566 char root[7] = {(char)('A'+d),':','\\','*','.','*',0}; 00567 bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME); 00568 if (ret) { 00569 dta.GetResult(name,lname,size,date,time,attr); 00570 DOS_FindNext(); //Mark entry as invalid 00571 } else name[0] = 0; 00572 00573 /* Change 8.3 to 11.0 */ 00574 const char* dot = strchr(name, '.'); 00575 if(dot && (dot - name == 8) ) { 00576 name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0; 00577 } 00578 00579 root[1] = 0; //This way, the format string can be reused. 00580 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name); 00581 } 00582 dos.dta(save_dta); 00583 } 00584 00585 void Run(void) { 00586 DOS_Drive *newdrive = NULL; 00587 std::string label; 00588 std::string umount; 00589 std::string newz; 00590 bool quiet=false; 00591 char drive; 00592 00593 //Hack To allow long commandlines 00594 ChangeToLongCmd(); 00595 /* Parse the command line */ 00596 /* if the command line is empty show current mounts */ 00597 if (!cmd->GetCount()) { 00598 ListMounts(); 00599 return; 00600 } 00601 00602 /* In secure mode don't allow people to change mount points. 00603 * Neither mount nor unmount */ 00604 if(control->SecureMode()) { 00605 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); 00606 return; 00607 } 00608 bool path_relative_to_last_config = false; 00609 if (cmd->FindExist("-pr",true)) path_relative_to_last_config = true; 00610 00611 if (cmd->FindExist("-q",false)) 00612 quiet = true; 00613 00614 /* Check for unmounting */ 00615 if (cmd->FindString("-u",umount,false)) { 00616 WriteOut(UnmountHelper(umount[0]), toupper(umount[0])); 00617 return; 00618 } 00619 00620 //look for -o options 00621 { 00622 std::string s; 00623 00624 while (cmd->FindString("-o", s, true)) 00625 options.push_back(s); 00626 } 00627 00628 /* Check for moving Z: */ 00629 /* Only allowing moving it once. It is merely a convenience added for the wine team */ 00630 if (ZDRIVE_NUM == 25 && cmd->FindString("-z", newz,false)) { 00631 newz[0] = toupper(newz[0]); 00632 int i_newz = (int)newz[0] - (int)'A'; 00633 if (i_newz >= 0 && i_newz < DOS_DRIVES-1 && !Drives[i_newz]) { 00634 ZDRIVE_NUM = i_newz; 00635 /* remap drives */ 00636 Drives[i_newz] = Drives[25]; 00637 Drives[25] = 0; 00638 DOS_EnableDriveMenu(i_newz+'A'); 00639 DOS_EnableDriveMenu(25+'A'); 00640 if (!first_shell) return; //Should not be possible 00641 /* Update environment */ 00642 std::string line = ""; 00643 char ppp[2] = {newz[0],0}; 00644 std::string tempenv = ppp; tempenv += ":\\"; 00645 if (first_shell->GetEnvStr("PATH",line)){ 00646 std::string::size_type idx = line.find('='); 00647 std::string value = line.substr(idx +1 , std::string::npos); 00648 while ( (idx = value.find("Z:\\")) != std::string::npos || 00649 (idx = value.find("z:\\")) != std::string::npos ) 00650 value.replace(idx,3,tempenv); 00651 line = value; 00652 } 00653 if (!line.size()) line = tempenv; 00654 first_shell->SetEnv("PATH",line.c_str()); 00655 tempenv += "COMMAND.COM"; 00656 first_shell->SetEnv("COMSPEC",tempenv.c_str()); 00657 00658 /* Update batch file if running from Z: (very likely: autoexec) */ 00659 if(first_shell->bf) { 00660 std::string &name = first_shell->bf->filename; 00661 if(name.length() >2 && name[0] == 'Z' && name[1] == ':') name[0] = newz[0]; 00662 } 00663 /* Change the active drive */ 00664 if (DOS_GetDefaultDrive() == 25) DOS_SetDrive(i_newz); 00665 } 00666 return; 00667 } 00668 /* Show list of cdroms */ 00669 if (cmd->FindExist("-cd",false)) { 00670 #if !defined(C_SDL2) 00671 int num = SDL_CDNumDrives(); 00672 WriteOut(MSG_Get("PROGRAM_MOUNT_CDROMS_FOUND"),num); 00673 for (int i=0; i<num; i++) { 00674 WriteOut("%2d. %s\n",i,SDL_CDName(i)); 00675 } 00676 #endif 00677 return; 00678 } 00679 00680 bool nocachedir = false; 00681 00682 if (force_nocachedir) 00683 nocachedir = true; 00684 00685 if (cmd->FindExist("-nocachedir",true)) 00686 nocachedir = true; 00687 00688 bool readonly = false; 00689 if (cmd->FindExist("-ro",true)) 00690 readonly = true; 00691 if (cmd->FindExist("-rw",true)) 00692 readonly = false; 00693 00694 std::string type="dir"; 00695 cmd->FindString("-t",type,true); 00696 std::transform(type.begin(), type.end(), type.begin(), ::tolower); 00697 bool iscdrom = (type =="cdrom"); //Used for mscdex bug cdrom label name emulation 00698 if (type=="floppy" || type=="dir" || type=="cdrom" || type =="overlay") { 00699 Bit16u sizes[4]; 00700 Bit8u mediaid; 00701 std::string str_size; 00702 if (type=="floppy") { 00703 str_size="512,1,2880,2880"; 00704 mediaid=0xF0; /* Floppy 1.44 media */ 00705 } else if (type=="dir" || type == "overlay") { 00706 // 512*32*32765==~500MB total size 00707 // 512*32*16000==~250MB total free size 00708 str_size="512,32,0,0"; 00709 mediaid=0xF8; /* Hard Disk */ 00710 } else if (type=="cdrom") { 00711 str_size="2048,1,65535,0"; 00712 mediaid=0xF8; /* Hard Disk */ 00713 } else { 00714 WriteOut(MSG_Get("PROGAM_MOUNT_ILL_TYPE"),type.c_str()); 00715 return; 00716 } 00717 /* Parse the free space in mb's (kb's for floppies) */ 00718 std::string mb_size; 00719 if(cmd->FindString("-freesize",mb_size,true)) { 00720 char teststr[1024]; 00721 Bit16u freesize = static_cast<Bit16u>(atoi(mb_size.c_str())); 00722 if (type=="floppy") { 00723 // freesize in kb 00724 sprintf(teststr,"512,1,2880,%d",freesize*1024/(512*1)>2880?2880:freesize*1024/(512*1)); 00725 } else { 00726 if (freesize>1919) freesize=1919; 00727 Bit16u numc=type=="cdrom"?1:32; 00728 Bit32u total_size_cyl=32765; 00729 Bit32u tmp=(Bit32u)freesize*1024*1024/(type=="cdrom"?2048*1:512*32); 00730 if (tmp>65534) numc=type=="cdrom"?(tmp+65535)/65536:64; 00731 Bit32u free_size_cyl=(Bit32u)freesize*1024*1024/(numc*(type=="cdrom"?2048:512)); 00732 if (free_size_cyl>65534) free_size_cyl=65534; 00733 if (total_size_cyl<free_size_cyl) total_size_cyl=free_size_cyl+10; 00734 if (total_size_cyl>65534) total_size_cyl=65534; 00735 sprintf(teststr,type=="cdrom"?"2048,%u,%u,%u":"512,%u,%u,%u",numc,total_size_cyl,free_size_cyl); 00736 } 00737 str_size=teststr; 00738 } 00739 00740 cmd->FindString("-size",str_size,true); 00741 char number[21] = { 0 }; const char* scan = str_size.c_str(); 00742 Bitu index = 0; Bitu count = 0; 00743 /* Parse the str_size string */ 00744 while (*scan && index < 20 && count < 4) { 00745 if (*scan==',') { 00746 number[index] = 0; 00747 sizes[count++] = atoi(number); 00748 index = 0; 00749 } else number[index++] = *scan; 00750 scan++; 00751 } 00752 if (count < 4) { 00753 number[index] = 0; //always goes correct as index is max 20 at this point. 00754 sizes[count] = atoi(number); 00755 } 00756 00757 // get the drive letter 00758 cmd->FindCommand(1,temp_line); 00759 if ((temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) goto showusage; 00760 int i_drive = toupper(temp_line[0]); 00761 if (!isalpha(i_drive)) goto showusage; 00762 if ((i_drive - 'A') >= DOS_DRIVES || (i_drive - 'A') < 0) goto showusage; 00763 drive = static_cast<char>(i_drive); 00764 00765 if (!cmd->FindCommand(2,temp_line)) goto showusage; 00766 if (!temp_line.size()) goto showusage; 00767 if (cmd->FindExist("-u",true)) { 00768 WriteOut(UnmountHelper(i_drive), toupper(i_drive)); 00769 if (!cmd->FindCommand(2,temp_line)||!temp_line.size()) return; 00770 } 00771 if(path_relative_to_last_config && control->configfiles.size() && !Cross::IsPathAbsolute(temp_line)) { 00772 std::string lastconfigdir(control->configfiles[control->configfiles.size()-1]); 00773 std::string::size_type pos = lastconfigdir.rfind(CROSS_FILESPLIT); 00774 if(pos == std::string::npos) pos = 0; //No directory then erase string 00775 lastconfigdir.erase(pos); 00776 if (lastconfigdir.length()) temp_line = lastconfigdir + CROSS_FILESPLIT + temp_line; 00777 } 00778 bool is_physfs = temp_line.find(':',((temp_line[0]|0x20) >= 'a' && (temp_line[0]|0x20) <= 'z')?2:0) != std::string::npos; 00779 struct stat test; 00780 //Win32 : strip tailing backslashes 00781 //os2: some special drive check 00782 //rest: substitute ~ for home 00783 bool failed = false; 00784 00785 (void)failed;// MAY BE UNUSED 00786 00787 #if defined (RISCOS) 00788 // If the user provided a RISC OS style path, convert it to a Unix style path 00789 // TODO: Disable UnixLib's automatic path conversion and use RISC OS style paths internally? 00790 if (temp_line.find('$',0) != std::string::npos) { 00791 char fname[PATH_MAX]; 00792 is_physfs = false; 00793 __unixify_std(temp_line.c_str(), fname, sizeof(fname), 0); 00794 temp_line = fname; 00795 } 00796 #endif 00797 00798 #if defined (WIN32) || defined(OS2) 00799 /* nothing */ 00800 #else 00801 // Linux: Convert backslash to forward slash 00802 if (!is_physfs && temp_line.size() > 0) { 00803 for (size_t i=0;i < temp_line.size();i++) { 00804 if (temp_line[i] == '\\') 00805 temp_line[i] = '/'; 00806 } 00807 } 00808 #endif 00809 00810 #if defined (WIN32) || defined(OS2) 00811 /* Removing trailing backslash if not root dir so stat will succeed */ 00812 if(temp_line.size() > 3 && temp_line[temp_line.size()-1]=='\\') temp_line.erase(temp_line.size()-1,1); 00813 if(temp_line.size() > 4 && temp_line[0]=='\\' && temp_line[1]=='\\' && temp_line[2]!='\\' && std::count(temp_line.begin()+3, temp_line.end(), '\\')==1) temp_line.append("\\"); 00814 if (!is_physfs && stat(temp_line.c_str(),&test)) { 00815 #endif 00816 #if defined(WIN32) 00817 // Nothing to do here. 00818 #elif defined (OS2) 00819 if (temp_line.size() <= 2) // Seems to be a drive. 00820 { 00821 failed = true; 00822 HFILE cdrom_fd = 0; 00823 ULONG ulAction = 0; 00824 00825 APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS, 00826 OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L); 00827 DosClose(cdrom_fd); 00828 if (rc != NO_ERROR && rc != ERROR_NOT_READY) 00829 { 00830 failed = true; 00831 } else { 00832 failed = false; 00833 } 00834 } 00835 } 00836 if (failed) { 00837 #else 00838 if (!is_physfs && stat(temp_line.c_str(),&test)) { 00839 failed = true; 00840 Cross::ResolveHomedir(temp_line); 00841 //Try again after resolving ~ 00842 if(!stat(temp_line.c_str(),&test)) failed = false; 00843 } 00844 if(failed) { 00845 #endif 00846 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_1"),temp_line.c_str()); 00847 return; 00848 } 00849 /* Not a switch so a normal directory/file */ 00850 if (!is_physfs && !S_ISDIR(test.st_mode)) { 00851 #ifdef OS2 00852 HFILE cdrom_fd = 0; 00853 ULONG ulAction = 0; 00854 00855 APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS, 00856 OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L); 00857 DosClose(cdrom_fd); 00858 if (rc != NO_ERROR && rc != ERROR_NOT_READY) { 00859 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str()); 00860 return; 00861 } 00862 #else 00863 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str()); 00864 return; 00865 #endif 00866 00867 } 00868 00869 if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT; 00870 Bit8u bit8size=(Bit8u) sizes[1]; 00871 if (type=="cdrom") { 00872 int num = -1; 00873 cmd->FindInt("-usecd",num,true); 00874 int error = 0; 00875 if (cmd->FindExist("-aspi",false)) { 00876 MSCDEX_SetCDInterface(CDROM_USE_ASPI, num); 00877 } else if (cmd->FindExist("-ioctl_dio",false)) { 00878 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); 00879 } else if (cmd->FindExist("-ioctl_dx",false)) { 00880 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num); 00881 #if defined (WIN32) 00882 } else if (cmd->FindExist("-ioctl_mci",false)) { 00883 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_MCI, num); 00884 #endif 00885 } else if (cmd->FindExist("-noioctl",false)) { 00886 MSCDEX_SetCDInterface(CDROM_USE_SDL, num); 00887 } else { 00888 #if defined (WIN32) 00889 // Check OS 00890 OSVERSIONINFO osi; 00891 osi.dwOSVersionInfoSize = sizeof(osi); 00892 GetVersionEx(&osi); 00893 if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>5)) { 00894 // Vista/above 00895 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num); 00896 } else { 00897 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); 00898 } 00899 #else 00900 MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); 00901 #endif 00902 } 00903 if (is_physfs) { 00904 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE_PHYSFS")); 00905 LOG_MSG("ERROR:This build does not support physfs"); 00906 return; 00907 } else { 00908 if (Drives[drive-'A']) { 00909 WriteOut(MSG_Get("PROGRAM_MOUNT_ALREADY_MOUNTED"),drive,Drives[drive-'A']->GetInfo()); 00910 return; 00911 } 00912 newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,error,options); 00913 } 00914 // Check Mscdex, if it worked out... 00915 switch (error) { 00916 case 0 : WriteOut(MSG_Get("MSCDEX_SUCCESS")); break; 00917 case 1 : WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); break; 00918 case 2 : WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED")); break; 00919 case 3 : WriteOut(MSG_Get("MSCDEX_ERROR_PATH")); break; 00920 case 4 : WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES")); break; 00921 case 5 : WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT")); break; 00922 default : WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR")); break; 00923 } 00924 if (error && error!=5) { 00925 delete newdrive; 00926 return; 00927 } 00928 } else { 00929 /* Give a warning when mount c:\ or the / */ 00930 #if defined (WIN32) || defined(OS2) 00931 if( (temp_line == "c:\\") || (temp_line == "C:\\") || 00932 (temp_line == "c:/") || (temp_line == "C:/") ) 00933 WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_WIN")); 00934 #else 00935 if(temp_line == "/") WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_OTHER")); 00936 #endif 00937 if (is_physfs) { 00938 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE_PHYSFS")); 00939 LOG_MSG("ERROR:This build does not support physfs"); 00940 return; 00941 } else if(type == "overlay") { 00942 //Ensure that the base drive exists: 00943 if (!Drives[drive-'A']) { 00944 WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_NO_BASE")); 00945 return; 00946 } 00947 localDrive* ldp = dynamic_cast<localDrive*>(Drives[drive-'A']); 00948 cdromDrive* cdp = dynamic_cast<cdromDrive*>(Drives[drive-'A']); 00949 if (!ldp || cdp) { 00950 WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_INCOMPAT_BASE")); 00951 return; 00952 } 00953 std::string base = ldp->getBasedir(); 00954 Bit8u o_error = 0; 00955 newdrive = new Overlay_Drive(base.c_str(),temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,o_error,options); 00956 //Erase old drive on succes 00957 if (newdrive) { 00958 if (o_error) { 00959 if (o_error == 1) WriteOut("No mixing of relative and absolute paths. Overlay failed.\n"); 00960 else if (o_error == 2) WriteOut("Overlay directory cannot be the same as underlying filesystem.\n"); 00961 else WriteOut("An error occurred when trying to create an overlay drive.\n"); 00962 delete newdrive; 00963 return; 00964 } else { 00965 Overlay_Drive* odrive=dynamic_cast<Overlay_Drive*>(newdrive); 00966 if (odrive!=NULL) { 00967 odrive->ovlnocachedir = nocachedir; 00968 odrive->ovlreadonly = readonly; 00969 } 00970 } 00971 delete Drives[drive-'A']; 00972 Drives[drive-'A'] = 0; 00973 } else { 00974 WriteOut("Overlay drive construction failed.\n"); 00975 return; 00976 } 00977 } else { 00978 newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,options); 00979 newdrive->nocachedir = nocachedir; 00980 newdrive->readonly = readonly; 00981 } 00982 } 00983 } else { 00984 WriteOut(MSG_Get("PROGRAM_MOUNT_ILL_TYPE"),type.c_str()); 00985 return; 00986 } 00987 if (Drives[drive-'A']) { 00988 WriteOut(MSG_Get("PROGRAM_MOUNT_ALREADY_MOUNTED"),drive,Drives[drive-'A']->GetInfo()); 00989 if (newdrive) delete newdrive; 00990 return; 00991 } 00992 if (!newdrive) E_Exit("DOS:Can't create drive"); 00993 Drives[drive-'A']=newdrive; 00994 DOS_EnableDriveMenu(drive); 00995 /* Set the correct media byte in the table */ 00996 mem_writeb(Real2Phys(dos.tables.mediaid)+((unsigned int)drive-'A')*dos.tables.dpb_size,newdrive->GetMediaByte()); 00997 if (!quiet) { 00998 if (type != "overlay") WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,newdrive->GetInfo()); 00999 else WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_STATUS"),temp_line.c_str(),drive); 01000 } 01001 /* check if volume label is given and don't allow it to updated in the future */ 01002 if (cmd->FindString("-label",label,true)) newdrive->SetLabel(label.c_str(),iscdrom,false); 01003 /* For hard drives set the label to DRIVELETTER_Drive. 01004 * For floppy drives set the label to DRIVELETTER_Floppy. 01005 * This way every drive except cdroms should get a label.*/ 01006 else if(type == "dir" || type == "overlay") { 01007 #if defined (WIN32) || defined(OS2) 01008 if(temp_line.size()==3 && toupper(drive) == toupper(temp_line[0])) { 01009 // automatic mount 01010 } else { 01011 label = drive; label += "_DRIVE"; 01012 newdrive->SetLabel(label.c_str(),iscdrom,false); 01013 } 01014 #endif 01015 } else if(type == "floppy") { 01016 #if defined (WIN32) || defined(OS2) 01017 if(temp_line.size()==3 && toupper(drive) == toupper(temp_line[0])) { 01018 // automatic mount 01019 } else { 01020 label = drive; label += "_FLOPPY"; 01021 newdrive->SetLabel(label.c_str(),iscdrom,true); 01022 } 01023 #endif 01024 } 01025 if(type == "floppy") incrementFDD(); 01026 return; 01027 showusage: 01028 #if defined (WIN32) || defined(OS2) 01029 WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"d:\\dosprogs","d:\\dosprogs","d:\\dosprogs","d:\\dosprogs","d:\\dosprogs","d:\\dosprogs"); 01030 #else 01031 WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"~/dosprogs","~/dosprogs","~/dosprogs","~/dosprogs","~/dosprogs","~/dosprogs"); 01032 #endif 01033 return; 01034 } 01035 }; 01036 01037 static void MOUNT_ProgramStart(Program * * make) { 01038 *make=new MOUNT; 01039 } 01040 01041 void GUI_Run(bool pressed); 01042 01043 class SHOWGUI : public Program { 01044 public: 01045 void Run(void) { 01046 if (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)) { 01047 WriteOut("Starts DOSBox-X's configuration GUI.\n\nSHOWGUI\n"); 01048 return; 01049 } 01050 GUI_Run(false); /* So that I don't have to run the keymapper on every setup of mine just to get the GUI --J.C */ 01051 } 01052 }; 01053 01054 static void SHOWGUI_ProgramStart(Program * * make) { 01055 *make=new SHOWGUI; 01056 } 01057 01058 extern bool custom_bios; 01059 extern Bit32u floppytype; 01060 extern bool dos_kernel_disabled; 01061 extern bool boot_debug_break; 01062 extern Bitu BIOS_bootfail_code_offset; 01063 01064 void DisableINT33(); 01065 void EMS_DoShutDown(); 01066 void XMS_DoShutDown(); 01067 void DOS_DoShutDown(); 01068 void GUS_DOS_Shutdown(); 01069 void SBLASTER_DOS_Shutdown(); 01070 01071 extern int swapInDisksSpecificDrive; 01072 01073 unsigned char PC98_ITF_ROM[0x8000]; 01074 bool PC98_ITF_ROM_init = false; 01075 unsigned char PC98_BANK_Select = 0x12; 01076 01077 #include "mem.h" 01078 #include "paging.h" 01079 01080 class PC98ITFPageHandler : public PageHandler { 01081 public: 01082 PC98ITFPageHandler() : PageHandler(PFLAG_READABLE|PFLAG_HASROM) {} 01083 PC98ITFPageHandler(Bitu flags) : PageHandler(flags) {} 01084 HostPt GetHostReadPt(Bitu phys_page) { 01085 return PC98_ITF_ROM+(phys_page&0x7)*MEM_PAGESIZE; 01086 } 01087 HostPt GetHostWritePt(Bitu phys_page) { 01088 return PC98_ITF_ROM+(phys_page&0x7)*MEM_PAGESIZE; 01089 } 01090 void writeb(PhysPt addr,Bit8u val){ 01091 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 01092 } 01093 void writew(PhysPt addr,Bit16u val){ 01094 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 01095 } 01096 void writed(PhysPt addr,Bit32u val){ 01097 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 01098 } 01099 }; 01100 01101 PC98ITFPageHandler mem_itf_rom; 01102 01103 bool FDC_AssignINT13Disk(unsigned char drv); 01104 void MEM_RegisterHandler(Bitu phys_page,PageHandler * handler,Bitu page_range); 01105 void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages); 01106 bool MEM_map_ROM_physmem(Bitu start,Bitu end); 01107 PageHandler &Get_ROM_page_handler(void); 01108 01109 // Normal BIOS is in the BIOS memory area 01110 // ITF is in it's own buffer, served by mem_itf_rom 01111 void PC98_BIOS_Bank_Switch(void) { 01112 if (PC98_BANK_Select == 0x00) { 01113 MEM_RegisterHandler(0xF8,&mem_itf_rom,0x8); 01114 } 01115 else { 01116 MEM_RegisterHandler(0xF8,&Get_ROM_page_handler(),0x8); 01117 } 01118 01119 PAGING_ClearTLB(); 01120 } 01121 01122 // BIOS behavior suggests a reset signal puts the BIOS back 01123 void PC98_BIOS_Bank_Switch_Reset(void) { 01124 LOG_MSG("PC-98 43Dh mapping BIOS back into top of RAM"); 01125 PC98_BANK_Select = 0x12; 01126 PC98_BIOS_Bank_Switch(); 01127 #if 0 01128 Bitu DEBUG_EnableDebugger(void); 01129 DEBUG_EnableDebugger(); 01130 #endif 01131 } 01132 01133 void pc98_43d_write(Bitu port,Bitu val,Bitu iolen) { 01134 (void)port; 01135 (void)iolen; 01136 01137 LOG_MSG("PC-98 43Dh BIOS bank switching write: 0x%02x",(unsigned int)val); 01138 01139 switch (val) { 01140 case 0x00: // ITF 01141 case 0x10: 01142 case 0x18: 01143 PC98_BANK_Select = 0x00; 01144 PC98_BIOS_Bank_Switch(); 01145 break; 01146 case 0x12: // BIOS 01147 PC98_BANK_Select = 0x12; 01148 PC98_BIOS_Bank_Switch(); 01149 break; 01150 default: 01151 LOG_MSG("PC-98 43Dh BIOS bank switching write: 0x%02x unknown value",(unsigned int)val); 01152 break; 01153 } 01154 } 01155 01163 class BOOT : public Program { 01164 public: 01165 BOOT() { 01166 for (size_t i=0;i < MAX_SWAPPABLE_DISKS;i++) newDiskSwap[i] = NULL; 01167 } 01168 virtual ~BOOT() { 01169 for (size_t i=0;i < MAX_SWAPPABLE_DISKS;i++) { 01170 if (newDiskSwap[i] != NULL) { 01171 newDiskSwap[i]->Release(); 01172 newDiskSwap[i] = NULL; 01173 } 01174 } 01175 } 01178 imageDisk* newDiskSwap[MAX_SWAPPABLE_DISKS] = {}; 01179 01180 private: 01181 01184 FILE *getFSFile_mounted(char const* filename, Bit32u *ksize, Bit32u *bsize, Bit8u *error) { 01185 //if return NULL then put in error the errormessage code if an error was requested 01186 bool tryload = (*error)?true:false; 01187 *error = 0; 01188 Bit8u drive; 01189 char fullname[DOS_PATHLENGTH]; 01190 01191 localDrive* ldp=0; 01192 bool readonly=wpcolon&&strlen(filename)>1&&filename[0]==':'; 01193 if (!DOS_MakeName(const_cast<char*>(readonly?filename+1:filename),fullname,&drive)) return NULL; 01194 01195 try { 01196 ldp=dynamic_cast<localDrive*>(Drives[drive]); 01197 if(!ldp) return NULL; 01198 01199 FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb"); 01200 if(tmpfile == NULL) { 01201 if (!tryload) *error=1; 01202 return NULL; 01203 } 01204 01205 // get file size 01206 fseek(tmpfile,0L, SEEK_END); 01207 *ksize = Bit32u(ftell(tmpfile) / 1024); 01208 *bsize = Bit32u(ftell(tmpfile)); 01209 fclose(tmpfile); 01210 01211 if (!readonly) 01212 tmpfile = ldp->GetSystemFilePtr(fullname, "rb+"); 01213 if(readonly || tmpfile == NULL) { 01214 // if (!tryload) *error=2; 01215 // return NULL; 01216 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED")); 01217 tmpfile = ldp->GetSystemFilePtr(fullname, "rb"); 01218 if(tmpfile == NULL) { 01219 if (!tryload) *error=1; 01220 return NULL; 01221 } 01222 } 01223 01224 return tmpfile; 01225 } 01226 catch(...) { 01227 return NULL; 01228 } 01229 } 01230 01233 FILE *getFSFile(char const * filename, Bit32u *ksize, Bit32u *bsize,bool tryload=false) { 01234 Bit8u error = tryload?1:0; 01235 FILE* tmpfile = getFSFile_mounted(filename,ksize,bsize,&error); 01236 if(tmpfile) return tmpfile; 01237 //File not found on mounted filesystem. Try regular filesystem 01238 std::string filename_s(filename); 01239 Cross::ResolveHomedir(filename_s); 01240 bool readonly=wpcolon&&filename_s.length()>1&&filename_s[0]==':'; 01241 if (!readonly) 01242 tmpfile = fopen(filename_s.c_str(),"rb+"); 01243 if(readonly || !tmpfile) { 01244 if( (tmpfile = fopen(readonly?filename_s.c_str()+1:filename_s.c_str(),"rb")) ) { 01245 //File exists; So can't be opened in correct mode => error 2 01246 // fclose(tmpfile); 01247 // if(tryload) error = 2; 01248 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED")); 01249 fseek(tmpfile,0L, SEEK_END); 01250 *ksize = Bit32u(ftell(tmpfile) / 1024); 01251 *bsize = Bit32u(ftell(tmpfile)); 01252 return tmpfile; 01253 } 01254 // Give the delayed errormessages from the mounted variant (or from above) 01255 if(error == 1) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_EXIST")); 01256 if(error == 2) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_OPEN")); 01257 return NULL; 01258 } 01259 fseek(tmpfile,0L, SEEK_END); 01260 *ksize = Bit32u(ftell(tmpfile) / 1024); 01261 *bsize = Bit32u(ftell(tmpfile)); 01262 return tmpfile; 01263 } 01264 01267 void printError(void) { 01268 WriteOut(MSG_Get("PROGRAM_BOOT_PRINT_ERROR")); 01269 } 01270 01271 public: 01272 01275 void Run(void) { 01276 std::string bios; 01277 std::string boothax_str; 01278 bool pc98_640x200 = true; 01279 bool pc98_show_graphics = false; 01280 bool bios_boot = false; 01281 bool swaponedrive = false; 01282 bool force = false; 01283 01284 //Hack To allow long commandlines 01285 ChangeToLongCmd(); 01286 01287 boot_debug_break = false; 01288 if (cmd->FindExist("-debug",true)) 01289 boot_debug_break = true; 01290 01291 if (cmd->FindExist("-swap-one-drive",true)) 01292 swaponedrive = true; 01293 01294 // debugging options 01295 if (cmd->FindExist("-pc98-640x200",true)) 01296 pc98_640x200 = true; 01297 if (cmd->FindExist("-pc98-640x400",true)) 01298 pc98_640x200 = false; 01299 if (cmd->FindExist("-pc98-graphics",true)) 01300 pc98_show_graphics = true; 01301 01302 if (cmd->FindExist("-force",true)) 01303 force = true; 01304 01305 if (cmd->FindString("-bios",bios,true)) 01306 bios_boot = true; 01307 01308 cmd->FindString("-boothax",boothax_str,true); 01309 01310 if (boothax_str == "msdos") // WARNING: For MS-DOS only, or the real-mode portion of Windows 95/98/ME. 01311 boothax = BOOTHAX_MSDOS; // do NOT use while in the graphical portion of Windows 95/98/ME especially a DOS VM. 01312 else if (boothax_str == "") 01313 boothax = BOOTHAX_NONE; 01314 else { 01315 WriteOut("Unknown boothax mode"); 01316 return; 01317 } 01318 01319 /* In secure mode don't allow people to boot stuff. 01320 * They might try to corrupt the data on it */ 01321 if(control->SecureMode()) { 01322 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); 01323 return; 01324 } 01325 01326 if (bios_boot) { 01327 Bit32u isz1,isz2; 01328 01329 if (bios.empty()) { 01330 WriteOut("Must specify BIOS image to boot\n"); 01331 return; 01332 } 01333 01334 // NOTES: 01335 // 01336 // Regarding PC-98 mode, you should use an older BIOS image. 01337 // The PC-9821 ROM image(s) I have appear to rely on bank 01338 // switching parts of itself to boot up and operate. 01339 // 01340 // Update: I found some PC-9801 ROM BIOS images online, which 01341 // ALSO seem to have a BIOS.ROM, ITF.ROM, etc... 01342 // 01343 // So, this command will not be able to run those 01344 // images until port 43Dh (the I/O port used for 01345 // bank switching) is implemented in DOSBox-X. 01346 // 01347 // In IBM PC/AT mode, this should hopefully allow using old 01348 // 386/486 BIOSes in DOSBox-X. 01349 01350 /* load it */ 01351 FILE *romfp = getFSFile(bios.c_str(), &isz1, &isz2); 01352 if (romfp == NULL) { 01353 WriteOut("Unable to open BIOS image\n"); 01354 return; 01355 } 01356 Bitu loadsz = (isz2 + 0xFU) & (~0xFU); 01357 if (loadsz == 0) loadsz = 0x10; 01358 if (loadsz > (IS_PC98_ARCH ? 0x18000u : 0x20000u)) loadsz = (IS_PC98_ARCH ? 0x18000u : 0x20000u); 01359 Bitu segbase = 0x100000 - loadsz; 01360 LOG_MSG("Loading BIOS image %s to 0x%lx, 0x%lx bytes",bios.c_str(),(unsigned long)segbase,(unsigned long)loadsz); 01361 fseek(romfp, 0, SEEK_SET); 01362 size_t readResult = fread(GetMemBase()+segbase,loadsz,1,romfp); 01363 fclose(romfp); 01364 if (readResult != 1) { 01365 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01366 return; 01367 } 01368 01369 // The PC-98 BIOS has a bank switching system where at least the last 32KB 01370 // can be switched to an Initial Firmware Test BIOS, which initializes the 01371 // system then switches back to the full 96KB visible during runtime. 01372 // 01373 // We can emulate the same if a file named ITF.ROM exists in the same directory 01374 // as the BIOS image we were given. 01375 // 01376 // To enable multiple ITFs per ROM image, we first try <bios filename>.itf.rom 01377 // before trying itf.rom, for the user's convenience. 01378 FILE *itffp; 01379 01380 itffp = getFSFile((bios + ".itf.rom").c_str(), &isz1, &isz2); 01381 if (itffp == NULL) itffp = getFSFile((bios + ".ITF.ROM").c_str(), &isz1, &isz2); 01382 if (itffp == NULL) itffp = getFSFile("itf.rom", &isz1, &isz2); 01383 if (itffp == NULL) itffp = getFSFile("ITF.ROM", &isz1, &isz2); 01384 01385 if (itffp != NULL && isz2 <= 0x8000ul) { 01386 LOG_MSG("Found ITF (initial firmware test) BIOS image (0x%lx bytes)",(unsigned long)isz2); 01387 01388 memset(PC98_ITF_ROM,0xFF,sizeof(PC98_ITF_ROM)); 01389 readResult = fread(PC98_ITF_ROM,isz2,1,itffp); 01390 fclose(itffp); 01391 if (readResult != 1) { 01392 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01393 return; 01394 } 01395 PC98_ITF_ROM_init = true; 01396 } 01397 01398 IO_RegisterWriteHandler(0x43D,pc98_43d_write,IO_MB); 01399 01400 custom_bios = true; 01401 01402 /* boot it */ 01403 throw int(8); 01404 } 01405 01406 bool bootbyDrive=false; 01407 FILE *usefile_1=NULL; 01408 FILE *usefile_2=NULL; 01409 Bitu i=0; 01410 Bit32u floppysize=0; 01411 Bit32u rombytesize_1=0; 01412 Bit32u rombytesize_2=0; 01413 Bit8u drive = 'A'; 01414 std::string cart_cmd=""; 01415 Bitu max_seg; 01416 01417 /* IBM PC: 01418 * CS:IP = 0000:7C00 Load = 07C0:0000 01419 * SS:SP = ??? 01420 * 01421 * PC-98: 01422 * CS:IP = 1FE0:0000 Load = 1FE0:0000 01423 * SS:SP = 0030:00D8 01424 */ 01425 Bitu stack_seg=IS_PC98_ARCH ? 0x0030 : 0x7000; 01426 Bitu load_seg;//=IS_PC98_ARCH ? 0x1FE0 : 0x07C0; 01427 01428 if (MEM_TotalPages() > 0x9C) 01429 max_seg = 0x9C00; 01430 else 01431 max_seg = MEM_TotalPages() << (12 - 4); 01432 01433 if ((stack_seg+0x20) > max_seg) 01434 stack_seg = max_seg - 0x20; 01435 01436 if(!cmd->GetCount()) { 01437 printError(); 01438 return; 01439 } else if (cmd->GetCount()==1) { 01440 cmd->FindCommand(1, temp_line); 01441 if (temp_line.length()==2&&toupper(temp_line[0])>='A'&&toupper(temp_line[0])<='Z'&&temp_line[1]==':') { 01442 drive=toupper(temp_line[0]); 01443 if ((drive != 'A') && (drive != 'C') && (drive != 'D')) { 01444 printError(); 01445 return; 01446 } 01447 bootbyDrive = true; 01448 } 01449 } 01450 01451 if (!bootbyDrive) 01452 while(i<cmd->GetCount()) { 01453 if(cmd->FindCommand((unsigned int)(i+1), temp_line)) { 01454 if ((temp_line == "/?") || (temp_line == "-?")) { 01455 printError(); 01456 return; 01457 } 01458 if((temp_line == "-l") || (temp_line == "-L")) { 01459 /* Specifying drive... next argument then is the drive */ 01460 bootbyDrive = true; 01461 i++; 01462 if(cmd->FindCommand((unsigned int)(i+1), temp_line)) { 01463 if (temp_line.length()==1&&isdigit(temp_line[0])) 01464 drive='A'+(temp_line[0]-'0'); 01465 else 01466 drive=toupper(temp_line[0]); 01467 if ((drive != 'A') && (drive != 'C') && (drive != 'D')) { 01468 printError(); 01469 return; 01470 } 01471 01472 } else { 01473 printError(); 01474 return; 01475 } 01476 i++; 01477 continue; 01478 } 01479 01480 if((temp_line == "-e") || (temp_line == "-E")) { 01481 /* Command mode for PCJr cartridges */ 01482 i++; 01483 if(cmd->FindCommand((unsigned int)(i + 1), temp_line)) { 01484 for(size_t ct = 0;ct < temp_line.size();ct++) temp_line[ct] = toupper(temp_line[ct]); 01485 cart_cmd = temp_line; 01486 } else { 01487 printError(); 01488 return; 01489 } 01490 i++; 01491 continue; 01492 } 01493 01494 if (i >= MAX_SWAPPABLE_DISKS) { 01495 return; //TODO give a warning. 01496 } 01497 01498 Bit32u rombytesize=0; 01499 bool readonly=wpcolon&&temp_line.length()>1&&temp_line[0]==':'; 01500 WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"), readonly?temp_line.c_str()+1:temp_line.c_str()); 01501 FILE *usefile = getFSFile(temp_line.c_str(), &floppysize, &rombytesize); 01502 if(usefile != NULL) { 01503 char tmp[256]; 01504 01505 if (newDiskSwap[i] != NULL) { 01506 newDiskSwap[i]->Release(); 01507 newDiskSwap[i] = NULL; 01508 } 01509 01510 fseeko64(usefile, 0L, SEEK_SET); 01511 size_t readResult = fread(tmp,256,1,usefile); // look for magic signatures 01512 if (readResult != 1) { 01513 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01514 return; 01515 } 01516 01517 const char *ext = strrchr(temp_line.c_str(),'.'), *fname=readonly?temp_line.c_str()+1:temp_line.c_str(); 01518 01519 if (ext != NULL && !strcasecmp(ext, ".d88")) { 01520 newDiskSwap[i] = new imageDiskD88(usefile, (Bit8u *)fname, floppysize, false); 01521 } 01522 else if (!memcmp(tmp,"VFD1.",5)) { /* FDD files */ 01523 newDiskSwap[i] = new imageDiskVFD(usefile, (Bit8u *)fname, floppysize, false); 01524 } 01525 else if (!memcmp(tmp,"T98FDDIMAGE.R0\0\0",16)) { 01526 newDiskSwap[i] = new imageDiskNFD(usefile, (Bit8u *)fname, floppysize, false, 0); 01527 } 01528 else if (!memcmp(tmp,"T98FDDIMAGE.R1\0\0",16)) { 01529 newDiskSwap[i] = new imageDiskNFD(usefile, (Bit8u *)fname, floppysize, false, 1); 01530 } 01531 else { 01532 newDiskSwap[i] = new imageDisk(usefile, (Bit8u *)fname, floppysize, false); 01533 } 01534 newDiskSwap[i]->Addref(); 01535 if (newDiskSwap[i]->active && !newDiskSwap[i]->hardDrive) incrementFDD(); //moved from imageDisk constructor 01536 01537 if (usefile_1==NULL) { 01538 usefile_1=usefile; 01539 rombytesize_1=rombytesize; 01540 } else { 01541 usefile_2=usefile; 01542 rombytesize_2=rombytesize; 01543 } 01544 } else { 01545 WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_NOT_OPEN"), readonly?temp_line.c_str()+1:temp_line.c_str()); 01546 return; 01547 } 01548 01549 } 01550 i++; 01551 } 01552 01553 if (!bootbyDrive) { 01554 if (i == 0) { 01555 WriteOut("No images specified"); 01556 return; 01557 } 01558 01559 if (i > 1) { 01560 /* if more than one image is given, then this drive becomes the focus of the swaplist */ 01561 if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive != (drive - 65)) { 01562 WriteOut("Multiple disk images specified and another drive is already connected to the swap list"); 01563 return; 01564 } 01565 else if (swapInDisksSpecificDrive < 0 && swaponedrive) { 01566 swapInDisksSpecificDrive = drive - 65; 01567 } 01568 01569 /* transfer to the diskSwap array */ 01570 for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) { 01571 if (diskSwap[si] != NULL) { 01572 diskSwap[si]->Release(); 01573 diskSwap[si] = NULL; 01574 } 01575 01576 diskSwap[si] = newDiskSwap[si]; 01577 newDiskSwap[si] = NULL; 01578 } 01579 01580 swapPosition = 0; 01581 swapInDisks(); 01582 } 01583 else { 01584 if (swapInDisksSpecificDrive == (drive - 65)) { 01585 /* if we're replacing the diskSwap drive clear it now */ 01586 for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) { 01587 if (diskSwap[si] != NULL) { 01588 diskSwap[si]->Release(); 01589 diskSwap[si] = NULL; 01590 } 01591 } 01592 01593 swapInDisksSpecificDrive = -1; 01594 } 01595 01596 /* attach directly without using the swap list */ 01597 if (imageDiskList[drive-65] != NULL) { 01598 imageDiskChange[drive-65] = true; 01599 imageDiskList[drive-65]->Release(); 01600 imageDiskList[drive-65] = NULL; 01601 } 01602 01603 imageDiskList[drive-65] = newDiskSwap[0]; 01604 newDiskSwap[0] = NULL; 01605 } 01606 } 01607 01608 if(imageDiskList[drive-65]==NULL) { 01609 WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive); 01610 return; 01611 } 01612 01613 // .D88 images come from PC-88 which usually means the boot sector is full 01614 // of Z80 executable code, therefore it's very unlikely the boot sector will 01615 // work with our x86 emulation! 01616 // 01617 // If the user is REALLY REALLY SURE they want to try executing Z80 bootsector 01618 // code as x86, they're free to use --force. 01619 // 01620 // However PC-98 games are also distributed as .D88 images and therefore 01621 // we probably CAN boot the image. 01622 // 01623 // It depends on the fd_type field of the image. 01624 if (!force && imageDiskList[drive-65]->class_id == imageDisk::ID_D88) { 01625 if (reinterpret_cast<imageDiskD88*>(imageDiskList[drive-65])->fd_type_major == imageDiskD88::DISKTYPE_2D) { 01626 WriteOut("The D88 image appears to target PC-88 and cannot be booted."); 01627 return; 01628 } 01629 } 01630 01631 01632 bootSector bootarea; 01633 01634 if (imageDiskList[drive-65]->getSectSize() > sizeof(bootarea)) { 01635 WriteOut("Bytes/sector too large"); 01636 return; 01637 } 01638 01639 /* clear the disk change flag. 01640 * Most OSes don't expect the disk change error signal when they first boot up */ 01641 imageDiskChange[drive-65] = false; 01642 01643 bool has_read = false; 01644 bool pc98_sect128 = false; 01645 unsigned int bootsize = imageDiskList[drive-65]->getSectSize(); 01646 01647 if (!has_read && IS_PC98_ARCH && drive < 'C') { 01648 /* this may be one of those odd FDD images where track 0, head 0 is all 128-byte sectors 01649 * and the rest of the disk is 256-byte sectors. */ 01650 if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea, 128) == 0 && 01651 imageDiskList[drive - 65]->Read_Sector(0, 0, 2, (Bit8u *)&bootarea + 128, 128) == 0 && 01652 imageDiskList[drive - 65]->Read_Sector(0, 0, 3, (Bit8u *)&bootarea + 256, 128) == 0 && 01653 imageDiskList[drive - 65]->Read_Sector(0, 0, 4, (Bit8u *)&bootarea + 384, 128) == 0) { 01654 LOG_MSG("First sector is 128 byte/sector. Booting from first four sectors."); 01655 has_read = true; 01656 bootsize = 512; // 128 x 4 01657 pc98_sect128 = true; 01658 } 01659 } 01660 01661 if (!has_read && IS_PC98_ARCH && drive < 'C') { 01662 /* another nonstandard one with track 0 having 256 bytes/sector while the rest have 1024 bytes/sector */ 01663 if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea, 256) == 0 && 01664 imageDiskList[drive - 65]->Read_Sector(0, 0, 2, (Bit8u *)&bootarea + 256, 256) == 0 && 01665 imageDiskList[drive - 65]->Read_Sector(0, 0, 3, (Bit8u *)&bootarea + 512, 256) == 0 && 01666 imageDiskList[drive - 65]->Read_Sector(0, 0, 4, (Bit8u *)&bootarea + 768, 256) == 0) { 01667 LOG_MSG("First sector is 256 byte/sector. Booting from first two sectors."); 01668 has_read = true; 01669 bootsize = 1024; // 256 x 4 01670 pc98_sect128 = true; 01671 } 01672 } 01673 01674 /* NTS: Load address is 128KB - sector size */ 01675 load_seg=IS_PC98_ARCH ? (0x2000 - (bootsize/16U)) : 0x07C0; 01676 01677 if (!has_read) { 01678 if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea) != 0) { 01679 WriteOut("Error reading drive"); 01680 return; 01681 } 01682 } 01683 01684 Bitu pcjr_hdr_length = 0; 01685 Bit8u pcjr_hdr_type = 0; // not a PCjr cartridge 01686 if ((bootarea.rawdata[0]==0x50) && (bootarea.rawdata[1]==0x43) && (bootarea.rawdata[2]==0x6a) && (bootarea.rawdata[3]==0x72)) { 01687 pcjr_hdr_type = 1; // JRipCart 01688 pcjr_hdr_length = 0x200; 01689 } else if ((bootarea.rawdata[56]==0x50) && (bootarea.rawdata[57]==0x43) && (bootarea.rawdata[58]==0x4a) && (bootarea.rawdata[59]==0x52)) { 01690 pcjr_hdr_type = 2; // PCJRCart 01691 pcjr_hdr_length = 0x80; 01692 } 01693 01694 if (pcjr_hdr_type > 0) { 01695 if (machine!=MCH_PCJR) WriteOut(MSG_Get("PROGRAM_BOOT_CART_WO_PCJR")); 01696 else { 01697 Bit8u rombuf[65536]; 01698 Bits cfound_at=-1; 01699 if (cart_cmd!="") { 01700 /* read cartridge data into buffer */ 01701 fseek(usefile_1, (long)pcjr_hdr_length, SEEK_SET); 01702 size_t readResult = fread(rombuf, 1, rombytesize_1-pcjr_hdr_length, usefile_1); 01703 if (readResult != rombytesize_1 - pcjr_hdr_length) { 01704 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01705 return; 01706 } 01707 01708 char cmdlist[1024]; 01709 cmdlist[0]=0; 01710 Bitu ct=6; 01711 Bits clen=rombuf[ct]; 01712 char buf[257]; 01713 if (cart_cmd=="?") { 01714 while (clen!=0) { 01715 safe_strncpy(buf,(char*)&rombuf[ct+1],clen); 01716 buf[clen]=0; 01717 upcase(buf); 01718 strcat(cmdlist," "); 01719 strcat(cmdlist,buf); 01720 ct+=1u+(Bitu)clen+3u; 01721 if (ct>sizeof(cmdlist)) break; 01722 clen=rombuf[ct]; 01723 } 01724 if (ct>6) { 01725 WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist); 01726 } else { 01727 WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS")); 01728 } 01729 for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) { 01730 if(diskSwap[dct]!=NULL) { 01731 diskSwap[dct]->Release(); 01732 diskSwap[dct]=NULL; 01733 } 01734 } 01735 //fclose(usefile_1); //delete diskSwap closes the file 01736 return; 01737 } else { 01738 while (clen!=0) { 01739 safe_strncpy(buf,(char*)&rombuf[ct+1],clen); 01740 buf[clen]=0; 01741 upcase(buf); 01742 strcat(cmdlist," "); 01743 strcat(cmdlist,buf); 01744 ct+=1u+(Bitu)clen; 01745 01746 if (cart_cmd==buf) { 01747 cfound_at=(Bits)ct; 01748 break; 01749 } 01750 01751 ct+=3; 01752 if (ct>sizeof(cmdlist)) break; 01753 clen=rombuf[ct]; 01754 } 01755 if (cfound_at<=0) { 01756 if (ct>6) { 01757 WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist); 01758 } else { 01759 WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS")); 01760 } 01761 for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) { 01762 if(diskSwap[dct]!=NULL) { 01763 diskSwap[dct]->Release(); 01764 diskSwap[dct]=NULL; 01765 } 01766 } 01767 //fclose(usefile_1); //Delete diskSwap closes the file 01768 return; 01769 } 01770 } 01771 } 01772 01773 void PreparePCJRCartRom(void); 01774 PreparePCJRCartRom(); 01775 01776 if (usefile_1==NULL) return; 01777 01778 Bit32u sz1,sz2; 01779 FILE *tfile = getFSFile("system.rom", &sz1, &sz2, true); 01780 if (tfile!=NULL) { 01781 fseek(tfile, 0x3000L, SEEK_SET); 01782 Bit32u drd=(Bit32u)fread(rombuf, 1, 0xb000, tfile); 01783 if (drd==0xb000) { 01784 for(i=0;i<0xb000;i++) phys_writeb((PhysPt)(0xf3000+i),rombuf[i]); 01785 } 01786 fclose(tfile); 01787 } 01788 01789 if (usefile_2!=NULL) { 01790 unsigned int romseg_pt=0; 01791 01792 fseek(usefile_2, 0x0L, SEEK_SET); 01793 size_t readResult = fread(rombuf, 1, pcjr_hdr_length, usefile_2); 01794 if (readResult != pcjr_hdr_length) { 01795 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01796 return; 01797 } 01798 01799 if (pcjr_hdr_type == 1) { 01800 romseg_pt=host_readw(&rombuf[0x1ce]); 01801 } else { 01802 fseek(usefile_2, 0x61L, SEEK_SET); 01803 int scanResult = fscanf(usefile_2, "%4x", &romseg_pt); 01804 if (scanResult == 0) { 01805 LOG(LOG_IO, LOG_ERROR) ("Scanning error in Run\n"); 01806 return; 01807 } 01808 } 01809 /* read cartridge data into buffer */ 01810 fseek(usefile_2, (long)pcjr_hdr_length, SEEK_SET); 01811 readResult = fread(rombuf, 1, rombytesize_2-pcjr_hdr_length, usefile_2); 01812 if (readResult != rombytesize_2 - pcjr_hdr_length) { 01813 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01814 return; 01815 } 01816 //fclose(usefile_2); //usefile_2 is in diskSwap structure which should be deleted to close the file 01817 01818 /* write cartridge data into ROM */ 01819 for(i=0;i<rombytesize_2-pcjr_hdr_length;i++) phys_writeb((PhysPt)((romseg_pt<<4)+i),rombuf[i]); 01820 } 01821 01822 unsigned int romseg=0; 01823 01824 fseek(usefile_1, 0x0L, SEEK_SET); 01825 size_t readResult = fread(rombuf, 1, pcjr_hdr_length, usefile_1); 01826 if (readResult != pcjr_hdr_length) { 01827 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01828 return; 01829 } 01830 01831 if (pcjr_hdr_type == 1) { 01832 romseg=host_readw(&rombuf[0x1ce]); 01833 } else { 01834 fseek(usefile_1, 0x61L, SEEK_SET); 01835 int scanResult = fscanf(usefile_1, "%4x", &romseg); 01836 if (scanResult == 0) { 01837 LOG(LOG_IO, LOG_ERROR) ("Scanning error in Run\n"); 01838 return; 01839 } 01840 } 01841 /* read cartridge data into buffer */ 01842 fseek(usefile_1,(long)pcjr_hdr_length, SEEK_SET); 01843 readResult = fread(rombuf, 1, rombytesize_1-pcjr_hdr_length, usefile_1); 01844 if (readResult != rombytesize_1 - pcjr_hdr_length) { 01845 LOG(LOG_IO, LOG_ERROR) ("Reading error in Run\n"); 01846 return; 01847 } 01848 01849 //fclose(usefile_1); //usefile_1 is in diskSwap structure which should be deleted to close the file 01850 /* write cartridge data into ROM */ 01851 for(i=0;i<rombytesize_1-pcjr_hdr_length;i++) phys_writeb((PhysPt)((romseg<<4)+i),rombuf[i]); 01852 01853 //Close cardridges 01854 for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) { 01855 if(diskSwap[dct]!=NULL) { 01856 diskSwap[dct]->Release(); 01857 diskSwap[dct]=NULL; 01858 } 01859 } 01860 01861 01862 if (cart_cmd=="") { 01863 Bit32u old_int18=mem_readd(0x60); 01864 /* run cartridge setup */ 01865 SegSet16(ds,romseg); 01866 SegSet16(es,romseg); 01867 SegSet16(ss,0x8000); 01868 reg_esp=0xfffe; 01869 CALLBACK_RunRealFar(romseg,0x0003); 01870 01871 Bit32u new_int18=mem_readd(0x60); 01872 if (old_int18!=new_int18) { 01873 /* boot cartridge (int18) */ 01874 SegSet16(cs,RealSeg(new_int18)); 01875 reg_ip = RealOff(new_int18); 01876 } 01877 } else { 01878 if (cfound_at>0) { 01879 /* run cartridge setup */ 01880 SegSet16(ds,dos.psp()); 01881 SegSet16(es,dos.psp()); 01882 CALLBACK_RunRealFar((Bit16u)romseg,(Bit16u)cfound_at); 01883 } 01884 } 01885 } 01886 } else { 01887 extern const char* RunningProgram; 01888 01889 if (max_seg < 0x0800) { 01890 /* TODO: For the adventerous, add a configuration option or command line switch to "BOOT" 01891 * that allows us to boot the guest OS anyway in a manner that is non-standard. */ 01892 WriteOut("32KB of RAM is required to boot a guest OS\n"); 01893 return; 01894 } 01895 01896 /* Other versions of MS-DOS/PC-DOS have their own requirements about memory: 01897 * - IBM PC-DOS 1.0/1.1: not too picky, will boot with as little as 32KB even though 01898 * it was intended for the average model with 64KB of RAM. 01899 * 01900 * - IBM PC-DOS 2.1: requires at least 44KB of RAM. will crash on boot otherwise. 01901 * 01902 * - MS-DOS 3.2: requires at least 64KB to boot without crashing, 80KB to make it 01903 * to the command line without halting at "configuration too big for 01904 * memory"*/ 01905 01906 /* TODO: Need a configuration option or a BOOT command option where the user can 01907 * dictate where we put the stack: if we put it at 0x7000 or top of memory 01908 * (default) or just below the boot sector, or... */ 01909 01910 if((bootarea.rawdata[0]==0) && (bootarea.rawdata[1]==0)) { 01911 WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive); 01912 return; 01913 } 01914 01915 char msg[30]; 01916 const Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);; 01917 BIOS_NCOLS; 01918 (void)ncols; 01919 strcpy(msg, CURSOR_POS_COL(page)>0?"\r\n":""); 01920 strcat(msg, "Booting from drive "); 01921 strcat(msg, std::string(1, drive).c_str()); 01922 strcat(msg, "...\r\n"); 01923 Bit16u s = (Bit16u)strlen(msg); 01924 DOS_WriteFile(STDERR,(Bit8u*)msg,&s); 01925 01926 if (IS_PC98_ARCH) { 01927 for(i=0;i<bootsize;i++) real_writeb((Bit16u)load_seg, (Bit16u)i, bootarea.rawdata[i]); 01928 } 01929 else { 01930 for(i=0;i<bootsize;i++) real_writeb(0, (Bit16u)((load_seg<<4) + i), bootarea.rawdata[i]); 01931 } 01932 01933 /* debug */ 01934 LOG_MSG("Booting guest OS stack_seg=0x%04x load_seg=0x%04x\n",(int)stack_seg,(int)load_seg); 01935 RunningProgram = "Guest OS"; 01936 01937 if (drive == 'A' || drive == 'B') { 01938 FDC_AssignINT13Disk(drive - 'A'); 01939 if (!IS_PC98_ARCH) incrementFDD(); 01940 } 01941 01942 /* NTS: IBM PC and PC-98 both use DMA channel 2 for the floppy, though according to 01943 * Neko Project II source code, DMA 3 is used for the double density drives (but we don't emulate that yet) */ 01944 /* create appearance of floppy drive DMA usage (Demon's Forge) */ 01945 if (IS_PC98_ARCH) { 01946 GetDMAChannel(2)->tcount=true; 01947 GetDMAChannel(3)->tcount=true; 01948 } 01949 else { 01950 if (!IS_TANDY_ARCH && floppysize!=0) GetDMAChannel(2)->tcount=true; 01951 } 01952 01953 /* standard method */ 01954 if (IS_PC98_ARCH) { 01955 /* Based on a CPU register dump at boot time on a real PC-9821: 01956 * 01957 * DUMP: 01958 * 01959 * SP: 00D8 SS: 0030 ES: 1FE0 DS: 0000 CS: 1FE0 FL: 0246 BP: 0000 01960 * DI: 055C SI: 1FE0 DX: 0001 CX: 0200 BX: 0200 AX: 0030 IP: 0000 01961 * 01962 * So: 01963 * 01964 * Stack at 0030:00D8 01965 * 01966 * CS:IP at load_seg:0000 01967 * 01968 * load_seg at 0x1FE0 which on the original 128KB PC-98 puts it at the top of memory 01969 * 01970 */ 01971 SegSet16(cs, (Bit16u)load_seg); 01972 SegSet16(ds, 0x0000); 01973 SegSet16(es, (Bit16u)load_seg); 01974 reg_ip = 0; 01975 reg_ebx = 0x200; 01976 reg_esp = 0xD8; 01977 /* set up stack at a safe place */ 01978 SegSet16(ss, (Bit16u)stack_seg); 01979 reg_esi = (Bit32u)load_seg; 01980 reg_ecx = 0x200; 01981 reg_ebp = 0; 01982 reg_eax = 0x30; 01983 reg_edx = 0x1; 01984 01985 /* It seems 640x200 8-color digital mode is the state of the graphics hardware when the 01986 * BIOS boots the OS, and some games like Ys II assume the hardware is in this state. 01987 * 01988 * If I am wrong, you can pass --pc98-640x400 as a command line option to disable this. */ 01989 if (pc98_640x200) { 01990 reg_eax = 0x4200; // setup 640x200 graphics 01991 reg_ecx = 0x8000; // lower 01992 CALLBACK_RunRealInt(0x18); 01993 } 01994 else { 01995 reg_eax = 0x4200; // setup 640x400 graphics 01996 reg_ecx = 0xC000; // full 01997 CALLBACK_RunRealInt(0x18); 01998 } 01999 02000 /* Some HDI images of Orange House games need this option because it assumes NEC MOUSE.COM 02001 * behavior where mouse driver init and reset show the graphics layer. Unfortunately the HDI 02002 * image uses QMOUSE which does not show the graphics layer. Use this option with those 02003 * HDI images to make them playable anyway. */ 02004 if (pc98_show_graphics) { 02005 reg_eax = 0x4000; // show graphics 02006 CALLBACK_RunRealInt(0x18); 02007 } 02008 else { 02009 reg_eax = 0x4100; // hide graphics (normal state of graphics layer on startup). INT 33h emulation might have enabled it. 02010 CALLBACK_RunRealInt(0x18); 02011 } 02012 02013 /* PC-98 MS-DOS boot sector behavior suggests that the BIOS does a CALL FAR 02014 * to the boot sector, and the boot sector can RETF back to the BIOS on failure. */ 02015 CPU_Push16((Bit16u)(BIOS_bootfail_code_offset >> 4)); /* segment */ 02016 CPU_Push16(BIOS_bootfail_code_offset & 0xF); /* offset */ 02017 02018 /* clear the text layer */ 02019 for (i=0;i < (80*25*2);i += 2) { 02020 mem_writew((PhysPt)(0xA0000+i),0x0000); 02021 mem_writew((PhysPt)(0xA2000+i),0x00E1); 02022 } 02023 02024 /* hide the cursor */ 02025 void PC98_show_cursor(bool show); 02026 PC98_show_cursor(false); 02027 02028 /* There is a byte at 0x584 that describes the boot drive + type. 02029 * This is confirmed in Neko Project II source and by the behavior 02030 * of an MS-DOS boot disk formatted by a PC-98 system. 02031 * 02032 * There are three values for three different floppy formats, and 02033 * one for hard drives */ 02034 Bit32u heads,cyls,sects,ssize; 02035 02036 imageDiskList[drive-65]->Get_Geometry(&heads,&cyls,§s,&ssize); 02037 02038 Bit8u RDISK_EQUIP = 0; /* 488h (ref. PC-9800 Series Technical Data Book - BIOS 1992 page 233 */ 02039 /* bits [7:4] = 640KB FD drives 3:0 02040 * bits [3:0] = 1MB FD drives 3:0 */ 02041 Bit8u F2HD_MODE = 0; /* 493h (ref. PC-9800 Series Technical Data Book - BIOS 1992 page 233 */ 02042 /* bits [7:4] = 640KB FD drives 3:0 ?? 02043 * bits [3:0] = 1MB FD drives 3:0 ?? */ 02044 Bit8u F2DD_MODE = 0; /* 5CAh (ref. PC-9800 Series Technical Data Book - BIOS 1992 page 233 */ 02045 /* bits [7:4] = 640KB FD drives 3:0 ?? 02046 * bits [3:0] = 1MB FD drives 3:0 ?? */ 02047 Bit16u disk_equip = 0, disk_equip_144 = 0; 02048 Bit8u scsi_equip = 0; 02049 02050 /* FIXME: MS-DOS appears to be able to see disk image B: but only 02051 * if the disk format is the same, for some reason. 02052 * 02053 * So, apparently you cannot put a 1.44MB image in drive A: 02054 * and a 1.2MB image in drive B: */ 02055 02056 for (i=0;i < 2;i++) { 02057 if (imageDiskList[i] != NULL) { 02058 disk_equip |= (0x0111u << i); /* 320KB[15:12] 1MB[11:8] 640KB[7:4] unit[1:0] */ 02059 disk_equip_144 |= (1u << i); 02060 F2HD_MODE |= (0x11u << i); 02061 } 02062 } 02063 02064 for (i=0;i < 2;i++) { 02065 if (imageDiskList[i+2] != NULL) { 02066 scsi_equip |= (1u << i); 02067 02068 Bit16u m = 0x460u + ((Bit16u)i * 4u); 02069 02070 mem_writeb(m+0u,sects); 02071 mem_writeb(m+1u,heads); 02072 mem_writew(m+2u,(cyls & 0xFFFu) + (ssize == 512u ? 0x1000u : 0u) + (ssize == 1024u ? 0x2000u : 0) + 0x8000u/*NP2:hwsec*/); 02073 } 02074 } 02075 02076 mem_writew(0x55C,disk_equip); /* disk equipment (drive 0 is present) */ 02077 mem_writew(0x5AE,disk_equip_144); /* disk equipment (drive 0 is present, 1.44MB) */ 02078 mem_writeb(0x482,scsi_equip); 02079 mem_writeb(0x488,RDISK_EQUIP); /* RAM disk equip */ 02080 mem_writeb(0x493,F2HD_MODE); 02081 mem_writeb(0x5CA,F2DD_MODE); 02082 02083 if (drive >= 'C') { 02084 /* hard drive */ 02085 mem_writeb(0x584,0xA0/*type*/ + (drive - 'C')/*drive*/); 02086 } 02087 else if ((ssize == 1024 && heads == 2 && cyls == 77 && sects == 8) || pc98_sect128) { 02088 mem_writeb(0x584,0x90/*type*/ + (drive - 65)/*drive*/); /* 1.2MB 3-mode */ 02089 } 02090 else if (ssize == 512 && heads == 2 && cyls == 80 && sects == 18) { 02091 mem_writeb(0x584,0x30/*type*/ + (drive - 65)/*drive*/); /* 1.44MB */ 02092 } 02093 else { 02094 // FIXME 02095 LOG_MSG("PC-98 boot: Unable to determine boot drive type for ssize=%u heads=%u cyls=%u sects=%u. Guessing.", 02096 ssize,heads,cyls,sects); 02097 02098 mem_writeb(0x584,(ssize < 1024 ? 0x30 : 0x90)/*type*/ + (drive - 65)/*drive*/); 02099 } 02100 } 02101 else { 02102 SegSet16(cs, 0); 02103 SegSet16(ds, 0); 02104 SegSet16(es, 0); 02105 reg_ip = (Bit16u)(load_seg<<4); 02106 reg_ebx = (Bit32u)(load_seg<<4); //Real code probably uses bx to load the image 02107 reg_esp = 0x100; 02108 /* set up stack at a safe place */ 02109 SegSet16(ss, (Bit16u)stack_seg); 02110 reg_esi = 0; 02111 reg_ecx = 1; 02112 reg_ebp = 0; 02113 reg_eax = 0; 02114 reg_edx = 0; //Head 0 02115 if (drive >= 'A' && drive <= 'B') 02116 reg_edx += (unsigned int)(drive-'A'); 02117 else if (drive >= 'C' && drive <= 'Z') 02118 reg_edx += 0x80u+(unsigned int)(drive-'C'); 02119 } 02120 #ifdef __WIN32__ 02121 // let menu know it boots 02122 menu.boot=true; 02123 #endif 02124 02125 /* forcibly exit the shell, the DOS kernel, and anything else by throwing an exception */ 02126 throw int(2); 02127 } 02128 } 02129 }; 02130 02131 static void BOOT_ProgramStart(Program * * make) { 02132 *make=new BOOT; 02133 } 02134 02135 class LOADROM : public Program { 02136 public: 02137 void Run(void) { 02138 if (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)) { 02139 WriteOut(MSG_Get("PROGRAM_LOADROM_HELP")); 02140 return; 02141 } 02142 02143 if (!(cmd->FindCommand(1, temp_line))) { 02144 WriteOut(MSG_Get("PROGRAM_LOADROM_SPECIFY_FILE")); 02145 return; 02146 } 02147 02148 Bit8u drive; 02149 char fullname[DOS_PATHLENGTH]; 02150 localDrive* ldp=0; 02151 if (!DOS_MakeName((char *)temp_line.c_str(),fullname,&drive)) return; 02152 02153 try { 02154 /* try to read ROM file into buffer */ 02155 ldp=dynamic_cast<localDrive*>(Drives[drive]); 02156 if(!ldp) return; 02157 02158 FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb"); 02159 if(tmpfile == NULL) { 02160 WriteOut(MSG_Get("PROGRAM_LOADROM_CANT_OPEN")); 02161 return; 02162 } 02163 fseek(tmpfile, 0L, SEEK_END); 02164 if (ftell(tmpfile)>0x8000) { 02165 WriteOut(MSG_Get("PROGRAM_LOADROM_TOO_LARGE")); 02166 fclose(tmpfile); 02167 return; 02168 } 02169 fseek(tmpfile, 0L, SEEK_SET); 02170 Bit8u rom_buffer[0x8000]; 02171 Bitu data_read = fread(rom_buffer, 1, 0x8000, tmpfile); 02172 fclose(tmpfile); 02173 02174 /* try to identify ROM type */ 02175 PhysPt rom_base = 0; 02176 if (data_read >= 0x4000 && rom_buffer[0] == 0x55 && rom_buffer[1] == 0xaa && 02177 (rom_buffer[3] & 0xfc) == 0xe8 && strncmp((char*)(&rom_buffer[0x1e]), "IBM", 3) == 0) { 02178 02179 if (!IS_EGAVGA_ARCH) { 02180 WriteOut(MSG_Get("PROGRAM_LOADROM_INCOMPATIBLE")); 02181 return; 02182 } 02183 rom_base = PhysMake(0xc000, 0); // video BIOS 02184 } 02185 else if (data_read == 0x8000 && rom_buffer[0] == 0xe9 && rom_buffer[1] == 0x8f && 02186 rom_buffer[2] == 0x7e && strncmp((char*)(&rom_buffer[0x4cd4]), "IBM", 3) == 0) { 02187 02188 rom_base = PhysMake(0xf600, 0); // BASIC 02189 } 02190 02191 if (rom_base) { 02192 /* write buffer into ROM */ 02193 for (Bitu i=0; i<data_read; i++) phys_writeb((PhysPt)(rom_base + i), rom_buffer[i]); 02194 02195 if (rom_base == 0xc0000) { 02196 /* initialize video BIOS */ 02197 phys_writeb(PhysMake(0xf000, 0xf065), 0xcf); 02198 reg_flags &= ~FLAG_IF; 02199 CALLBACK_RunRealFar(0xc000, 0x0003); 02200 LOG_MSG("Video BIOS ROM loaded and initialized."); 02201 } 02202 else WriteOut(MSG_Get("PROGRAM_LOADROM_BASIC_LOADED")); 02203 } 02204 else WriteOut(MSG_Get("PROGRAM_LOADROM_UNRECOGNIZED")); 02205 } 02206 catch(...) { 02207 return; 02208 } 02209 } 02210 }; 02211 02212 static void LOADROM_ProgramStart(Program * * make) { 02213 *make=new LOADROM; 02214 } 02215 02216 const Bit8u freedos_mbr[] = { 02217 0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0x8E,0xD0,0xBC,0x00,0x7C,0xFC,0x8B,0xF4,0xBF,0x00, 02218 0x06,0xB9,0x00,0x01,0xF2,0xA5,0xEA,0x67,0x06,0x00,0x00,0x8B,0xD5,0x58,0xA2,0x4F, // 10h 02219 0x07,0x3C,0x35,0x74,0x23,0xB4,0x10,0xF6,0xE4,0x05,0xAE,0x04,0x8B,0xF0,0x80,0x7C, // 20h 02220 0x04,0x00,0x74,0x44,0x80,0x7C,0x04,0x05,0x74,0x3E,0xC6,0x04,0x80,0xE8,0xDA,0x00, 02221 0x8A,0x74,0x01,0x8B,0x4C,0x02,0xEB,0x08,0xE8,0xCF,0x00,0xB9,0x01,0x00,0x32,0xD1, // 40h 02222 0xBB,0x00,0x7C,0xB8,0x01,0x02,0xCD,0x13,0x72,0x1E,0x81,0xBF,0xFE,0x01,0x55,0xAA, 02223 0x75,0x16,0xEA,0x00,0x7C,0x00,0x00,0x80,0xFA,0x81,0x74,0x02,0xB2,0x80,0x8B,0xEA, 02224 0x42,0x80,0xF2,0xB3,0x88,0x16,0x41,0x07,0xBF,0xBE,0x07,0xB9,0x04,0x00,0xC6,0x06, 02225 0x34,0x07,0x31,0x32,0xF6,0x88,0x2D,0x8A,0x45,0x04,0x3C,0x00,0x74,0x23,0x3C,0x05, // 80h 02226 0x74,0x1F,0xFE,0xC6,0xBE,0x31,0x07,0xE8,0x71,0x00,0xBE,0x4F,0x07,0x46,0x46,0x8B, 02227 0x1C,0x0A,0xFF,0x74,0x05,0x32,0x7D,0x04,0x75,0xF3,0x8D,0xB7,0x7B,0x07,0xE8,0x5A, 02228 0x00,0x83,0xC7,0x10,0xFE,0x06,0x34,0x07,0xE2,0xCB,0x80,0x3E,0x75,0x04,0x02,0x74, 02229 0x0B,0xBE,0x42,0x07,0x0A,0xF6,0x75,0x0A,0xCD,0x18,0xEB,0xAC,0xBE,0x31,0x07,0xE8, 02230 0x39,0x00,0xE8,0x36,0x00,0x32,0xE4,0xCD,0x1A,0x8B,0xDA,0x83,0xC3,0x60,0xB4,0x01, 02231 0xCD,0x16,0xB4,0x00,0x75,0x0B,0xCD,0x1A,0x3B,0xD3,0x72,0xF2,0xA0,0x4F,0x07,0xEB, 02232 0x0A,0xCD,0x16,0x8A,0xC4,0x3C,0x1C,0x74,0xF3,0x04,0xF6,0x3C,0x31,0x72,0xD6,0x3C, 02233 0x35,0x77,0xD2,0x50,0xBE,0x2F,0x07,0xBB,0x1B,0x06,0x53,0xFC,0xAC,0x50,0x24,0x7F, //100h 02234 0xB4,0x0E,0xCD,0x10,0x58,0xA8,0x80,0x74,0xF2,0xC3,0x56,0xB8,0x01,0x03,0xBB,0x00, //110h 02235 0x06,0xB9,0x01,0x00,0x32,0xF6,0xCD,0x13,0x5E,0xC6,0x06,0x4F,0x07,0x3F,0xC3,0x0D, //120h 02236 0x8A,0x0D,0x0A,0x46,0x35,0x20,0x2E,0x20,0x2E,0x20,0x2E,0xA0,0x64,0x69,0x73,0x6B, 02237 0x20,0x32,0x0D,0x0A,0x0A,0x44,0x65,0x66,0x61,0x75,0x6C,0x74,0x3A,0x20,0x46,0x31, //140h 02238 0xA0,0x00,0x01,0x00,0x04,0x00,0x06,0x03,0x07,0x07,0x0A,0x0A,0x63,0x0E,0x64,0x0E, 02239 0x65,0x14,0x80,0x19,0x81,0x19,0x82,0x19,0x83,0x1E,0x93,0x24,0xA5,0x2B,0x9F,0x2F, 02240 0x75,0x33,0x52,0x33,0xDB,0x36,0x40,0x3B,0xF2,0x41,0x00,0x44,0x6F,0xF3,0x48,0x70, 02241 0x66,0xF3,0x4F,0x73,0xB2,0x55,0x6E,0x69,0xF8,0x4E,0x6F,0x76,0x65,0x6C,0xEC,0x4D, //180h 02242 0x69,0x6E,0x69,0xF8,0x4C,0x69,0x6E,0x75,0xF8,0x41,0x6D,0x6F,0x65,0x62,0xE1,0x46, 02243 0x72,0x65,0x65,0x42,0x53,0xC4,0x42,0x53,0x44,0xE9,0x50,0x63,0x69,0xF8,0x43,0x70, 02244 0xED,0x56,0x65,0x6E,0x69,0xF8,0x44,0x6F,0x73,0x73,0x65,0xE3,0x3F,0xBF,0x00,0x00, //1B0h 02245 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 02246 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 02247 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 02248 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA 02249 }; 02250 #ifdef WIN32 02251 #include <winioctl.h> 02252 #endif 02253 02254 class IMGMAKE : public Program { 02255 public: 02256 #ifdef WIN32 02257 bool OpenDisk(HANDLE* f, OVERLAPPED* o, Bit8u* name) { 02258 o->hEvent = INVALID_HANDLE_VALUE; 02259 *f = CreateFile( (LPCSTR)name, GENERIC_READ | GENERIC_WRITE, 02260 0, // exclusive access 02261 NULL, // default security attributes 02262 OPEN_EXISTING, 02263 FILE_FLAG_OVERLAPPED, 02264 NULL ); 02265 02266 if (*f == INVALID_HANDLE_VALUE) return false; 02267 02268 // init OVERLAPPED 02269 o->Internal = 0; 02270 o->InternalHigh = 0; 02271 o->Offset = 0; 02272 o->OffsetHigh = 0; 02273 o->hEvent = CreateEvent( 02274 NULL, // default security attributes 02275 TRUE, // manual-reset event 02276 FALSE, // not signaled 02277 NULL // no name 02278 ); 02279 return true; 02280 } 02281 02282 void CloseDisk(HANDLE f, OVERLAPPED* o) { 02283 if(f != INVALID_HANDLE_VALUE) CloseHandle(f); 02284 if(o->hEvent != INVALID_HANDLE_VALUE) CloseHandle(o->hEvent); 02285 } 02286 02287 bool StartReadDisk(HANDLE f, OVERLAPPED* o, Bit8u* buffer, Bitu offset, Bitu size) { 02288 o->Offset = (DWORD)offset; 02289 if (!ReadFile(f, buffer, (DWORD)size, NULL, o) && 02290 (GetLastError()==ERROR_IO_PENDING)) return true; 02291 return false; 02292 } 02293 02294 // 0=still waiting, 1=catastrophic faliure, 2=success, 3=sector not found, 4=crc error 02295 Bitu CheckDiskReadComplete(HANDLE f, OVERLAPPED* o) { 02296 DWORD numret; 02297 BOOL b = GetOverlappedResult( f, o, &numret,false); 02298 if(b) return 2; 02299 else { 02300 int error = GetLastError(); 02301 if(error==ERROR_IO_INCOMPLETE) return 0; 02302 if(error==ERROR_FLOPPY_UNKNOWN_ERROR) return 5; 02303 if(error==ERROR_CRC) return 4; 02304 if(error==ERROR_SECTOR_NOT_FOUND) return 3; 02305 return 1; 02306 } 02307 } 02308 02309 Bitu ReadDisk(FILE* f, Bit8u driveletter, Bitu retries_max) { 02310 unsigned char data[36*2*512]; 02311 HANDLE hFloppy; 02312 DWORD numret; 02313 OVERLAPPED o; 02314 DISK_GEOMETRY geom; 02315 02316 Bit8u drivestring[] = "\\\\.\\x:"; drivestring[4]=driveletter; 02317 if(!OpenDisk(&hFloppy, &o, drivestring)) return false; 02318 02319 // get drive geom 02320 DeviceIoControl( hFloppy, IOCTL_DISK_GET_DRIVE_GEOMETRY,NULL,0, 02321 &geom,sizeof(DISK_GEOMETRY),&numret,NULL); 02322 02323 switch(geom.MediaType) { 02324 case F5_1Pt2_512: case F3_1Pt44_512: case F3_2Pt88_512: case F3_720_512: 02325 case F5_360_512: case F5_320_512: case F5_180_512: case F5_160_512: 02326 break; 02327 default: 02328 CloseDisk(hFloppy,&o); 02329 return false; 02330 } 02331 Bitu total_sect_per_cyl = geom.SectorsPerTrack * geom.TracksPerCylinder; 02332 Bitu cyln_size = 512 * total_sect_per_cyl; 02333 02334 WriteOut(MSG_Get("PROGRAM_IMGMAKE_FLREAD"), 02335 geom.Cylinders.LowPart,geom.TracksPerCylinder, 02336 geom.SectorsPerTrack,(cyln_size*geom.Cylinders.LowPart)/1024); 02337 WriteOut(MSG_Get("PROGRAM_IMGMAKE_FLREAD2")); 02338 02339 for(Bitu i = 0; i < geom.Cylinders.LowPart; i++) { 02340 Bitu result; 02341 // for each cylinder 02342 WriteOut("%2u",i); 02343 02344 if(!StartReadDisk(hFloppy, &o, &data[0], cyln_size*i, cyln_size)){ 02345 CloseDisk(hFloppy,&o); 02346 return false; 02347 } 02348 do { 02349 result = CheckDiskReadComplete(hFloppy, &o); 02350 CALLBACK_Idle(); 02351 } 02352 while (result==0); 02353 02354 switch(result) { 02355 case 1: 02356 CloseDisk(hFloppy,&o); 02357 return false; 02358 case 2: // success 02359 for(Bitu m=0; m < cyln_size/512; m++) WriteOut("\xdb"); 02360 break; 02361 case 3: 02362 case 4: // data errors 02363 case 5: 02364 for(Bitu k=0; k < total_sect_per_cyl; k++) { 02365 Bitu retries=retries_max; 02366 restart_int: 02367 StartReadDisk(hFloppy, &o, &data[512*k], cyln_size*i + 512*k, 512); 02368 do { 02369 result = CheckDiskReadComplete(hFloppy, &o); 02370 CALLBACK_Idle(); 02371 } 02372 while (result==0); 02373 02374 switch(result) { 02375 case 1: // bad error 02376 CloseDisk(hFloppy,&o); 02377 return false; 02378 case 2: // success 02379 if(retries==retries_max) WriteOut("\xdb"); 02380 else WriteOut("\b\b\b\xb1"); 02381 break; 02382 case 3: 02383 case 4: // read errors 02384 case 5: 02385 if(retries!=retries_max) WriteOut("\b\b\b"); 02386 retries--; 02387 switch(result) { 02388 case 3: WriteOut("x"); 02389 case 4: WriteOut("!"); 02390 case 5: WriteOut("?"); 02391 } 02392 WriteOut("%2d",retries); 02393 02394 if(retries) goto restart_int; 02395 const Bit8u badfood[]="IMGMAKE BAD FLOPPY SECTOR \xBA\xAD\xF0\x0D"; 02396 for(Bit8u z = 0; z < 512/32; z++) 02397 memcpy(&data[512*k+z*32],badfood,31); 02398 WriteOut("\b\b"); 02399 break; 02400 } 02401 } 02402 break; 02403 } 02404 fwrite(data, 512, total_sect_per_cyl, f); 02405 WriteOut("%2x%2x\n", data[0], data[1]); 02406 } 02407 // seek to 0 02408 StartReadDisk(hFloppy, &o, &data[0], 0, 512); 02409 CloseDisk(hFloppy,&o); 02410 return true; 02411 } 02412 #endif 02413 02414 void Run(void) { 02415 std::string disktype; 02416 std::string src; 02417 std::string filename; 02418 std::string dpath; 02419 std::string tmp; 02420 02421 unsigned int c, h, s, sectors; 02422 Bit64u size = 0; 02423 02424 if(cmd->FindExist("-?")) { 02425 printHelp(); 02426 return; 02427 } 02428 if (cmd->FindExist("-examples")) { 02429 WriteOut(MSG_Get("PROGRAM_IMGMAKE_EXAMPLE")); 02430 return; 02431 } 02432 02433 /* 02434 this stuff is too frustrating 02435 02436 // when only a filename is passed try to create the file on the current DOS path 02437 // if directory+filename are passed first see if directory is a host path, if not 02438 // maybe it is a DOSBox path. 02439 02440 // split filename and path 02441 std::string path = ""; 02442 Bitu spos = temp_line.rfind('\\'); 02443 if(spos==std::string::npos) { 02444 temp_line.rfind('/'); 02445 } 02446 02447 if(spos==std::string::npos) { 02448 // no path separator 02449 filename=temp_line; 02450 } else { 02451 path=temp_line.substr(0,spos); 02452 filename=temp_line.substr(spos+1,std::string::npos); 02453 } 02454 if(filename=="") 02455 02456 char tbuffer[DOS_PATHLENGTH]= { 0 }; 02457 if(path=="") { 02458 if(!DOS_GetCurrentDir(DOS_GetDefaultDrive()+1,tbuffer)){ 02459 printHelp(); 02460 return; 02461 } 02462 dpath=(std::string)tbuffer; 02463 } 02464 WriteOut("path %s, filename %s, dpath %s", 02465 path.c_str(),filename.c_str(),dpath.c_str()); 02466 return; 02467 */ 02468 02469 #ifdef WIN32 02470 // read from real floppy? 02471 if(cmd->FindString("-source",src,true)) { 02472 int retries = 10; 02473 cmd->FindInt("-retries",retries,true); 02474 if((retries < 1)||(retries > 99)) { 02475 printHelp(); 02476 return; 02477 } 02478 if((src.length()!=1) || !isalpha(src.c_str()[0])) { 02479 // only one letter allowed 02480 printHelp(); 02481 return; 02482 } 02483 02484 // temp_line is the filename 02485 if (!(cmd->FindCommand(1, temp_line))) { 02486 printHelp(); 02487 return; 02488 } 02489 02490 // don't trash user's files 02491 FILE* f = fopen(temp_line.c_str(),"r"); 02492 if(f) { 02493 fclose(f); 02494 WriteOut(MSG_Get("PROGRAM_IMGMAKE_FILE_EXISTS"),temp_line.c_str()); 02495 return; 02496 } 02497 f = fopen(temp_line.c_str(),"wb+"); 02498 if (!f) { 02499 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),temp_line.c_str()); 02500 return; 02501 } 02502 // maybe delete f if it failed? 02503 if(!ReadDisk(f, src.c_str()[0],retries)) 02504 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANT_READ_FLOPPY")); 02505 fclose(f); 02506 return; 02507 } 02508 #endif 02509 // disk type 02510 if (!(cmd->FindString("-t",disktype,true))) { 02511 printHelp(); 02512 return; 02513 } 02514 std::transform(disktype.begin(), disktype.end(), disktype.begin(), ::tolower); 02515 02516 Bit8u mediadesc = 0xF8; // media descriptor byte; also used to differ fd and hd 02517 Bit16u root_ent = 512; // FAT root directory entries: 512 is for harddisks 02518 if(disktype=="fd_160") { 02519 c = 40; h = 1; s = 8; mediadesc = 0xFE; root_ent = 56; // root_ent? 02520 } else if(disktype=="fd_180") { 02521 c = 40; h = 1; s = 9; mediadesc = 0xFC; root_ent = 56; // root_ent? 02522 } else if(disktype=="fd_200") { 02523 c = 40; h = 1; s = 10; mediadesc = 0xFC; root_ent = 56; // root_ent? 02524 } else if(disktype=="fd_320") { 02525 c = 40; h = 2; s = 8; mediadesc = 0xFF; root_ent = 112; // root_ent? 02526 } else if(disktype=="fd_360") { 02527 c = 40; h = 2; s = 9; mediadesc = 0xFD; root_ent = 112; 02528 } else if(disktype=="fd_400") { 02529 c = 40; h = 2; s = 10; mediadesc = 0xFD; root_ent = 112; // root_ent? 02530 } else if(disktype=="fd_720") { 02531 c = 80; h = 2; s = 9; mediadesc = 0xF9; root_ent = 112; 02532 } else if(disktype=="fd_1200") { 02533 c = 80; h = 2; s = 15; mediadesc = 0xF9; root_ent = 224; 02534 } else if(disktype=="fd_1440") { 02535 c = 80; h = 2; s = 18; mediadesc = 0xF0; root_ent = 224; 02536 } else if(disktype=="fd_2880") { 02537 c = 80; h = 2; s = 36; mediadesc = 0xF0; root_ent = 512; // root_ent? 02538 } else if(disktype=="hd_250") { 02539 c = 489; h = 16; s = 63; 02540 } else if(disktype=="hd_520") { 02541 c = 1023; h = 16; s = 63; 02542 } else if(disktype=="hd_2gig") { 02543 c = 1023; h = 64; s = 63; 02544 } else if(disktype=="hd_4gig") { // fseek only supports 2gb 02545 c = 1023; h = 130; s = 63; 02546 } else if(disktype=="hd_8gig") { // fseek only supports 2gb 02547 c = 1023; h = 255; s = 63; 02548 } else if(disktype=="hd_st251") { // old 40mb drive 02549 c = 820; h = 6; s = 17; 02550 } else if(disktype=="hd_st225") { // even older 20mb drive 02551 c = 615; h = 4; s = 17; 02552 } else if(disktype=="hd") { 02553 // get size from parameter 02554 std::string isize; 02555 if (!(cmd->FindString("-size",isize,true))) { 02556 // maybe -chs? 02557 if (!(cmd->FindString("-chs",isize,true))){ 02558 // user forgot -size and -chs 02559 printHelp(); 02560 return; 02561 } 02562 else { 02563 // got chs data: -chs 1023,16,63 02564 if(sscanf(isize.c_str(),"%u,%u,%u",&c,&h,&s) != 3) { 02565 printHelp(); 02566 return; 02567 } 02568 // sanity-check chs values 02569 if((h>255)||(c>1023)||(s>63)) { 02570 printHelp(); 02571 return; 02572 } 02573 size = (unsigned long long)c * (unsigned long long)h * (unsigned long long)s * 512ULL; 02574 if((size < 3u*1024u*1024u) || (size > 0x1FFFFFFFFLL)/*8GB*/) { 02575 // user picked geometry resulting in wrong size 02576 printHelp(); 02577 return; 02578 } 02579 } 02580 } else { 02581 // got -size 02582 std::istringstream stream(isize); 02583 stream >> size; 02584 size *= 1024*1024LL; // size in megabytes 02585 // low limit: 3 megs, high limit: 2 terabytes 02586 // Int13 limit would be 8 gigs 02587 if((size < 3*1024*1024LL) || (size > 0x1FFFFFFFFFFLL)/*2TB*/) { 02588 // wrong size 02589 printHelp(); 02590 return; 02591 } 02592 sectors = (unsigned int)(size / 512); 02593 02594 // Now that we finally have the proper size, figure out good CHS values 02595 if (size > 0xFFFFFFFFLL/*4GB*/) { 02596 /* beyond that point it's easier to just map like LBA and be done with it */ 02597 h=255; 02598 s=63; 02599 c=sectors/(h*s); 02600 } 02601 else { 02602 h=2; 02603 while(h*1023*63 < sectors) h <<= 1; 02604 if(h>255) h=255; 02605 s=8; 02606 while(h*s*1023 < sectors) s *= 2; 02607 if(s>63) s=63; 02608 c=sectors/(h*s); 02609 if(c>1023) c=1023; 02610 } 02611 } 02612 } else { 02613 // user passed a wrong -t argument 02614 printHelp(); 02615 return; 02616 } 02617 02618 std::string t2 = ""; 02619 if(cmd->FindExist("-bat",true)) { 02620 t2 = "-bat"; 02621 } 02622 02623 size = (unsigned long long)c * (unsigned long long)h * (unsigned long long)s * 512ULL; 02624 Bits bootsect_pos = 0; // offset of the boot sector in clusters 02625 if(cmd->FindExist("-nofs",true)) { 02626 bootsect_pos = -1; 02627 } 02628 02629 /* beyond this point clamp c */ 02630 if (c > 1023) c = 1023; 02631 02632 // temp_line is the filename 02633 if (!(cmd->FindCommand(1, temp_line))) { 02634 printHelp(); 02635 return; 02636 } 02637 02638 // don't trash user's files 02639 FILE* f = fopen(temp_line.c_str(),"r"); 02640 if(f) { 02641 fclose(f); 02642 WriteOut(MSG_Get("PROGRAM_IMGMAKE_FILE_EXISTS"),temp_line.c_str()); 02643 return; 02644 } 02645 02646 WriteOut(MSG_Get("PROGRAM_IMGMAKE_PRINT_CHS"),c,h,s); 02647 LOG_MSG(MSG_Get("PROGRAM_IMGMAKE_PRINT_CHS"),c,h,s); 02648 02649 // do it again for fixed chs values 02650 sectors = (unsigned int)(size / 512); 02651 02652 // create the image file 02653 f = fopen64(temp_line.c_str(),"wb+"); 02654 if (!f) { 02655 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),temp_line.c_str()); 02656 return; 02657 } 02658 #if defined (_MSC_VER) and (_MSC_VER >= 1400) 02659 if(fseeko64(f,(__int64)(size - 1ull),SEEK_SET)) { 02660 #else 02661 if(fseeko64(f,static_cast<off_t>(size - 1ull),SEEK_SET)) { 02662 #endif 02663 WriteOut(MSG_Get("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE"),size); 02664 fclose(f); 02665 return; 02666 } 02667 Bit8u bufferbyte=0; 02668 if(fwrite(&bufferbyte,1,1,f)!=1) { 02669 WriteOut(MSG_Get("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE"),size); 02670 fclose(f); 02671 return; 02672 } 02673 02674 // Format the image if not unrequested (and image size<2GB) 02675 if(bootsect_pos > -1) { 02676 unsigned int reserved_sectors = 1; /* 1 for the boot sector + BPB. FAT32 will require more */ 02677 unsigned int sectors_per_cluster = 0; 02678 unsigned int vol_sectors = 0; 02679 unsigned int fat_copies = 2; /* number of copies of the FAT. always 2. TODO: Allow the user to specify */ 02680 unsigned int fatlimitmin; 02681 unsigned int fatlimit; 02682 int FAT = -1; 02683 02684 /* FAT filesystem, user choice */ 02685 if (cmd->FindString("-fat",tmp,true)) { 02686 FAT = atoi(tmp.c_str()); 02687 if (!(FAT == 12 || FAT == 16 || FAT == 32)) { 02688 WriteOut("Invalid -fat option. Must be 12, 16, or 32\n"); 02689 fclose(f); 02690 return; 02691 } 02692 } 02693 02694 /* FAT copies, user choice */ 02695 if (cmd->FindString("-fatcopies",tmp,true)) { 02696 fat_copies = atoi(tmp.c_str()); 02697 if (fat_copies < 1u || fat_copies > 4u) { 02698 WriteOut("Invalid -fatcopies option\n"); 02699 fclose(f); 02700 return; 02701 } 02702 } 02703 02704 /* Sectors per cluster, user choice */ 02705 if (cmd->FindString("-spc",tmp,true)) { 02706 sectors_per_cluster = atoi(tmp.c_str()); 02707 if (sectors_per_cluster < 1u || sectors_per_cluster > 128u) { 02708 WriteOut("Invalid -spc option, out of range\n"); 02709 fclose(f); 02710 return; 02711 } 02712 if ((sectors_per_cluster & (sectors_per_cluster - 1u)) != 0u) { 02713 WriteOut("Invalid -spc option, must be a power of 2\n"); 02714 fclose(f); 02715 return; 02716 } 02717 } 02718 02719 /* Root directory count, user choice. 02720 * Does not apply to FAT32, which makes the root directory an allocation chain like any other directory/file. */ 02721 if (cmd->FindString("-rootdir",tmp,true)) { 02722 root_ent = atoi(tmp.c_str()); 02723 if (root_ent < 1u || root_ent > 4096u) { 02724 WriteOut("Invalid -rootdir option\n"); 02725 fclose(f); 02726 return; 02727 } 02728 } 02729 02730 /* decide partition placement */ 02731 if (mediadesc == 0xF8) { 02732 bootsect_pos = (Bits)s; 02733 vol_sectors = sectors - (unsigned int)bootsect_pos; 02734 } 02735 else { 02736 bootsect_pos = 0; 02737 vol_sectors = sectors; 02738 } 02739 02740 /* auto-decide FAT system */ 02741 if (FAT < 0) { 02742 bool dosver_fat32 = (dos.version.major >= 8) || (dos.version.major == 7 && dos.version.minor >= 10); 02743 02744 if (vol_sectors >= 4194304 && !dosver_fat32) /* 2GB or larger */ 02745 FAT = 32; 02746 else if (vol_sectors >= 1048576 && dosver_fat32) /* 512MB or larger */ 02747 FAT = 32; 02748 else if (vol_sectors >= 24576) /* 12MB or larger */ 02749 FAT = 16; 02750 else 02751 FAT = 12; 02752 } 02753 02754 /* highest cluster number + 1 */ 02755 switch (FAT) { 02756 case 32: 02757 fatlimit = 0x0FFFFFF6; 02758 fatlimitmin = 0xFFF6; 02759 break; 02760 case 16: 02761 fatlimit = 0xFFF6; 02762 fatlimitmin = 0xFF6; 02763 break; 02764 case 12: 02765 fatlimit = 0xFF6; 02766 fatlimitmin = 0; 02767 break; 02768 default: 02769 abort(); 02770 break; 02771 } 02772 02773 /* FAT32 increases reserved area to at least 7. Microsoft likes to use 32 */ 02774 if (FAT >= 32) 02775 reserved_sectors = 32; 02776 02777 Bit8u sbuf[512]; 02778 if(mediadesc == 0xF8) { 02779 // is a harddisk: write MBR 02780 memcpy(sbuf,freedos_mbr,512); 02781 // active partition 02782 sbuf[0x1be]=0x80; 02783 // start head - head 0 has the partition table, head 1 first partition 02784 sbuf[0x1bf]=1; 02785 // start sector with bits 8-9 of start cylinder in bits 6-7 02786 sbuf[0x1c0]=1; 02787 // start cylinder bits 0-7 02788 sbuf[0x1c1]=0; 02789 // OS indicator 02790 if (FAT < 32 && (bootsect_pos+vol_sectors) < 65536) { /* 32MB or smaller */ 02791 if (FAT >= 16) 02792 sbuf[0x1c2]=0x04; /* FAT16 within the first 32MB */ 02793 else 02794 sbuf[0x1c2]=0x01; /* FAT12 within the first 32MB */ 02795 } 02796 else if ((bootsect_pos+vol_sectors) < 8388608) { /* 4GB or smaller */ 02797 if (FAT >= 32) 02798 sbuf[0x1c2]=0x0B; /* FAT32 C/H/S */ 02799 else 02800 sbuf[0x1c2]=0x06; /* FAT12/FAT16 C/H/S */ 02801 } 02802 else { 02803 if (FAT >= 32) 02804 sbuf[0x1c2]=0x0C; /* FAT32 LBA */ 02805 else 02806 sbuf[0x1c2]=0x0E; /* FAT12/FAT16 LBA */ 02807 } 02808 // end head (0-based) 02809 sbuf[0x1c3]= h-1; 02810 // end sector with bits 8-9 of end cylinder (0-based) in bits 6-7 02811 sbuf[0x1c4]=s|(((c-1)&0x300)>>2); 02812 // end cylinder (0-based) bits 0-7 02813 sbuf[0x1c5]=(c-1)&0xFF; 02814 // sectors preceding partition1 (one head) 02815 host_writed(&sbuf[0x1c6],bootsect_pos); 02816 // length of partition1, align to chs value 02817 host_writed(&sbuf[0x1ca],vol_sectors); 02818 02819 // write partition table 02820 fseeko64(f,0,SEEK_SET); 02821 fwrite(&sbuf,512,1,f); 02822 } 02823 02824 // set boot sector values 02825 memset(sbuf,0,512); 02826 // TODO boot code jump 02827 if (FAT >= 32) { 02828 sbuf[0]=0xEB; sbuf[1]=0x58; sbuf[2]=0x90; // Windows 98 values 02829 } 02830 else { 02831 sbuf[0]=0xEB; sbuf[1]=0x3c; sbuf[2]=0x90; 02832 } 02833 // OEM 02834 if (FAT >= 32) { 02835 sprintf((char*)&sbuf[0x03],"MSWIN4.1"); 02836 } else { 02837 sprintf((char*)&sbuf[0x03],"MSDOS5.0"); 02838 } 02839 // bytes per sector: always 512 02840 host_writew(&sbuf[0x0b],512); 02841 // sectors per cluster: 1,2,4,8,16,... 02842 // NOTES: SCANDISK.EXE will hang if you ask it to check a FAT12 filesystem with 128 sectors/cluster. 02843 if (sectors_per_cluster == 0) { 02844 sectors_per_cluster = 1; 02845 /* one sector per cluster on anything larger than 200KB is a bit wasteful (large FAT tables). 02846 * Improve capacity by starting from a larger value.*/ 02847 if (vol_sectors >= 400) { 02848 unsigned int tmp_fatlimit; 02849 02850 /* Windows 98 likes multiples of 4KB, which is actually reasonable considering 02851 * that it keeps FAT32 efficient. Also, Windows 98 SETUP will crash if sectors/cluster 02852 * is too small. Ref: [https://github.com/joncampbell123/dosbox-x/issues/1553#issuecomment-651880604] 02853 * and [http://www.helpwithwindows.com/windows98/fat32.html] */ 02854 if (FAT >= 32) { 02855 if (vol_sectors >= 67108864/*32GB*/) 02856 sectors_per_cluster = 64; /* 32KB (64*512) */ 02857 else if (vol_sectors >= 33554432/*16GB*/) 02858 sectors_per_cluster = 32; /* 16KB (32*512) */ 02859 else if (vol_sectors >= 16777216/*8GB*/) 02860 sectors_per_cluster = 16; /* 8KB (16*512) */ 02861 else 02862 sectors_per_cluster = 8; /* 4KB (8*512) */ 02863 } 02864 else { 02865 /* 1 sector per cluster is very inefficent */ 02866 if (vol_sectors >= 6144000/*3000MB*/) 02867 sectors_per_cluster = 8; 02868 else if (vol_sectors >= 1048576/*512MB*/) 02869 sectors_per_cluster = 4; 02870 else if (vol_sectors >= 131072/*64MB*/) 02871 sectors_per_cluster = 2; 02872 } 02873 02874 /* no more than 5% of the disk */ 02875 switch (FAT) { 02876 case 12: tmp_fatlimit = ((((vol_sectors / 20u) * (512u / fat_copies)) / 3u) * 2u) + 2u; break; 02877 case 16: tmp_fatlimit = (((vol_sectors / 20u) * (512u / fat_copies)) / 2u) + 2u; break; 02878 case 32: tmp_fatlimit = (((vol_sectors / 20u) * (512u / fat_copies)) / 4u) + 2u; break; 02879 default: abort(); break; 02880 } 02881 02882 while ((vol_sectors/sectors_per_cluster) >= (tmp_fatlimit - 2u) && sectors_per_cluster < 0x80u) sectors_per_cluster <<= 1; 02883 } 02884 } 02885 while ((vol_sectors/sectors_per_cluster) >= (fatlimit - 2u) && sectors_per_cluster < 0x80u) sectors_per_cluster <<= 1; 02886 sbuf[0x0d]=(Bit8u)sectors_per_cluster; 02887 // TODO small floppys have 2 sectors per cluster? 02888 // reserverd sectors 02889 host_writew(&sbuf[0x0e],reserved_sectors); 02890 // Number of FATs 02891 sbuf[0x10] = fat_copies; 02892 // Root entries if not FAT32 02893 if (FAT < 32) host_writew(&sbuf[0x11],root_ent); 02894 // sectors (under 32MB) if not FAT32 and less than 65536 02895 if (FAT < 32 && vol_sectors < 65536ul) host_writew(&sbuf[0x13],vol_sectors); 02896 // sectors (32MB or larger or FAT32) 02897 if (FAT >= 32 || vol_sectors >= 65536ul) host_writed(&sbuf[0x20],vol_sectors); 02898 // media descriptor 02899 sbuf[0x15]=mediadesc; 02900 // sectors per FAT 02901 // needed entries: (sectors per cluster) 02902 Bitu sect_per_fat=0; 02903 Bitu clusters = vol_sectors / sectors_per_cluster; // initial estimate 02904 02905 if (FAT >= 32) sect_per_fat = ((clusters*4u)+511u)/512u; 02906 else if (FAT >= 16) sect_per_fat = ((clusters*2u)+511u)/512u; 02907 else sect_per_fat = ((((clusters+1u)/2u)*3u)+511u)/512u; 02908 02909 if (FAT < 32 && sect_per_fat >= 65536u) { 02910 WriteOut("Error: Generated filesystem has more than 64KB sectors per FAT and is not FAT32\n"); 02911 fclose(f); 02912 return; 02913 } 02914 02915 Bitu data_area = vol_sectors - reserved_sectors - (sect_per_fat * fat_copies); 02916 if (FAT < 32) data_area -= ((root_ent * 32u) + 511u) / 512u; 02917 clusters = data_area / sectors_per_cluster; 02918 if (FAT < 32) host_writew(&sbuf[0x16],(Bit16u)sect_per_fat); 02919 02920 /* Too many or to few clusters can foul up FAT12/FAT16/FAT32 detection and cause corruption! */ 02921 if ((clusters+2u) < fatlimitmin) { 02922 WriteOut("Error: Generated filesystem has too few clusters given the parameters\n"); 02923 fclose(f); 02924 return; 02925 } 02926 if ((clusters+2u) > fatlimit) { 02927 clusters = fatlimit-2u; 02928 WriteOut("Warning: Cluster count is too high given the volume size. Reporting a\n"); 02929 WriteOut(" smaller sector count.\n"); 02930 /* Well, if the user wants an oversized partition, hack the total sectors fields to make it work */ 02931 unsigned int adj_vol_sectors = 02932 reserved_sectors + (sect_per_fat * fat_copies) + 02933 (((root_ent * 32u) + 511u) / 512u) + (clusters * sectors_per_cluster); 02934 02935 // sectors (under 32MB) if not FAT32 and less than 65536 02936 if (adj_vol_sectors < 65536ul) host_writew(&sbuf[0x13],adj_vol_sectors); 02937 // sectors (32MB or larger or FAT32) 02938 if (adj_vol_sectors >= 65536ul) host_writed(&sbuf[0x20],adj_vol_sectors); 02939 } 02940 02941 // sectors per track 02942 host_writew(&sbuf[0x18],s); 02943 // heads 02944 host_writew(&sbuf[0x1a],h); 02945 // hidden sectors 02946 host_writed(&sbuf[0x1c],(Bit32u)bootsect_pos); 02947 /* after 0x24, FAT12/FAT16 and FAT32 diverge in structure */ 02948 if (FAT >= 32) { 02949 host_writed(&sbuf[0x24],(Bit32u)sect_per_fat); 02950 sbuf[0x28] = 0x00; // FAT is mirrored at runtime because that is what DOSBox-X's FAT driver does 02951 host_writew(&sbuf[0x2A],0x0000); // FAT32 version 0.0 02952 host_writed(&sbuf[0x2C],2); // root directory starting cluster 02953 host_writew(&sbuf[0x30],1); // sector number in reserved area of FSINFO structure 02954 host_writew(&sbuf[0x32],6); // sector number in reserved area of backup boot sector 02955 // BIOS drive 02956 if(mediadesc == 0xF8) sbuf[0x40]=0x80; 02957 else sbuf[0x40]=0x00; 02958 // ext. boot signature 02959 sbuf[0x42]=0x29; 02960 // volume serial number 02961 // let's use the BIOS time (cheap, huh?) 02962 host_writed(&sbuf[0x43],mem_readd(BIOS_TIMER)); 02963 // Volume label 02964 sprintf((char*)&sbuf[0x47],"NO NAME "); 02965 // file system type 02966 sprintf((char*)&sbuf[0x52],"FAT32 "); 02967 } 02968 else { /* FAT12/FAT16 */ 02969 // BIOS drive 02970 if(mediadesc == 0xF8) sbuf[0x24]=0x80; 02971 else sbuf[0x24]=0x00; 02972 // ext. boot signature 02973 sbuf[0x26]=0x29; 02974 // volume serial number 02975 // let's use the BIOS time (cheap, huh?) 02976 host_writed(&sbuf[0x27],mem_readd(BIOS_TIMER)); 02977 // Volume label 02978 sprintf((char*)&sbuf[0x2b],"NO NAME "); 02979 // file system type 02980 if (FAT >= 16) sprintf((char*)&sbuf[0x36],"FAT16 "); 02981 else sprintf((char*)&sbuf[0x36],"FAT12 "); 02982 } 02983 // boot sector signature 02984 host_writew(&sbuf[0x1fe],0xAA55); 02985 02986 // write the boot sector 02987 fseeko64(f,bootsect_pos*512,SEEK_SET); 02988 fwrite(&sbuf,512,1,f); 02989 02990 // FAT32: Write backup copy too. 02991 // The BPB we wrote says sector 6 from start of volume 02992 if (FAT >= 32) { 02993 fseeko64(f,(bootsect_pos+6u)*512,SEEK_SET); 02994 fwrite(&sbuf,512,1,f); 02995 } 02996 02997 // FAT32: Write FSInfo sector too at sector 1 from start of volume. 02998 // Windows 98 behavior shows that the FSInfo is duplicated 02999 // along with the boot sector. 03000 if (FAT >= 32) { 03001 memset(sbuf,0,512); 03002 host_writed(&sbuf[0x000],0x41615252); /* "RRaA" */ 03003 host_writed(&sbuf[0x1e4],0x61417272); /* "rrAa" */ 03004 host_writed(&sbuf[0x1e8],clusters-1); /* Last known free cluster count */ 03005 host_writed(&sbuf[0x1ec],3); /* Next free cluster. We used 2 for the root dir, so 3 is next */ 03006 host_writed(&sbuf[0x1fc],0xAA550000); /* signature */ 03007 fseeko64(f,(bootsect_pos+1u)*512,SEEK_SET); 03008 fwrite(&sbuf,512,1,f); 03009 fseeko64(f,(bootsect_pos+6u+1u)*512,SEEK_SET); 03010 fwrite(&sbuf,512,1,f); 03011 } 03012 03013 // write FATs 03014 memset(sbuf,0,512); 03015 if (FAT >= 32) { 03016 host_writed(&sbuf[0],0x0FFFFF00 | mediadesc); 03017 host_writed(&sbuf[4],0x0FFFFFFF); 03018 03019 /* The code above marks cluster 2 as the start of the root directory. */ 03020 host_writed(&sbuf[8],0x0FFFFFFF); 03021 } 03022 else if (FAT >= 16) 03023 host_writed(&sbuf[0],0xFFFFFF00 | mediadesc); 03024 else 03025 host_writed(&sbuf[0],0xFFFF00 | mediadesc); 03026 03027 for (unsigned int fat=0;fat < fat_copies;fat++) { 03028 fseeko64(f,(off_t)(((unsigned long long)bootsect_pos+reserved_sectors+(unsigned long long)sect_per_fat*(unsigned long long)fat)*512ull),SEEK_SET); 03029 fwrite(&sbuf,512,1,f); 03030 } 03031 03032 // warning 03033 if ((sectors_per_cluster*512ul) >= 65536ul) 03034 WriteOut("WARNING: Cluster sizes >= 64KB are not compatible with MS-DOS and SCANDISK\n"); 03035 } 03036 // write VHD footer if requested, largely copied from RAW2VHD program, no license was included 03037 if((mediadesc == 0xF8) && (temp_line.find(".vhd")) != std::string::npos) { 03038 int i; 03039 Bit8u footer[512]; 03040 // basic information 03041 memcpy(footer,"conectix" "\0\0\0\2\0\1\0\0" "\xff\xff\xff\xff\xff\xff\xff\xff" "????rawv" "\0\1\0\0Wi2k",40); 03042 memset(footer+40,0,512-40); 03043 // time 03044 struct tm tm20000101 = { /*sec*/0,/*min*/0,/*hours*/0, /*day of month*/1,/*month*/0,/*year*/100, /*wday*/0,/*yday*/0,/*isdst*/0 }; 03045 time_t basetime = mktime(&tm20000101); 03046 time_t vhdtime = time(NULL) - basetime; 03047 #if defined (_MSC_VER) 03048 *(Bit32u*)(footer+0x18) = SDL_SwapBE32((__time32_t)vhdtime); 03049 #else 03050 *(Bit32u*)(footer+0x18) = Bit32u(SDL_SwapBE32((Uint32)vhdtime)); 03051 #endif 03052 // size and geometry 03053 *(Bit64u*)(footer+0x30) = *(Bit64u*)(footer+0x28) = SDL_SwapBE64(size); 03054 03055 *(Bit16u*)(footer+0x38) = SDL_SwapBE16(c); 03056 *(Bit8u*)( footer+0x3A) = h; 03057 *(Bit8u*)( footer+0x3B) = s; 03058 *(Bit32u*)(footer+0x3C) = SDL_SwapBE32(2); 03059 03060 // generate UUID 03061 for (i=0; i<16; ++i) { 03062 *(footer+0x44+i) = (Bit8u)(rand()>>4); 03063 } 03064 03065 // calculate checksum 03066 Bit32u sum; 03067 for (i=0,sum=0; i<512; ++i) { 03068 sum += footer[i]; 03069 } 03070 03071 *(Bit32u*)(footer+0x40) = SDL_SwapBE32(~sum); 03072 03073 // write footer 03074 fseeko64(f, 0L, SEEK_END); 03075 fwrite(&footer,512,1,f); 03076 } 03077 fclose(f); 03078 03079 // create the batch file 03080 if(t2 == "-bat") { 03081 if(temp_line.length() > 3) { 03082 t2 = temp_line.substr(0,temp_line.length()-4); 03083 t2 = t2.append(".bat"); 03084 } else { 03085 t2 = temp_line.append(".bat"); 03086 } 03087 WriteOut("%s\n",t2.c_str()); 03088 f = fopen(t2.c_str(),"wb+"); 03089 if (!f) { 03090 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),t2.c_str()); 03091 return; 03092 } 03093 fprintf(f,"imgmount c %s -size 512,%u,%u,%u\r\n",temp_line.c_str(),s,h,c); 03094 fclose(f); 03095 } 03096 return; 03097 } 03098 void printHelp() { // maybe hint parameter? 03099 WriteOut(MSG_Get("PROGRAM_IMGMAKE_SYNTAX")); 03100 } 03101 }; 03102 03103 static void IMGMAKE_ProgramStart(Program * * make) { 03104 *make=new IMGMAKE; 03105 } 03106 03107 // LOADFIX 03108 03109 class LOADFIX : public Program { 03110 public: 03111 void Run(void); 03112 }; 03113 03114 bool XMS_Active(void); 03115 Bitu XMS_AllocateMemory(Bitu size, Bit16u& handle); 03116 03117 void LOADFIX::Run(void) 03118 { 03119 Bit16u commandNr = 1; 03120 Bitu kb = 64; 03121 bool xms = false; 03122 03123 if (cmd->FindExist("-xms",true)) { 03124 xms = true; 03125 kb = 1024; 03126 } 03127 03128 if (cmd->GetCount()==1 && (cmd->FindExist("-?", false) || cmd->FindExist("/?", false))) { 03129 WriteOut(MSG_Get("PROGRAM_LOADFIX_HELP")); 03130 return; 03131 } 03132 03133 if (cmd->FindCommand(commandNr,temp_line)) { 03134 if (temp_line[0]=='-') { 03135 char ch = temp_line[1]; 03136 if ((*upcase(&ch)=='D') || (*upcase(&ch)=='F')) { 03137 // Deallocate all 03138 if (xms) { 03139 WriteOut("XMS deallocation not yet implemented\n"); 03140 } 03141 else { 03142 DOS_FreeProcessMemory(0x40); 03143 WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOCALL"),kb); 03144 } 03145 return; 03146 } else { 03147 // Set mem amount to allocate 03148 kb = (Bitu)atoi(temp_line.c_str()+1); 03149 if (kb==0) kb=xms?1024:64; 03150 commandNr++; 03151 } 03152 } 03153 } 03154 03155 // Allocate Memory 03156 if (xms) { 03157 if (XMS_Active()) { 03158 Bit16u handle; 03159 Bitu err; 03160 03161 err = XMS_AllocateMemory(kb,/*&*/handle); 03162 if (err == 0) { 03163 WriteOut("XMS block allocated (%uKB)\n",kb); 03164 } 03165 else { 03166 WriteOut("Unable to allocate XMS block\n"); 03167 } 03168 } 03169 else { 03170 WriteOut("XMS not active\n"); 03171 } 03172 } 03173 else { 03174 Bit16u segment; 03175 Bit16u blocks = (Bit16u)(kb*1024/16); 03176 if (DOS_AllocateMemory(&segment,&blocks)) { 03177 DOS_MCB mcb((Bit16u)(segment-1)); 03178 mcb.SetPSPSeg(0x40); // use fake segment 03179 WriteOut(MSG_Get("PROGRAM_LOADFIX_ALLOC"),kb); 03180 // Prepare commandline... 03181 if (cmd->FindCommand(commandNr++,temp_line)) { 03182 // get Filename 03183 char filename[128]; 03184 safe_strncpy(filename,temp_line.c_str(),128); 03185 // Setup commandline 03186 bool ok; 03187 char args[256]; 03188 args[0] = 0; 03189 do { 03190 ok = cmd->FindCommand(commandNr,temp_line); 03191 if(commandNr++>cmd->GetCount() || sizeof(args)-strlen(args)-1 < temp_line.length()+1) 03192 break; 03193 strcat(args,temp_line.c_str()); 03194 strcat(args," "); 03195 } while (ok); 03196 // Use shell to start program 03197 DOS_Shell shell; 03198 shell.Execute(filename,args); 03199 DOS_FreeMemory(segment); 03200 WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOC"),kb); 03201 } 03202 } else { 03203 WriteOut(MSG_Get("PROGRAM_LOADFIX_ERROR"),kb); 03204 } 03205 } 03206 } 03207 03208 static void LOADFIX_ProgramStart(Program * * make) { 03209 *make=new LOADFIX; 03210 } 03211 03212 // RESCAN 03213 03214 class RESCAN : public Program { 03215 public: 03216 void Run(void); 03217 }; 03218 03219 void RESCAN::Run(void) 03220 { 03221 if (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)) { 03222 WriteOut("Clears the caches of a mounted drive.\n\nRESCAN [/A]\nRESCAN [drive:]\n\n [/A]\t\tRescan all drives\n [drive:]\tThe drive to rescan\n\nType RESCAN with no parameters to rescan the current drive.\n"); 03223 return; 03224 } 03225 bool all = false; 03226 03227 Bit8u drive = DOS_GetDefaultDrive(); 03228 03229 if(cmd->FindCommand(1,temp_line)) { 03230 //-A -All /A /All 03231 if(temp_line.size() >= 2 && (temp_line[0] == '-' ||temp_line[0] =='/')&& (temp_line[1] == 'a' || temp_line[1] =='A') ) all = true; 03232 else if(temp_line.size() == 2 && temp_line[1] == ':') { 03233 lowcase(temp_line); 03234 drive = temp_line[0] - 'a'; 03235 } 03236 } 03237 // Get current drive 03238 if (all) { 03239 for(Bitu i =0; i<DOS_DRIVES;i++) { 03240 if (Drives[i]) Drives[i]->EmptyCache(); 03241 } 03242 WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS")); 03243 } else { 03244 if (drive < DOS_DRIVES && Drives[drive]) { 03245 Drives[drive]->EmptyCache(); 03246 WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS")); 03247 } else 03248 WriteOut("Invalid drive specification\n"); 03249 } 03250 } 03251 03252 static void RESCAN_ProgramStart(Program * * make) { 03253 *make=new RESCAN; 03254 } 03255 03256 /* TODO: This menu code sucks. Write a better one. */ 03257 class INTRO : public Program { 03258 public: 03259 void DisplayMount(void) { 03260 /* Basic mounting has a version for each operating system. 03261 * This is done this way so both messages appear in the language file*/ 03262 WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_START")); 03263 #if (WIN32) 03264 WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS")); 03265 #else 03266 WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_OTHER")); 03267 #endif 03268 WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_END")); 03269 } 03270 void DisplayUsage(void) { 03271 Bit8u c;Bit16u n=1; 03272 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP")); 03273 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_1")); 03274 DOS_ReadFile (STDIN,&c,&n); 03275 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP")); 03276 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_2")); 03277 DOS_ReadFile (STDIN,&c,&n); 03278 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP")); 03279 WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_3")); 03280 DOS_ReadFile (STDIN,&c,&n); 03281 } 03282 void DisplayIntro(void) { 03283 WriteOut(MSG_Get("PROGRAM_INTRO")); 03284 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_UP")); 03285 } 03286 void DisplayMenuBefore(void) { WriteOut("\033[44m\033[K\033[33m\033[1m \033[0m "); } 03287 void DisplayMenuCursorStart(void) { 03288 if (machine == MCH_PC98) { 03289 WriteOut("\033[44m\033[K\033[1m\033[33;44m \x1C\033[0m\033[5;37;44m "); 03290 } else { 03291 WriteOut("\033[44m\033[K\033[1m\033[33;44m \x10\033[0m\033[5;37;44m "); 03292 } 03293 } 03294 void DisplayMenuCursorEnd(void) { WriteOut("\033[0m\n"); } 03295 void DisplayMenuNone(void) { WriteOut("\033[44m\033[K\033[0m\n"); } 03296 03297 bool CON_IN(Bit8u * data) { 03298 Bit8u c; 03299 Bit16u n=1; 03300 03301 /* handle arrow keys vs normal input, 03302 * with special conditions for PC-98 and IBM PC */ 03303 if (!DOS_ReadFile(STDIN,&c,&n) || n == 0) return false; 03304 03305 if (IS_PC98_ARCH) { 03306 /* translate PC-98 arrow keys to IBM PC escape for the caller */ 03307 if (c == 0x0B) 03308 *data = 0x48 | 0x80; /* IBM extended code up arrow */ 03309 else if (c == 0x0A) 03310 *data = 0x50 | 0x80; /* IBM extended code down arrow */ 03311 else 03312 *data = c; 03313 } 03314 else { 03315 if (c == 0) { 03316 if (!DOS_ReadFile(STDIN,&c,&n) || n == 0) return false; 03317 *data = c | 0x80; /* extended code */ 03318 } 03319 else { 03320 *data = c; 03321 } 03322 } 03323 03324 return true; 03325 } 03326 03327 void Run(void) { 03328 if (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)) { 03329 WriteOut("A full-screen introduction to DOSBox-X.\n\nINTRO [CDROM|MOUNT|USAGE]\n"); 03330 return; 03331 } 03332 std::string menuname = "BASIC"; // default 03333 /* Only run if called from the first shell (Xcom TFTD runs any intro file in the path) */ 03334 if(DOS_PSP(dos.psp()).GetParent() != DOS_PSP(DOS_PSP(dos.psp()).GetParent()).GetParent()) return; 03335 if(cmd->FindExist("cdrom",false)) { 03336 WriteOut(MSG_Get("PROGRAM_INTRO_CDROM")); 03337 return; 03338 } 03339 if(cmd->FindExist("mount",false)) { 03340 WriteOut("\033[2J");//Clear screen before printing 03341 DisplayMount(); 03342 return; 03343 } 03344 03345 if(cmd->FindExist("usage",false)) { DisplayUsage(); return; } 03346 Bit8u c;Bit16u n=1; 03347 03348 #define CURSOR(option) \ 03349 if (menuname==option) DisplayMenuCursorStart(); \ 03350 else DisplayMenuBefore(); \ 03351 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_" option "")); \ 03352 if (menuname==option) DisplayMenuCursorEnd(); \ 03353 else WriteOut("\n"); 03354 03355 /* Intro */ 03356 03357 menufirst: 03358 DisplayIntro(); 03359 CURSOR("BASIC") 03360 CURSOR("CDROM") 03361 CURSOR("USAGE") 03362 DisplayMenuNone(); // None 03363 CURSOR("INFO") 03364 CURSOR("QUIT") 03365 DisplayMenuNone(); // None 03366 03367 if (menuname=="BASIC") goto basic; 03368 else if (menuname=="CDROM") goto cdrom; 03369 else if (menuname=="USAGE") goto usage; 03370 else if (menuname=="INFO") goto info; 03371 else if (menuname=="QUIT") goto quit; 03372 else if (menuname=="GOTO_EXIT") goto goto_exit; 03373 03374 goto_exit: 03375 WriteOut("\n"); // Give a line 03376 return; 03377 03378 basic: 03379 menuname="BASIC"; 03380 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_BASIC_HELP")); 03381 CON_IN(&c); 03382 do switch (c) { 03383 case 0x48|0x80: menuname="QUIT"; goto menufirst; // Up 03384 case 0x50|0x80: menuname="CDROM"; goto menufirst; // Down 03385 case 0xD: // Run 03386 WriteOut("\033[2J"); 03387 WriteOut(MSG_Get("PROGRAM_INTRO")); 03388 WriteOut("\n"); 03389 DisplayMount(); 03390 DOS_ReadFile (STDIN,&c,&n); 03391 goto menufirst; 03392 } while (CON_IN(&c)); 03393 03394 cdrom: 03395 menuname="CDROM"; 03396 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_CDROM_HELP")); 03397 CON_IN(&c); 03398 do switch (c) { 03399 case 0x48|0x80: menuname="BASIC"; goto menufirst; // Up 03400 case 0x50|0x80: menuname="USAGE"; goto menufirst; // Down 03401 case 0xD: // Run 03402 WriteOut(MSG_Get("PROGRAM_INTRO_CDROM")); 03403 DOS_ReadFile (STDIN,&c,&n); 03404 goto menufirst; 03405 } while (CON_IN(&c)); 03406 03407 usage: 03408 menuname="USAGE"; 03409 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_USAGE_HELP")); 03410 CON_IN(&c); 03411 do switch (c) { 03412 case 0x48|0x80: menuname="CDROM"; goto menufirst; // Down 03413 case 0x50|0x80: menuname="INFO"; goto menufirst; // Down 03414 case 0xD: // Run 03415 DisplayUsage(); 03416 goto menufirst; 03417 } while (CON_IN(&c)); 03418 03419 info: 03420 menuname="INFO"; 03421 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_INFO_HELP")); 03422 CON_IN(&c); 03423 do switch (c) { 03424 case 0x48|0x80: menuname="USAGE"; goto menufirst; // Up 03425 case 0x50|0x80: menuname="QUIT"; goto menufirst; // Down 03426 case 0xD: // Run 03427 WriteOut("\033[2J"); 03428 WriteOut(MSG_Get("PROGRAM_INTRO")); 03429 WriteOut("\n"); 03430 WriteOut(MSG_Get("PROGRAM_INTRO_INFO")); 03431 DOS_ReadFile (STDIN,&c,&n); 03432 goto menufirst; 03433 } while (CON_IN(&c)); 03434 03435 quit: 03436 menuname="QUIT"; 03437 WriteOut(MSG_Get("PROGRAM_INTRO_MENU_QUIT_HELP")); 03438 CON_IN(&c); 03439 do switch (c) { 03440 case 0x48|0x80: menuname="INFO"; goto menufirst; // Up 03441 case 0x50|0x80: menuname="BASIC"; goto menufirst; // Down 03442 case 0xD: // Run 03443 menuname="GOTO_EXIT"; 03444 goto menufirst; 03445 } while (CON_IN(&c)); 03446 } 03447 }; 03448 03449 bool ElTorito_ChecksumRecord(unsigned char *entry/*32 bytes*/) { 03450 unsigned int chk=0,i; 03451 03452 for (i=0;i < 16;i++) { 03453 unsigned int word = ((unsigned int)entry[0]) + ((unsigned int)entry[1] << 8); 03454 chk += word; 03455 entry += 2; 03456 } 03457 chk &= 0xFFFF; 03458 return (chk == 0); 03459 } 03460 03461 static void INTRO_ProgramStart(Program * * make) { 03462 *make=new INTRO; 03463 } 03464 03465 bool ElTorito_ScanForBootRecord(CDROM_Interface *drv,unsigned long &boot_record,unsigned long &el_torito_base) { 03466 unsigned char buffer[2048]; 03467 unsigned int sec; 03468 03469 for (sec=16;sec < 32;sec++) { 03470 if (!drv->ReadSectorsHost(buffer,false,sec,1)) 03471 break; 03472 03473 /* stop at terminating volume record */ 03474 if (buffer[0] == 0xFF) break; 03475 03476 /* match boot record and whether it conforms to El Torito */ 03477 if (buffer[0] == 0x00 && memcmp(buffer+1,"CD001",5) == 0 && buffer[6] == 0x01 && 03478 memcmp(buffer+7,"EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0",32) == 0) { 03479 boot_record = sec; 03480 el_torito_base = (unsigned long)buffer[71] + 03481 ((unsigned long)buffer[72] << 8UL) + 03482 ((unsigned long)buffer[73] << 16UL) + 03483 ((unsigned long)buffer[74] << 24UL); 03484 03485 return true; 03486 } 03487 } 03488 03489 return false; 03490 } 03491 03492 03493 /* C++ class implementing El Torito floppy emulation */ 03494 class imageDiskElToritoFloppy : public imageDisk { 03495 public: 03496 /* Read_Sector and Write_Sector take care of geometry translation for us, 03497 * then call the absolute versions. So, we override the absolute versions only */ 03498 virtual Bit8u Read_AbsoluteSector(Bit32u sectnum, void * data) { 03499 unsigned char buffer[2048]; 03500 03501 bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom); 03502 03503 CDROM_Interface *src_drive=NULL; 03504 if (!GetMSCDEXDrive(CDROM_drive-'A',&src_drive)) return 0x05; 03505 03506 if (!src_drive->ReadSectorsHost(buffer,false,cdrom_sector_offset+(sectnum>>2)/*512 byte/sector to 2048 byte/sector conversion*/,1)) 03507 return 0x05; 03508 03509 memcpy(data,buffer+((sectnum&3)*512),512); 03510 return 0x00; 03511 } 03512 virtual Bit8u Write_AbsoluteSector(Bit32u sectnum,const void * data) { 03513 (void)sectnum;//UNUSED 03514 (void)data;//UNUSED 03515 return 0x05; /* fail, read only */ 03516 } 03517 imageDiskElToritoFloppy(unsigned char new_CDROM_drive,unsigned long new_cdrom_sector_offset,unsigned char floppy_emu_type) : imageDisk(NULL,NULL,0,false) { 03518 diskimg = NULL; 03519 sector_size = 512; 03520 CDROM_drive = new_CDROM_drive; 03521 cdrom_sector_offset = new_cdrom_sector_offset; 03522 class_id = ID_EL_TORITO_FLOPPY; 03523 03524 if (floppy_emu_type == 1) { /* 1.2MB */ 03525 heads = 2; 03526 cylinders = 80; 03527 sectors = 15; 03528 } 03529 else if (floppy_emu_type == 2) { /* 1.44MB */ 03530 heads = 2; 03531 cylinders = 80; 03532 sectors = 18; 03533 } 03534 else if (floppy_emu_type == 3) { /* 2.88MB */ 03535 heads = 2; 03536 cylinders = 80; 03537 sectors = 36; /* FIXME: right? */ 03538 } 03539 else { 03540 heads = 2; 03541 cylinders = 69; 03542 sectors = 14; 03543 LOG_MSG("BUG! unsupported floppy_emu_type in El Torito floppy object\n"); 03544 } 03545 03546 diskSizeK = ((Bit64u)heads * cylinders * sectors * sector_size) / 1024; 03547 active = true; 03548 } 03549 virtual ~imageDiskElToritoFloppy() { 03550 } 03551 03552 unsigned long cdrom_sector_offset; 03553 unsigned char CDROM_drive; 03554 /* 03555 int class_id; 03556 03557 bool hardDrive; 03558 bool active; 03559 FILE *diskimg; 03560 std::string diskname; 03561 Bit8u floppytype; 03562 03563 Bit32u sector_size; 03564 Bit32u heads,cylinders,sectors; 03565 Bit32u reserved_cylinders; 03566 Bit64u current_fpos; */ 03567 }; 03568 03569 bool FDC_AssignINT13Disk(unsigned char drv); 03570 bool FDC_UnassignINT13Disk(unsigned char drv); 03571 03572 class IMGMOUNT : public Program { 03573 public: 03574 std::vector<std::string> options; 03575 void ListImgMounts(void) { 03576 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH]; 03577 Bit32u size;Bit16u date;Bit16u time;Bit8u attr; 03578 /* Command uses dta so set it to our internal dta */ 03579 RealPt save_dta = dos.dta(); 03580 dos.dta(dos.tables.tempdta); 03581 DOS_DTA dta(dos.dta()); 03582 03583 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_STATUS_1")); 03584 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label"); 03585 for(int p = 0;p < 8;p++) WriteOut("----------"); 03586 03587 for (int d = 0;d < DOS_DRIVES;d++) { 03588 if (!Drives[d] || (strncmp(Drives[d]->GetInfo(), "fatDrive ", 9) && strncmp(Drives[d]->GetInfo(), "isoDrive ", 9))) continue; 03589 char root[7] = {(char)('A'+d),':','\\','*','.','*',0}; 03590 bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME); 03591 if (ret) { 03592 dta.GetResult(name,lname,size,date,time,attr); 03593 DOS_FindNext(); //Mark entry as invalid 03594 } else name[0] = 0; 03595 03596 /* Change 8.3 to 11.0 */ 03597 const char* dot = strchr(name, '.'); 03598 if(dot && (dot - name == 8) ) { 03599 name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0; 03600 } 03601 03602 root[1] = 0; //This way, the format string can be reused. 03603 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name); 03604 } 03605 WriteOut("\n"); 03606 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_STATUS_2")); 03607 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_NUMBER_FORMAT"),"Drive number","Disk name"); 03608 for(int p = 0;p < 8;p++) WriteOut("----------"); 03609 for (int index = 0; index < MAX_DISK_IMAGES; index++) 03610 if (imageDiskList[index]) WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_NUMBER_FORMAT"), std::to_string(index).c_str(), imageDiskList[index]->diskname.c_str()); 03611 dos.dta(save_dta); 03612 } 03613 void Run(void) { 03614 //Hack To allow long commandlines 03615 ChangeToLongCmd(); 03616 /* In secure mode don't allow people to change imgmount points. 03617 * Neither mount nor unmount */ 03618 if(control->SecureMode()) { 03619 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); 03620 return; 03621 } 03622 imageDisk * newImage; 03623 char drive; 03624 std::vector<std::string> paths; 03625 if (!cmd->GetCount()) { 03626 ListImgMounts(); 03627 return; 03628 } 03629 //show help if /? or -? 03630 if (cmd->FindExist("/?", true) || cmd->FindExist("-?", true) || cmd->FindExist("-help", true)) { 03631 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_HELP")); 03632 return; 03633 } 03634 /* Check for unmounting */ 03635 std::string umount; 03636 if (cmd->FindString("-u",umount,false)) { 03637 Unmount(umount[0]); 03638 return; 03639 } 03640 03641 //initialize more variables 03642 unsigned long el_torito_floppy_base=~0UL; 03643 unsigned char el_torito_floppy_type=0xFF; 03644 bool ide_slave = false; 03645 signed char ide_index = -1; 03646 char el_torito_cd_drive = 0; 03647 std::string el_torito; 03648 std::string ideattach="auto"; 03649 std::string type="hdd"; 03650 03651 //this code simply sets default type to "floppy" if mounting at A: or B: --- nothing else 03652 // get first parameter - which is probably the drive letter to mount at (A-Z or A:-Z:) - and check it if is A or B or A: or B: 03653 // default to floppy for drive letters A and B and numbers 0 and 1 03654 if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || 03655 ((temp_line.size()>1) && (temp_line[1]!=':'))) { 03656 // drive not valid 03657 } else { 03658 Bit8u tdr = toupper(temp_line[0]); 03659 if(tdr=='A'||tdr=='B'||tdr=='0'||tdr=='1') type="floppy"; 03660 } 03661 03662 //get the type 03663 bool rtype=cmd->FindString("-t", type, true); 03664 std::transform(type.begin(), type.end(), type.begin(), ::tolower); 03665 03666 if (type == "cdrom") type = "iso"; //Tiny hack for people who like to type -t cdrom 03667 if (!(type == "floppy" || type == "hdd" || type == "iso" || type == "ram")) { 03668 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED"), type.c_str()); 03669 return; 03670 } 03671 03672 //look for -o options 03673 { 03674 std::string s; 03675 03676 while (cmd->FindString("-o", s, true)) 03677 options.push_back(s); 03678 } 03679 03680 //look for -el-torito parameter and remove it from the command line 03681 cmd->FindString("-el-torito",el_torito,true); 03682 if (el_torito != "") { 03683 //get el-torito floppy from cdrom mounted at drive letter el_torito_cd_drive 03684 el_torito_cd_drive = toupper(el_torito[0]); 03685 //validate the el_torito loading (look for boot image on the cdrom, etc), and 03686 // find the el_torito_floppy_base and el_torito_floppy_type values 03687 if (!PrepElTorito(type, el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type)) return; 03688 } 03689 03690 if (temp_line.size() == 1 && isdigit(temp_line[0]) && temp_line[0]>='0' && temp_line[0]<MAX_DISK_IMAGES+'0' && cmd->FindExist("-u",true)) { 03691 Unmount(temp_line[0]); 03692 if (!cmd->FindCommand(2,temp_line)||!temp_line.size()) return; 03693 } 03694 03695 //default fstype is fat 03696 std::string fstype="fat"; 03697 bool rfstype=cmd->FindString("-fs",fstype,true); 03698 std::transform(fstype.begin(), fstype.end(), fstype.begin(), ::tolower); 03699 03700 Bitu sizes[4] = { 0,0,0,0 }; 03701 int reserved_cylinders=0; 03702 std::string reservecyl; 03703 03704 /* DOSBox-X: to please certain 32-bit drivers like Windows 3.1 WDCTRL, or to emulate older h/w configurations, 03705 * we allow the user or script to specify the number of reserved cylinders. older BIOSes were known 03706 * to subtract 1 or 2 additional cylinders from the total in the fixed disk param table. the -reservecyl 03707 * option allows the number we subtract from the total in INT 13H to be set */ 03708 cmd->FindString("-reservecyl",reservecyl,true); 03709 if (reservecyl != "") reserved_cylinders = atoi(reservecyl.c_str()); 03710 03711 /* DOSBox-X: we allow "-ide" to allow controlling which IDE controller and slot to attach the hard disk/CD-ROM to */ 03712 cmd->FindString("-ide",ideattach,true); 03713 std::transform(ideattach.begin(), ideattach.end(), ideattach.begin(), ::tolower); 03714 03715 if (ideattach == "auto") { 03716 if (type != "floppy") { 03717 IDE_Auto(ide_index,ide_slave); 03718 } 03719 03720 LOG_MSG("IDE: index %d slave=%d",ide_index,ide_slave?1:0); 03721 } 03722 else if (ideattach != "none" && isdigit(ideattach[0]) && ideattach[0] > '0') { /* takes the form [controller]<m/s> such as: 1m for primary master */ 03723 ide_index = ideattach[0] - '1'; 03724 if (ideattach.length() >= 2) ide_slave = (ideattach[1] == 's'); 03725 LOG_MSG("IDE: index %d slave=%d",ide_index,ide_slave?1:0); 03726 } 03727 03728 //if floppy, don't attach to ide controller 03729 //if cdrom, file system is iso 03730 if (type=="floppy") { 03731 ideattach="none"; 03732 } else if (type=="iso") { 03733 //str_size=="2048,1,60000,0"; // ignored, see drive_iso.cpp (AllocationInfo) 03734 fstype = "iso"; 03735 } 03736 03737 //load the size parameter 03738 //auto detect hard drives if not specified 03739 std::string str_size; 03740 std::string str_chs; 03741 cmd->FindString("-size", str_size, true); 03742 cmd->FindString("-chs", str_chs, true); 03743 if (!ReadSizeParameter(str_size, str_chs, type, sizes)) return; 03744 03745 //for floppies, hard drives, and cdroms, require a drive letter 03746 //for -fs none, require a number indicating where to mount at 03747 if(fstype=="fat" || fstype=="iso") { 03748 // get the drive letter 03749 if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) { 03750 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); 03751 return; 03752 } 03753 int i_drive = toupper(temp_line[0]); 03754 if (!isalpha(i_drive) || (i_drive - 'A') >= DOS_DRIVES || (i_drive - 'A') < 0) { 03755 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); 03756 return; 03757 } 03758 drive = static_cast<char>(i_drive); 03759 } else if (fstype=="none") { 03760 cmd->FindCommand(1,temp_line); 03761 if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) { 03762 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); 03763 return; 03764 } 03765 drive=temp_line[0]; 03766 if ((drive<'0') || (drive>=MAX_DISK_IMAGES+'0')) { 03767 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); 03768 return; 03769 } 03770 } else { 03771 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED"),fstype.c_str()); 03772 return; 03773 } 03774 03775 // find all file parameters, assuming that all option parameters have been removed 03776 ParseFiles(temp_line, paths); 03777 03778 // some generic checks 03779 if (el_torito != "") { 03780 if (paths.size() != 0) { 03781 WriteOut("Do not specify files when mounting virtual floppy disk images from El Torito bootable CDs\n"); 03782 return; 03783 } 03784 } 03785 else if (type == "ram") { 03786 if (paths.size() != 0) { 03787 WriteOut("Do not specify files when mounting RAM drives\n"); 03788 return; 03789 } 03790 } 03791 else { 03792 if (paths.size() == 0) { 03793 if (strcasecmp(temp_line.c_str(), "-u")) WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_FILE")); 03794 return; 03795 } 03796 if (!rtype&&!rfstype&&paths[0].length()>4) { 03797 char ext[5]; 03798 strncpy(ext, paths[0].substr(paths[0].length()-4).c_str(), 4); 03799 ext[4]=0; 03800 if (!strcasecmp(ext, ".iso")||!strcasecmp(ext, ".cue")||!strcasecmp(ext, ".bin")||!strcasecmp(ext, ".mdf")) { 03801 type="iso"; 03802 fstype="iso"; 03803 } 03804 } 03805 } 03806 03807 //====== call the proper subroutine ====== 03808 if(fstype=="fat") { 03809 //mount floppy or hard drive 03810 if (el_torito != "") { 03811 if (!MountElToritoFat(drive, sizes, el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type)) return; 03812 } 03813 else if (type == "ram") { 03814 if (!MountRam(sizes, drive, ide_index, ide_slave)) return; 03815 } 03816 else { 03817 //supports multiple files 03818 if (!MountFat(sizes, drive, type == "hdd", str_size, paths, ide_index, ide_slave)) return; 03819 } 03820 } else if (fstype=="iso") { 03821 if (el_torito != "") { 03822 WriteOut("El Torito bootable CD: -fs iso mounting not supported\n"); /* <- NTS: Will never implement, either */ 03823 return; 03824 } 03825 //supports multiple files 03826 if (!MountIso(drive, paths, ide_index, ide_slave)) return; 03827 } else if (fstype=="none") { 03828 unsigned char driveIndex = drive - '0'; 03829 03830 if (paths.size() > 1) { 03831 if (driveIndex <= 1) { 03832 if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive <= 1 && 03833 swapInDisksSpecificDrive != driveIndex) { 03834 WriteOut("Multiple images given and another drive already uses multiple images"); 03835 return; 03836 } 03837 } 03838 else { 03839 WriteOut("Multiple disk images not supported for that drive"); 03840 return; 03841 } 03842 } 03843 03844 if (el_torito != "") { 03845 newImage = new imageDiskElToritoFloppy((unsigned char)el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type); 03846 } 03847 else if (type == "ram") { 03848 newImage = MountImageNoneRam(sizes, reserved_cylinders, driveIndex < 2); 03849 } 03850 else { 03851 newImage = MountImageNone(paths[0].c_str(), sizes, reserved_cylinders); 03852 } 03853 if (newImage == NULL) return; 03854 newImage->Addref(); 03855 if (newImage->hardDrive && (driveIndex < 2)) { 03856 WriteOut("Cannot mount hard drive in floppy position"); 03857 } 03858 else if (!newImage->hardDrive && (driveIndex >= 2)) { 03859 WriteOut("Cannot mount floppy in hard drive position"); 03860 } 03861 else { 03862 if (AttachToBiosAndIdeByIndex(newImage, (unsigned char)driveIndex, (unsigned char)ide_index, ide_slave)) { 03863 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT_NUMBER"), drive - '0', (!paths.empty()) ? (wpcolon&&paths[0].length()>1&&paths[0].c_str()[0]==':'?paths[0].c_str()+1:paths[0].c_str()) : (el_torito != ""?"El Torito floppy drive":(type == "ram"?"RAM drive":"-"))); 03864 03865 if (paths.size() > 1) { 03866 for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) { 03867 if (diskSwap[si] != NULL) { 03868 diskSwap[si]->Release(); 03869 diskSwap[si] = NULL; 03870 } 03871 } 03872 03873 /* slot 0 is the image we already assigned */ 03874 diskSwap[0] = newImage; 03875 diskSwap[0]->Addref(); 03876 swapPosition = 0; 03877 swapInDisksSpecificDrive = driveIndex; 03878 03879 for (size_t si=1;si < MAX_SWAPPABLE_DISKS && si < paths.size();si++) { 03880 imageDisk *img = MountImageNone(paths[si].c_str(), sizes, reserved_cylinders); 03881 03882 if (img != NULL) { 03883 diskSwap[si] = img; 03884 diskSwap[si]->Addref(); 03885 } 03886 } 03887 } 03888 } 03889 else { 03890 WriteOut("Invalid mount number"); 03891 } 03892 } 03893 newImage->Release(); 03894 return; 03895 } 03896 else { 03897 WriteOut("Invalid fstype\n"); 03898 return; 03899 } 03900 03901 return; 03902 } 03903 03904 private: 03905 bool ReadSizeParameter(const std::string &str_size, const std::string &str_chs, const std::string &type, Bitu sizes[]) { 03906 bool isCHS = false; 03907 const char * scan; 03908 if (str_chs.size() != 0) { 03909 if (str_size.size() != 0) { 03910 WriteOut("Size and chs parameter cannot both be specified\n"); 03911 return false; 03912 } 03913 isCHS = true; 03914 scan = str_chs.c_str(); 03915 } 03916 else if (str_size.size() != 0) { 03917 scan = str_size.c_str(); 03918 } 03919 else { 03920 //nothing specified, so automatic size detection 03921 return true; 03922 } 03923 char number[20]; 03924 Bitu index = 0; 03925 Bitu count = 0; 03926 int val; 03927 03928 //scan through input string 03929 while (*scan) { 03930 //separate string by ',' 03931 if (*scan == ',') { 03932 number[index] = 0; //add null char 03933 val = atoi(number); 03934 if (val <= 0) { 03935 //out of range 03936 WriteOut("Invalid size parameter\n"); 03937 return false; 03938 } 03939 sizes[count++] = (unsigned int)val; 03940 index = 0; 03941 if (count == 4) { 03942 //too many commas 03943 WriteOut("Invalid size parameter\n"); 03944 return false; 03945 } 03946 } 03947 else if (index >= 19) { 03948 //number too large (too many characters, anyway) 03949 WriteOut("Invalid size parameter\n"); 03950 return false; 03951 } 03952 else { 03953 number[index++] = *scan; 03954 } 03955 scan++; 03956 } 03957 number[index] = 0; 03958 val = atoi(number); 03959 if (val <= 0) { 03960 //out of range 03961 WriteOut("Invalid size parameter\n"); 03962 return false; 03963 } 03964 sizes[count++] = (unsigned int)val; 03965 if (isCHS) { 03966 if (count == 3) sizes[count++] = 512; //set sector size automatically 03967 if (count != 4) { 03968 WriteOut("Invalid chs parameter\n"); 03969 return false; 03970 } 03971 Bitu temp = sizes[3]; //hold on to sector size temporarily 03972 sizes[3] = sizes[0]; //put cylinders in the right spot 03973 sizes[0] = temp; //put sector size in the right spot 03974 temp = sizes[2]; //hold on to sectors temporarily 03975 sizes[2] = sizes[1]; //put heads in the right spot 03976 sizes[1] = temp; //put sectors in the right spot 03977 } 03978 if (!((type == "ram" && count == 1) || count == 4)) { 03979 //ram drives require 1 or 4 numbers 03980 //other drives require 4 numbers 03981 WriteOut("Invalid size parameter\n"); 03982 return false; 03983 } 03984 return true; 03985 } 03986 void ParseFiles(std::string &commandLine, std::vector<std::string> &paths) { 03987 char drive=commandLine[0]; 03988 while (cmd->FindCommand((unsigned int)(paths.size() + 2), commandLine) && commandLine.size()) { 03989 #if defined (WIN32) || defined(OS2) 03990 /* nothing */ 03991 #else 03992 // Linux: Convert backslash to forward slash 03993 if (commandLine.size() > 0) { 03994 for (size_t i = 0; i < commandLine.size(); i++) { 03995 if (commandLine[i] == '\\') 03996 commandLine[i] = '/'; 03997 } 03998 } 03999 #endif 04000 04001 if (!strcasecmp(commandLine.c_str(), "-u")) { 04002 Unmount(drive); 04003 return; 04004 } 04005 04006 pref_struct_stat test; 04007 char fullname[CROSS_LEN]; 04008 char tmp[CROSS_LEN]; 04009 safe_strncpy(tmp, wpcolon&&commandLine.length()>1&&commandLine[0]==':'?commandLine.c_str()+1:commandLine.c_str(), CROSS_LEN); 04010 if (pref_stat(tmp, &test)) { 04011 //See if it works if the ~ are written out 04012 std::string homedir(commandLine); 04013 Cross::ResolveHomedir(homedir); 04014 if (!pref_stat(homedir.c_str(), &test)) { 04015 commandLine = homedir; 04016 } 04017 else { 04018 // convert dosbox filename to system filename 04019 Bit8u dummy; 04020 if (!DOS_MakeName(tmp, fullname, &dummy) || strncmp(Drives[dummy]->GetInfo(), "local directory", 15)) { 04021 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE")); 04022 return; 04023 } 04024 04025 localDrive *ldp = dynamic_cast<localDrive*>(Drives[dummy]); 04026 if (ldp == NULL) { 04027 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); 04028 return; 04029 } 04030 bool readonly=wpcolon&&commandLine.length()>1&&commandLine[0]==':'; 04031 ldp->GetSystemFilename(readonly?tmp+1:tmp, fullname); 04032 if (readonly) tmp[0]=':'; 04033 commandLine = tmp; 04034 04035 if (pref_stat(readonly?tmp+1:tmp, &test)) { 04036 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); 04037 return; 04038 } 04039 } 04040 } 04041 if (S_ISDIR(test.st_mode)) { 04042 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT")); 04043 return; 04044 } 04045 paths.push_back(commandLine); 04046 } 04047 } 04048 04049 bool Unmount(char &letter) { 04050 letter = toupper(letter); 04051 if (isalpha(letter)) { /* if it's a drive letter, then traditional usage applies */ 04052 int i_drive = letter - 'A'; 04053 if (i_drive < DOS_DRIVES && i_drive >= 0 && Drives[i_drive]) { 04054 //if drive A: or B: 04055 if (i_drive <= 1) 04056 FDC_UnassignINT13Disk(i_drive); 04057 04058 //get reference to image and cdrom before they are possibly destroyed 04059 const fatDrive* drive = dynamic_cast<fatDrive*>(Drives[i_drive]); 04060 imageDisk* image = drive ? drive->loadedDisk : NULL; 04061 const isoDrive* cdrom = dynamic_cast<isoDrive*>(Drives[i_drive]); 04062 04063 switch (DriveManager::UnmountDrive(i_drive)) { 04064 case 0: //success 04065 { 04066 //detatch hard drive or floppy drive from bios and ide controller 04067 if (image) DetachFromBios(image); 04068 04069 /* If the drive letter is also a CD-ROM drive attached to IDE, then let the IDE code know */ 04070 if (cdrom) IDE_CDROM_Detach(i_drive); 04071 04072 Drives[i_drive] = NULL; 04073 DOS_EnableDriveMenu(i_drive+'A'); 04074 if (i_drive == DOS_GetDefaultDrive()) 04075 DOS_SetDrive(toupper('Z') - 'A'); 04076 WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"), letter); 04077 return true; 04078 } 04079 case 1: 04080 WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL")); 04081 return false; 04082 case 2: 04083 WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); 04084 return false; 04085 default: 04086 return false; 04087 } 04088 } 04089 else { 04090 WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"), letter); 04091 return false; 04092 } 04093 } 04094 else if (isdigit(letter)) { /* DOSBox-X: drives mounted by number (INT 13h) can be unmounted this way */ 04095 int index = letter - '0'; 04096 04097 //detatch hard drive or floppy drive from bios and ide controller 04098 if (index < MAX_DISK_IMAGES && imageDiskList[index]) { 04099 if (index > 1) IDE_Hard_Disk_Detach(index); 04100 imageDiskList[index]->Release(); 04101 imageDiskList[index] = NULL; 04102 imageDiskChange[index] = true; 04103 WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NUMBER_SUCCESS"), letter); 04104 return true; 04105 } 04106 WriteOut("No drive loaded at specified point\n"); 04107 return false; 04108 } 04109 else { 04110 WriteOut("Incorrect IMGMOUNT unmount usage\n"); 04111 return false; 04112 } 04113 } 04114 04115 bool PrepElTorito(const std::string& type, const char &el_torito_cd_drive, unsigned long &el_torito_floppy_base, unsigned char &el_torito_floppy_type) { 04116 el_torito_floppy_base = ~0UL; 04117 el_torito_floppy_type = 0xFF; 04118 04119 unsigned char entries[2048], *entry, ent_num = 0; 04120 int header_platform = -1, header_count = 0; 04121 bool header_final = false; 04122 int header_more = -1; 04123 04124 /* must be valid drive letter, C to Z */ 04125 if (!isalpha(el_torito_cd_drive) || el_torito_cd_drive < 'C') { 04126 WriteOut("-el-torito requires a proper drive letter corresponding to your CD-ROM drive\n"); 04127 return false; 04128 } 04129 04130 /* drive must not exist (as a hard drive) */ 04131 if (imageDiskList[el_torito_cd_drive - 'A'] != NULL) { 04132 WriteOut("-el-torito CD-ROM drive specified already exists as a non-CD-ROM device\n"); 04133 return false; 04134 } 04135 04136 bool GetMSCDEXDrive(unsigned char drive_letter, CDROM_Interface **_cdrom); 04137 04138 /* get the CD-ROM drive */ 04139 CDROM_Interface *src_drive = NULL; 04140 if (!GetMSCDEXDrive(el_torito_cd_drive - 'A', &src_drive)) { 04141 WriteOut("-el-torito CD-ROM drive specified is not actually a CD-ROM drive\n"); 04142 return false; 04143 } 04144 04145 /* FIXME: We only support the floppy emulation mode at this time. 04146 * "Superfloppy" or hard disk emulation modes are not yet implemented */ 04147 if (type != "floppy") { 04148 WriteOut("-el-torito must be used with -t floppy at this time\n"); 04149 return false; 04150 } 04151 04152 /* Okay. Step #1: Scan the volume descriptors for the Boot Record. */ 04153 unsigned long el_torito_base = 0, boot_record_sector = 0; 04154 if (!ElTorito_ScanForBootRecord(src_drive, boot_record_sector, el_torito_base)) { 04155 WriteOut("El Torito boot record not found\n"); 04156 return false; 04157 } 04158 04159 LOG_MSG("El Torito emulation: Found ISO 9660 Boot Record in sector %lu, pointing to sector %lu\n", 04160 boot_record_sector, el_torito_base); 04161 04162 /* Step #2: Parse the records. Each one is 32 bytes long */ 04163 if (!src_drive->ReadSectorsHost(entries, false, el_torito_base, 1)) { 04164 WriteOut("El Torito entries unreadable\n"); 04165 return false; 04166 } 04167 04168 /* for more information about what this loop is doing, read: 04169 * http://download.intel.com/support/motherboards/desktop/sb/specscdrom.pdf 04170 */ 04171 /* FIXME: Somebody find me an example of a CD-ROM with bootable code for both x86, PowerPC, and Macintosh. 04172 * I need an example of such a CD since El Torito allows multiple "headers" */ 04173 /* TODO: Is it possible for this record list to span multiple sectors? */ 04174 for (ent_num = 0; ent_num < (2048 / 0x20); ent_num++) { 04175 entry = entries + (ent_num * 0x20); 04176 04177 if (memcmp(entry, "\0\0\0\0""\0\0\0\0""\0\0\0\0""\0\0\0\0""\0\0\0\0""\0\0\0\0""\0\0\0\0""\0\0\0\0", 32) == 0) 04178 break; 04179 04180 if (entry[0] == 0x01/*header*/) { 04181 if (!ElTorito_ChecksumRecord(entry)) { 04182 LOG_MSG("Warning: El Torito checksum error in header(0x01) entry\n"); 04183 continue; 04184 } 04185 04186 if (header_count != 0) { 04187 LOG_MSG("Warning: El Torito has more than one Header/validation entry\n"); 04188 continue; 04189 } 04190 04191 if (header_final) { 04192 LOG_MSG("Warning: El Torito has an additional header past the final header\n"); 04193 continue; 04194 } 04195 04196 header_more = -1; 04197 header_platform = entry[1]; 04198 LOG_MSG("El Torito entry: first header platform=0x%02x\n", header_platform); 04199 header_count++; 04200 } 04201 else if (entry[0] == 0x90/*header, more follows*/ || entry[0] == 0x91/*final header*/) { 04202 if (header_final) { 04203 LOG_MSG("Warning: El Torito has an additional header past the final header\n"); 04204 continue; 04205 } 04206 04207 header_final = (entry[0] == 0x91); 04208 header_more = (int)(((unsigned int)entry[2]) + (((unsigned int)entry[3]) << 8u)); 04209 header_platform = entry[1]; 04210 LOG_MSG("El Torito entry: first header platform=0x%02x more=%u final=%u\n", header_platform, header_more, header_final); 04211 header_count++; 04212 } 04213 else { 04214 if (header_more == 0) { 04215 LOG_MSG("El Torito entry: Non-header entry count expired, ignoring record 0x%02x\n", entry[0]); 04216 continue; 04217 } 04218 else if (header_more > 0) { 04219 header_more--; 04220 } 04221 04222 if (entry[0] == 0x44) { 04223 LOG_MSG("El Torito entry: ignoring extension record\n"); 04224 } 04225 else if (entry[0] == 0x00/*non-bootable*/) { 04226 LOG_MSG("El Torito entry: ignoring non-bootable record\n"); 04227 } 04228 else if (entry[0] == 0x88/*bootable*/) { 04229 if (header_platform == 0x00/*x86*/) { 04230 unsigned char mediatype = entry[1] & 0xF; 04231 unsigned short load_segment = ((unsigned int)entry[2]) + (((unsigned int)entry[3]) << 8); 04232 unsigned char system_type = entry[4]; 04233 unsigned short sector_count = ((unsigned int)entry[6]) + (((unsigned int)entry[7]) << 8); 04234 unsigned long load_rba = ((unsigned int)entry[8]) + (((unsigned int)entry[9]) << 8) + 04235 (((unsigned int)entry[10]) << 16) + (((unsigned int)entry[11]) << 24); 04236 04237 LOG_MSG("El Torito entry: bootable x86 record mediatype=%u load_segment=0x%04x " 04238 "system_type=0x%02x sector_count=%u load_rba=%lu\n", 04239 mediatype, load_segment, system_type, sector_count, load_rba); 04240 04241 /* already chose one, ignore */ 04242 if (el_torito_floppy_base != ~0UL) 04243 continue; 04244 04245 if (load_segment != 0 && load_segment != 0x7C0) 04246 LOG_MSG("El Torito boot warning: load segments other than 0x7C0 not supported yet\n"); 04247 if (sector_count != 1) 04248 LOG_MSG("El Torito boot warning: sector counts other than 1 are not supported yet\n"); 04249 04250 if (mediatype < 1 || mediatype > 3) { 04251 LOG_MSG("El Torito boot entry: media types other than floppy emulation not supported yet\n"); 04252 continue; 04253 } 04254 04255 el_torito_floppy_base = load_rba; 04256 el_torito_floppy_type = mediatype; 04257 } 04258 else { 04259 LOG_MSG("El Torito entry: ignoring bootable non-x86 (platform_id=0x%02x) record\n", header_platform); 04260 } 04261 } 04262 else { 04263 LOG_MSG("El Torito entry: ignoring unknown record ID %02x\n", entry[0]); 04264 } 04265 } 04266 } 04267 04268 if (el_torito_floppy_type == 0xFF || el_torito_floppy_base == ~0UL) { 04269 WriteOut("El Torito bootable floppy not found\n"); 04270 return false; 04271 } 04272 04273 return true; 04274 } 04275 04276 bool MountElToritoFat(const char drive, const Bitu sizes[], const char el_torito_cd_drive, const unsigned long el_torito_floppy_base, const unsigned char el_torito_floppy_type) { 04277 unsigned char driveIndex = drive - 'A'; 04278 04279 (void)sizes;//UNUSED 04280 04281 if (driveIndex > 1) { 04282 WriteOut("Invalid drive letter"); 04283 return false; 04284 } 04285 04286 if (Drives[driveIndex]) { 04287 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); 04288 return false; 04289 } 04290 04291 imageDisk * newImage = new imageDiskElToritoFloppy((unsigned char)el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type); 04292 newImage->Addref(); 04293 04294 DOS_Drive* newDrive = new fatDrive(newImage, options); 04295 newImage->Release(); //fatDrive calls Addref, and this will release newImage if fatDrive doesn't use it 04296 if (!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) { 04297 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE")); 04298 return false; 04299 } 04300 04301 AddToDriveManager(drive, newDrive, 0xF0); 04302 AttachToBiosByLetter(newImage, drive); 04303 DOS_EnableDriveMenu(drive); 04304 04305 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_ELTORITO"), drive); 04306 04307 return true; 04308 } 04309 04310 bool MountFat(Bitu sizes[], const char drive, const bool isHardDrive, const std::string &str_size, const std::vector<std::string> &paths, const signed char ide_index, const bool ide_slave) { 04311 if (Drives[drive - 'A']) { 04312 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); 04313 return false; 04314 } 04315 04316 bool imgsizedetect = isHardDrive && sizes[0] == 0; 04317 04318 std::vector<DOS_Drive*> imgDisks; 04319 std::vector<std::string>::size_type i; 04320 std::vector<DOS_Drive*>::size_type ct; 04321 04322 for (i = 0; i < paths.size(); i++) { 04323 const char* errorMessage = NULL; 04324 imageDisk* vhdImage = NULL; 04325 04326 //detect hard drive geometry 04327 if (imgsizedetect) { 04328 bool skipDetectGeometry = false; 04329 sizes[0] = 0; 04330 sizes[1] = 0; 04331 sizes[2] = 0; 04332 sizes[3] = 0; 04333 04334 /* .HDI images contain the geometry explicitly in the header. */ 04335 if (str_size.size() == 0) { 04336 const char *ext = strrchr(paths[i].c_str(), '.'); 04337 if (ext != NULL) { 04338 if (!strcasecmp(ext, ".hdi")) { 04339 skipDetectGeometry = true; 04340 } 04341 if (!strcasecmp(ext, ".nhd")) { 04342 skipDetectGeometry = true; 04343 } 04344 if (!strcasecmp(ext, ".nfd")) { 04345 skipDetectGeometry = true; 04346 } 04347 //for all vhd files where the system will autodetect the chs values, 04348 if (!strcasecmp(ext, ".vhd")) { 04349 //load the file with imageDiskVHD, which supports fixed/dynamic/differential disks 04350 imageDiskVHD::ErrorCodes ret = imageDiskVHD::Open(paths[i].c_str(), false, &vhdImage); 04351 switch (ret) { 04352 case imageDiskVHD::OPEN_SUCCESS: { 04353 //upon successful, go back to old code if using a fixed disk, which patches chs values for incorrectly identified disks 04354 skipDetectGeometry = true; 04355 const imageDiskVHD* vhdDisk = dynamic_cast<imageDiskVHD*>(vhdImage); 04356 if (vhdDisk == NULL || vhdDisk->vhdType == imageDiskVHD::VHD_TYPE_FIXED) { //fixed disks would be null here 04357 delete vhdDisk; 04358 vhdDisk = 0; 04359 skipDetectGeometry = false; 04360 } 04361 break; 04362 } 04363 case imageDiskVHD::ERROR_OPENING: 04364 errorMessage = (char*)MSG_Get("VHD_ERROR_OPENING"); break; 04365 case imageDiskVHD::INVALID_DATA: 04366 errorMessage = (char*)MSG_Get("VHD_INVALID_DATA"); break; 04367 case imageDiskVHD::UNSUPPORTED_TYPE: 04368 errorMessage = (char*)MSG_Get("VHD_UNSUPPORTED_TYPE"); break; 04369 case imageDiskVHD::ERROR_OPENING_PARENT: 04370 errorMessage = (char*)MSG_Get("VHD_ERROR_OPENING_PARENT"); break; 04371 case imageDiskVHD::PARENT_INVALID_DATA: 04372 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_DATA"); break; 04373 case imageDiskVHD::PARENT_UNSUPPORTED_TYPE: 04374 errorMessage = (char*)MSG_Get("VHD_PARENT_UNSUPPORTED_TYPE"); break; 04375 case imageDiskVHD::PARENT_INVALID_MATCH: 04376 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_MATCH"); break; 04377 case imageDiskVHD::PARENT_INVALID_DATE: 04378 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_DATE"); break; 04379 default: break; 04380 } 04381 } 04382 } 04383 } 04384 if (!skipDetectGeometry && !DetectGeometry(paths[i].c_str(), sizes)) { 04385 errorMessage = (char*)("Unable to detect geometry\n"); 04386 } 04387 } 04388 04389 if (!errorMessage) { 04390 DOS_Drive* newDrive = NULL; 04391 if (vhdImage) { 04392 newDrive = new fatDrive(vhdImage, options); 04393 vhdImage = NULL; 04394 } 04395 else { 04396 newDrive = new fatDrive(paths[i].c_str(), (Bit32u)sizes[0], (Bit32u)sizes[1], (Bit32u)sizes[2], (Bit32u)sizes[3], options); 04397 } 04398 imgDisks.push_back(newDrive); 04399 fatDrive* fdrive=dynamic_cast<fatDrive*>(newDrive); 04400 if (!fdrive->created_successfully) { 04401 errorMessage = (char*)MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"); 04402 if (fdrive->req_ver_major>0) { 04403 static char ver_msg[150]; 04404 sprintf(ver_msg, "This operation requires DOS version %u.%u or higher.\n%s", fdrive->req_ver_major, fdrive->req_ver_minor, errorMessage); 04405 errorMessage = ver_msg; 04406 } 04407 } 04408 } 04409 if (errorMessage) { 04410 WriteOut(errorMessage); 04411 for (ct = 0; ct < imgDisks.size(); ct++) { 04412 delete imgDisks[ct]; 04413 } 04414 return false; 04415 } 04416 } 04417 04418 AddToDriveManager(drive, imgDisks, isHardDrive ? 0xF8 : 0xF0); 04419 DOS_EnableDriveMenu(drive); 04420 04421 std::string tmp(wpcolon&&paths[0].length()>1&&paths[0].c_str()[0]==':'?paths[0].substr(1):paths[0]); 04422 for (i = 1; i < paths.size(); i++) { 04423 tmp += "; " + (wpcolon&&paths[i].length()>1&&paths[i].c_str()[0]==':'?paths[i].substr(1):paths[i]); 04424 } 04425 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str()); 04426 04427 if (imgDisks.size() == 1) { 04428 imageDisk* image = ((fatDrive*)imgDisks[0])->loadedDisk; 04429 AttachToBiosAndIdeByLetter(image, drive, (unsigned char)ide_index, ide_slave); 04430 } 04431 return true; 04432 } 04433 04434 imageDiskMemory* CreateRamDrive(Bitu sizes[], const int reserved_cylinders, const bool forceFloppy) { 04435 imageDiskMemory* dsk = NULL; 04436 //if chs not specified 04437 if (sizes[1] == 0) { 04438 Bit32u imgSizeK = (Bit32u)sizes[0]; 04439 //default to 1.44mb floppy 04440 if (forceFloppy && imgSizeK == 0) imgSizeK = 1440; 04441 //search for floppy geometry that matches specified size in KB 04442 int index = 0; 04443 while (DiskGeometryList[index].cylcount != 0) { 04444 if (DiskGeometryList[index].ksize == imgSizeK) { 04445 //create floppy 04446 dsk = new imageDiskMemory(DiskGeometryList[index]); 04447 break; 04448 } 04449 index++; 04450 } 04451 if (dsk == NULL) { 04452 //create hard drive 04453 if (forceFloppy) { 04454 WriteOut("Floppy size not recognized\n"); 04455 return NULL; 04456 } 04457 04458 // The fatDrive class is hard-coded to assume that disks 2880KB or smaller are floppies, 04459 // whether or not they are attached to a floppy controller. So, let's enforce a minimum 04460 // size of 4096kb for hard drives. Use the other constructor for floppy drives. 04461 // Note that a size of 0 means to auto-select a size 04462 if (imgSizeK < 4096) imgSizeK = 4096; 04463 04464 dsk = new imageDiskMemory(imgSizeK); 04465 } 04466 } 04467 else { 04468 //search for floppy geometry that matches specified geometry 04469 int index = 0; 04470 while (DiskGeometryList[index].cylcount != 0) { 04471 if (DiskGeometryList[index].cylcount == sizes[3] && 04472 DiskGeometryList[index].headscyl == sizes[2] && 04473 DiskGeometryList[index].secttrack == sizes[1] && 04474 DiskGeometryList[index].bytespersect == sizes[0]) { 04475 //create floppy 04476 dsk = new imageDiskMemory(DiskGeometryList[index]); 04477 break; 04478 } 04479 index++; 04480 } 04481 if (dsk == NULL) { 04482 //create hard drive 04483 if (forceFloppy) { 04484 WriteOut("Floppy size not recognized\n"); 04485 return NULL; 04486 } 04487 dsk = new imageDiskMemory((Bit16u)sizes[3], (Bit16u)sizes[2], (Bit16u)sizes[1], (Bit16u)sizes[0]); 04488 } 04489 } 04490 if (!dsk->active) { 04491 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE")); 04492 delete dsk; 04493 return NULL; 04494 } 04495 dsk->Set_Reserved_Cylinders((Bitu)reserved_cylinders); 04496 return dsk; 04497 } 04498 04499 imageDisk* MountImageNoneRam(Bitu sizes[], const int reserved_cylinders, const bool forceFloppy) { 04500 imageDiskMemory* dsk = CreateRamDrive(sizes, reserved_cylinders, forceFloppy); 04501 if (dsk == NULL) return NULL; 04502 //formatting might fail; just log the failure and continue 04503 Bit8u ret = dsk->Format(); 04504 if (ret != 0x00) { 04505 LOG_MSG("Warning: could not format RAM drive - error code %u\n", (unsigned int)ret); 04506 } 04507 return dsk; 04508 } 04509 04510 bool MountRam(Bitu sizes[], const char drive, const signed char ide_index, const bool ide_slave) { 04511 if (Drives[drive - 'A']) { 04512 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); 04513 return false; 04514 } 04515 04516 //by default, make a floppy disk if A: or B: is specified (still makes a hard drive if not a recognized size) 04517 imageDiskMemory* dsk = CreateRamDrive(sizes, 0, (drive - 'A') < 2 && sizes[0] == 0); 04518 if (dsk == NULL) return false; 04519 if (dsk->Format() != 0x00) { 04520 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE")); 04521 delete dsk; 04522 return false; 04523 } 04524 dsk->Addref(); 04525 DOS_Drive* newDrive = new fatDrive(dsk, options); 04526 dsk->Release(); 04527 if (!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) { 04528 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE")); 04529 delete newDrive; //this executes dsk.Release() which executes delete dsk 04530 return false; 04531 } 04532 04533 AddToDriveManager(drive, newDrive, dsk->hardDrive ? 0xF8 : 0xF0); 04534 DOS_EnableDriveMenu(drive); 04535 04536 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_RAMDRIVE"), drive); 04537 04538 AttachToBiosAndIdeByLetter(dsk, drive, (unsigned char)ide_index, ide_slave); 04539 04540 return true; 04541 } 04542 04543 bool AttachToBiosByIndex(imageDisk* image, const unsigned char bios_drive_index) { 04544 if (bios_drive_index >= MAX_DISK_IMAGES) return false; 04545 if (imageDiskList[bios_drive_index] != NULL) { 04546 /* Notify IDE ATA emulation if a drive is already there */ 04547 if (bios_drive_index >= 2) IDE_Hard_Disk_Detach(bios_drive_index); 04548 imageDiskList[bios_drive_index]->Release(); 04549 } 04550 imageDiskList[bios_drive_index] = image; 04551 imageDiskChange[bios_drive_index] = true; 04552 image->Addref(); 04553 04554 // let FDC know if we mounted a floppy 04555 if (bios_drive_index <= 1) { 04556 FDC_AssignINT13Disk(bios_drive_index); 04557 incrementFDD(); 04558 } 04559 04560 return true; 04561 } 04562 04563 bool AttachToBiosAndIdeByIndex(imageDisk* image, const unsigned char bios_drive_index, const unsigned char ide_index, const bool ide_slave) { 04564 if (!AttachToBiosByIndex(image, bios_drive_index)) return false; 04565 //if hard drive image, and if ide controller is specified 04566 if (bios_drive_index >= 2 && bios_drive_index < MAX_DISK_IMAGES) { 04567 IDE_Hard_Disk_Attach((signed char)ide_index, ide_slave, bios_drive_index); 04568 updateDPT(); 04569 } 04570 return true; 04571 } 04572 04573 bool AttachToBiosByLetter(imageDisk* image, const char drive) { 04574 if (image->hardDrive) { 04575 //for hard drives, mount hard drives at first available index 04576 for (int index = 2; index < MAX_DISK_IMAGES; index++) { 04577 if (imageDiskList[index] == NULL) { 04578 return AttachToBiosByIndex(image, index); 04579 } 04580 } 04581 } 04582 else if (IS_PC98_ARCH) { 04583 //for pc-98 machines, mount floppies at first available index 04584 for (int index = 0; index < 2; index++) { 04585 if (imageDiskList[index] == NULL) { 04586 return AttachToBiosByIndex(image, index); 04587 } 04588 } 04589 } 04590 else if ((drive - 'A') < 2) { 04591 //for PCs, mount floppies only if A: or B: is specified, and then if so, at specified index 04592 return AttachToBiosByIndex(image, drive - 'A'); 04593 } 04594 return false; 04595 } 04596 04597 bool AttachToBiosAndIdeByLetter(imageDisk* image, const char drive, const unsigned char ide_index, const bool ide_slave) { 04598 if (image->hardDrive) { 04599 //for hard drives, mount hard drives at first available index 04600 for (int index = 2; index < MAX_DISK_IMAGES; index++) { 04601 if (imageDiskList[index] == NULL) { 04602 return AttachToBiosAndIdeByIndex(image, index, ide_index, ide_slave); 04603 } 04604 } 04605 } 04606 else if (IS_PC98_ARCH) { 04607 //for pc-98 machines, mount floppies at first available index 04608 for (int index = 0; index < 2; index++) { 04609 if (imageDiskList[index] == NULL) { 04610 return AttachToBiosByIndex(image, index); 04611 } 04612 } 04613 } else if ((drive - 'A') < 2) { 04614 //for PCs, mount floppies only if A: or B: is specified, and then if so, at specified index 04615 return AttachToBiosByIndex(image, drive - 'A'); 04616 } 04617 return false; 04618 } 04619 04620 void DetachFromBios(imageDisk* image) { 04621 if (image) { 04622 for (int index = 0; index < MAX_DISK_IMAGES; index++) { 04623 if (imageDiskList[index] == image) { 04624 if (index > 1) IDE_Hard_Disk_Detach(index); 04625 imageDiskList[index]->Release(); 04626 imageDiskChange[index] = true; 04627 imageDiskList[index] = NULL; 04628 } 04629 } 04630 } 04631 } 04632 04633 void AddToDriveManager(const char drive, DOS_Drive* imgDisk, const Bit8u mediaid) { 04634 std::vector<DOS_Drive*> imgDisks = { imgDisk }; 04635 AddToDriveManager(drive, imgDisks, mediaid); 04636 } 04637 04638 void AddToDriveManager(const char drive, const std::vector<DOS_Drive*> &imgDisks, const Bit8u mediaid) { 04639 std::vector<DOS_Drive*>::size_type ct; 04640 04641 // Update DriveManager 04642 for (ct = 0; ct < imgDisks.size(); ct++) { 04643 DriveManager::AppendDisk(drive - 'A', imgDisks[ct]); 04644 } 04645 DriveManager::InitializeDrive(drive - 'A'); 04646 04647 // Set the correct media byte in the table 04648 mem_writeb(Real2Phys(dos.tables.mediaid) + ((unsigned int)drive - 'A') * dos.tables.dpb_size, mediaid); 04649 04650 /* Command uses dta so set it to our internal dta */ 04651 RealPt save_dta = dos.dta(); 04652 dos.dta(dos.tables.tempdta); 04653 04654 for (ct = 0; ct < imgDisks.size(); ct++) { 04655 DriveManager::CycleDisks(drive - 'A', (ct == (imgDisks.size() - 1))); 04656 04657 char root[7] = { drive, ':', '\\', '*', '.', '*', 0 }; 04658 DOS_FindFirst(root, DOS_ATTR_VOLUME); // force obtaining the label and saving it in dirCache 04659 } 04660 dos.dta(save_dta); 04661 04662 } 04663 04664 bool DetectGeometry(const char* fileName, Bitu sizes[]) { 04665 bool yet_detected = false, readonly = wpcolon&&strlen(fileName)>1&&fileName[0]==':'; 04666 FILE * diskfile = fopen64(readonly?fileName+1:fileName, readonly?"rb":"rb+"); 04667 if (!diskfile) { 04668 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); 04669 return false; 04670 } 04671 fseeko64(diskfile, 0L, SEEK_END); 04672 Bit32u fcsize = (Bit32u)(ftello64(diskfile) / 512L); 04673 Bit8u buf[512]; 04674 // check for vhd signature 04675 fseeko64(diskfile, -512, SEEK_CUR); 04676 if (fread(buf, sizeof(Bit8u), 512, diskfile)<512) { 04677 fclose(diskfile); 04678 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); 04679 return false; 04680 } 04681 if (!strcmp((const char*)buf, "conectix")) { 04682 fcsize--; // skip footer (512 bytes) 04683 sizes[0] = 512; // sector size 04684 sizes[1] = buf[0x3b]; // sectors 04685 sizes[2] = buf[0x3a]; // heads 04686 sizes[3] = SDL_SwapBE16((Bit16u)(*(Bit16s*)(buf + 0x38))); // cylinders 04687 04688 // Do translation (?) 04689 while ((sizes[2] < 128u) && (sizes[3] > 1023u)) { 04690 sizes[2] <<= 1u; 04691 sizes[3] >>= 1u; 04692 } 04693 04694 if (sizes[3]>1023) { 04695 // Set x/255/63 04696 sizes[2] = 255; 04697 sizes[3] = fcsize / sizes[2] / sizes[1]; 04698 } 04699 04700 LOG_MSG("VHD image detected: %u,%u,%u,%u", 04701 (unsigned int)sizes[0], (unsigned int)sizes[1], (unsigned int)sizes[2], (unsigned int)sizes[3]); 04702 if (sizes[3]>1023) LOG_MSG("WARNING: cylinders>1023, INT13 will not work unless extensions are used"); 04703 yet_detected = true; 04704 } 04705 04706 fseeko64(diskfile, 0L, SEEK_SET); 04707 if (fread(buf, sizeof(Bit8u), 512, diskfile)<512) { 04708 fclose(diskfile); 04709 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); 04710 return false; 04711 } 04712 fclose(diskfile); 04713 // check it is not dynamic VHD image 04714 if (!strcmp((const char*)buf, "conectix")) { 04715 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_DYNAMIC_VHD_UNSUPPORTED")); 04716 return false; 04717 } 04718 // check MBR signature for unknown images 04719 if (!yet_detected && ((buf[510] != 0x55) || (buf[511] != 0xaa))) { 04720 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); 04721 return false; 04722 } 04723 // check MBR partition entry 1 04724 if (!yet_detected) 04725 yet_detected = DetectMFMsectorPartition(buf, fcsize, sizes); 04726 04727 // Try bximage disk geometry 04728 // bximage flat images should already be detected by 04729 // DetectMFMSectorPartition(), not sure what this adds... 04730 if (!yet_detected) { 04731 yet_detected = DetectBximagePartition(fcsize, sizes); 04732 } 04733 04734 Bit8u ptype = buf[0x1c2]; // Location of DOS 3.3+ partition type 04735 bool assume_lba = false; 04736 04737 /* If the first partition is a Windows 95 FAT32 (LBA) type partition, and we failed to detect, 04738 * then assume LBA and make up a geometry */ 04739 if (!yet_detected && (ptype == 0x0C/*FAT32+LBA*/ || ptype == 0x0E/*FAT16+LBA*/)) { 04740 yet_detected = 1; 04741 assume_lba = true; 04742 LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT with LBA)"); 04743 } 04744 04745 /* If the MBR has only a partition table, but the part that normally contains executable 04746 * code is all zeros. To avoid false negatives, check only the first 0x20 bytes since 04747 * at boot time executable code must reside there to do something, and many of these 04748 * disk images while they ARE mostly zeros, do have some extra nonzero bytes immediately 04749 * before the partition table at 0x1BE. 04750 * 04751 * Modern FAT32 generator tools and older digital cameras will format FAT32 like this. 04752 * These tools are unlikely to support non-LBA disks. 04753 * 04754 * To avoid false positives, the partition type has to be something related to FAT */ 04755 if (!yet_detected && (ptype == 0x01 || ptype == 0x04 || ptype == 0x06 || ptype == 0x0B || ptype == 0x0C || ptype == 0x0E)) { 04756 /* buf[] still contains MBR */ 04757 unsigned int i=0; 04758 while (i < 0x20 && buf[i] == 0) i++; 04759 if (i == 0x20) { 04760 yet_detected = 1; 04761 assume_lba = true; 04762 LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT-related) and lack of executable code in the MBR"); 04763 } 04764 } 04765 04766 /* If we failed to detect, but the disk image is 4GB or larger, make up a geometry because 04767 * IDE drives by that point were pure LBA anyway and supported C/H/S for the sake of 04768 * backward compatibility anyway. fcsize is in 512-byte sectors. */ 04769 if (!yet_detected && fcsize >= ((4ull*1024ull*1024ull*1024ull)/512ull)) { 04770 yet_detected = 1; 04771 assume_lba = true; 04772 LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on size"); 04773 } 04774 04775 if (yet_detected && assume_lba) { 04776 sizes[0] = 512; 04777 sizes[1] = 63; 04778 sizes[2] = 255; 04779 { 04780 const Bitu d = sizes[1]*sizes[2]; 04781 sizes[3] = (fcsize + d - 1) / d; /* round up */ 04782 } 04783 } 04784 04785 if (yet_detected) { 04786 //"Image geometry auto detection: -size %u,%u,%u,%u\r\n", 04787 //sizes[0],sizes[1],sizes[2],sizes[3]); 04788 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_AUTODET_VALUES"), sizes[0], sizes[1], sizes[2], sizes[3]); 04789 return true; 04790 } 04791 else { 04792 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); 04793 return false; 04794 } 04795 } 04796 04797 bool DetectMFMsectorPartition(Bit8u buf[], Bit32u fcsize, Bitu sizes[]) { 04798 // This is used for plain MFM sector format as created by IMGMAKE 04799 // It tries to find the first partition. Addressing is in CHS format. 04800 /* Offset | Length | Description 04801 * +0 | 1 byte | 80 hex = active, 00 = inactive 04802 * +1 | 3 bytes | CHS of first sector in partition 04803 * +4 | 1 byte | partition type 04804 * +5 | 3 bytes | CHS of last sector in partition 04805 * +8 | 4 bytes | LBA of first sector in partition 04806 * +C | 4 bytes | Number of sectors in partition. 0 may mean, use LBA 04807 */ 04808 Bit8u starthead = 0; // start head of partition 04809 Bit8u startsect = 0; // start sector of partition 04810 Bit16u startcyl = 0; // start cylinder of partition 04811 Bit8u ptype = 0; // Partition Type 04812 Bit16u endcyl = 0; // end cylinder of partition 04813 Bit8u heads = 0; // heads in partition 04814 Bit8u sectors = 0; // sectors per track in partition 04815 Bit32u pe1_size = host_readd(&buf[0x1ca]); 04816 if ((Bit32u)host_readd(&buf[0x1fa]) != 0) { // DOS 2.0-3.21 partition table 04817 pe1_size = host_readd(&buf[0x1fa]); 04818 starthead = buf[0x1ef]; 04819 startsect = (buf[0x1f0] & 0x3fu) - 1u; 04820 startcyl = (unsigned char)buf[0x1f1] | (unsigned int)((buf[0x1f0] & 0xc0) << 2u); 04821 endcyl = (unsigned char)buf[0x1f5] | (unsigned int)((buf[0x1f4] & 0xc0) << 2u); 04822 ptype = buf[0x1f2]; 04823 heads = buf[0x1f3] + 1u; 04824 sectors = buf[0x1f4] & 0x3fu; 04825 } else if (pe1_size != 0) { // DOS 3.3+ partition table, starting at 0x1BE 04826 starthead = buf[0x1bf]; 04827 startsect = (buf[0x1c0] & 0x3fu) - 1u; 04828 startcyl = (unsigned char)buf[0x1c1] | (unsigned int)((buf[0x1c0] & 0xc0) << 2u); 04829 endcyl = (unsigned char)buf[0x1c5] | (unsigned int)((buf[0x1c4] & 0xc0) << 2u); 04830 ptype = buf[0x1c2]; 04831 heads = buf[0x1c3] + 1u; 04832 sectors = buf[0x1c4] & 0x3fu; 04833 } 04834 (void)ptype;//GCC: Set but not used. Assume it will be used someday --J.C. 04835 if (pe1_size != 0) { 04836 Bit32u part_start = startsect + sectors * starthead + 04837 startcyl * sectors * heads; 04838 Bit32u part_end = heads * sectors * endcyl; 04839 Bit32u part_len = part_end - part_start; 04840 // partition start/end sanity check 04841 // partition length should not exceed file length 04842 // real partition size can be a few cylinders less than pe1_size 04843 // if more than 1023 cylinders see if first partition fits 04844 // into 1023, else bail. 04845 if (/*(part_len<0) always false because unsigned || */(part_len > pe1_size) || (pe1_size > fcsize) || 04846 ((pe1_size - part_len) / (sectors*heads)>2u) || 04847 ((pe1_size / (heads*sectors))>1023u)) { 04848 //LOG_MSG("start(c,h,s) %u,%u,%u",startcyl,starthead,startsect); 04849 //LOG_MSG("endcyl %u heads %u sectors %u",endcyl,heads,sectors); 04850 //LOG_MSG("psize %u start %u end %u",pe1_size,part_start,part_end); 04851 } else { 04852 sizes[0] = 512; sizes[1] = sectors; 04853 sizes[2] = heads; sizes[3] = (Bit16u)(fcsize / (heads*sectors)); 04854 if (sizes[3]>1023) sizes[3] = 1023; 04855 return true; 04856 } 04857 } 04858 return false; 04859 } 04860 04861 bool DetectBximagePartition(Bit32u fcsize, Bitu sizes[]) { 04862 // Try bximage disk geometry 04863 Bit32u cylinders = fcsize / (16 * 63); 04864 // Int13 only supports up to 1023 cylinders 04865 // For mounting unknown images we could go up with the heads to 255 04866 if ((cylinders * 16 * 63 == fcsize) && (cylinders<1024)) { 04867 sizes[0] = 512; sizes[1] = 63; sizes[2] = 16; sizes[3] = cylinders; 04868 return true; 04869 } 04870 return false; 04871 } 04872 04873 bool MountIso(const char drive, const std::vector<std::string> &paths, const signed char ide_index, const bool ide_slave) { 04874 //mount cdrom 04875 if (Drives[drive - 'A']) { 04876 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); 04877 return false; 04878 } 04879 Bit8u mediaid = 0xF8; 04880 MSCDEX_SetCDInterface(CDROM_USE_SDL, -1); 04881 // create new drives for all images 04882 std::vector<DOS_Drive*> isoDisks; 04883 std::vector<std::string>::size_type i; 04884 std::vector<DOS_Drive*>::size_type ct; 04885 for (i = 0; i < paths.size(); i++) { 04886 int error = -1; 04887 DOS_Drive* newDrive = new isoDrive(drive, wpcolon&&paths[i].length()>1&&paths[i].c_str()[0]==':'?paths[i].c_str()+1:paths[i].c_str(), mediaid, error); 04888 isoDisks.push_back(newDrive); 04889 switch (error) { 04890 case 0: break; 04891 case 1: WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); break; 04892 case 2: WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED")); break; 04893 case 3: WriteOut(MSG_Get("MSCDEX_ERROR_OPEN")); break; 04894 case 4: WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES")); break; 04895 case 5: WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT")); break; 04896 case 6: WriteOut(MSG_Get("MSCDEX_INVALID_FILEFORMAT")); break; 04897 default: WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR")); break; 04898 } 04899 // error: clean up and leave 04900 if (error) { 04901 for (ct = 0; ct < isoDisks.size(); ct++) { 04902 delete isoDisks[ct]; 04903 } 04904 return false; 04905 } 04906 } 04907 // Update DriveManager 04908 for (ct = 0; ct < isoDisks.size(); ct++) { 04909 DriveManager::AppendDisk(drive - 'A', isoDisks[ct]); 04910 } 04911 DriveManager::InitializeDrive(drive - 'A'); 04912 DOS_EnableDriveMenu(drive); 04913 04914 // Set the correct media byte in the table 04915 mem_writeb(Real2Phys(dos.tables.mediaid) + ((unsigned int)drive - 'A') * dos.tables.dpb_size, mediaid); 04916 04917 // If instructed, attach to IDE controller as ATAPI CD-ROM device 04918 if (ide_index >= 0) IDE_CDROM_Attach(ide_index, ide_slave, drive - 'A'); 04919 04920 // Print status message (success) 04921 WriteOut(MSG_Get("MSCDEX_SUCCESS")); 04922 std::string tmp(wpcolon&&paths[0].length()>1&&paths[0].c_str()[0]==':'?paths[0].substr(1):paths[0]); 04923 for (i = 1; i < paths.size(); i++) { 04924 tmp += "; " + (wpcolon&&paths[i].length()>1&&paths[i].c_str()[0]==':'?paths[i].substr(1):paths[i]); 04925 } 04926 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str()); 04927 return true; 04928 } 04929 04930 imageDisk* MountImageNone(const char* fileName, const Bitu sizesOriginal[], const int reserved_cylinders) { 04931 imageDisk* newImage = 0; 04932 Bitu sizes[4]; 04933 sizes[0] = sizesOriginal[0]; 04934 sizes[1] = sizesOriginal[1]; 04935 sizes[2] = sizesOriginal[2]; 04936 sizes[3] = sizesOriginal[3]; 04937 04938 //check for VHD files 04939 if (sizes[0] == 0 /* auto detect size */) { 04940 const char *ext = strrchr(fileName, '.'); 04941 if (ext != NULL) { 04942 if (!strcasecmp(ext, ".vhd")) { 04943 imageDiskVHD::ErrorCodes ret = imageDiskVHD::Open(fileName, false, &newImage); 04944 switch (ret) { 04945 case imageDiskVHD::ERROR_OPENING: WriteOut(MSG_Get("VHD_ERROR_OPENING")); break; 04946 case imageDiskVHD::INVALID_DATA: WriteOut(MSG_Get("VHD_INVALID_DATA")); break; 04947 case imageDiskVHD::UNSUPPORTED_TYPE: WriteOut(MSG_Get("VHD_UNSUPPORTED_TYPE")); break; 04948 case imageDiskVHD::ERROR_OPENING_PARENT: WriteOut(MSG_Get("VHD_ERROR_OPENING_PARENT")); break; 04949 case imageDiskVHD::PARENT_INVALID_DATA: WriteOut(MSG_Get("VHD_PARENT_INVALID_DATA")); break; 04950 case imageDiskVHD::PARENT_UNSUPPORTED_TYPE: WriteOut(MSG_Get("VHD_PARENT_UNSUPPORTED_TYPE")); break; 04951 case imageDiskVHD::PARENT_INVALID_MATCH: WriteOut(MSG_Get("VHD_PARENT_INVALID_MATCH")); break; 04952 case imageDiskVHD::PARENT_INVALID_DATE: WriteOut(MSG_Get("VHD_PARENT_INVALID_DATE")); break; 04953 default: break; 04954 } 04955 return newImage; 04956 } 04957 } 04958 } 04959 04960 Bit32u imagesize; 04961 /* auto-fill: sector size */ 04962 if (sizes[0] == 0) sizes[0] = 512; 04963 04964 bool readonly = wpcolon&&strlen(fileName)>1&&fileName[0]==':'; 04965 const char* fname=readonly?fileName+1:fileName; 04966 FILE *newDisk = fopen64(fname, readonly?"rb":"rb+"); 04967 if (!newDisk) { 04968 WriteOut("Unable to open '%s'\n", fname); 04969 return NULL; 04970 } 04971 04972 QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(newDisk); 04973 04974 Bit64u sectors; 04975 if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)) { 04976 Bit32u cluster_size = 1u << qcow2_header.cluster_bits; 04977 if ((sizes[0] < 512) || ((cluster_size % sizes[0]) != 0)) { 04978 WriteOut("Sector size must be larger than 512 bytes and evenly divide the image cluster size of %lu bytes.\n", cluster_size); 04979 return 0; 04980 } 04981 sectors = (Bit64u)qcow2_header.size / (Bit64u)sizes[0]; 04982 imagesize = (Bit32u)(qcow2_header.size / 1024L); 04983 setbuf(newDisk, NULL); 04984 newImage = new QCow2Disk(qcow2_header, newDisk, (Bit8u *)fname, imagesize, (Bit32u)sizes[0], (imagesize > 2880)); 04985 } 04986 else { 04987 char tmp[256]; 04988 04989 fseeko64(newDisk, 0L, SEEK_SET); 04990 size_t readResult = fread(tmp, 256, 1, newDisk); // look for magic signatures 04991 if (readResult != 1) { 04992 LOG(LOG_IO, LOG_ERROR) ("Reading error in MountImageNone\n"); 04993 return NULL; 04994 } 04995 04996 const char *ext = strrchr(fname,'.'); 04997 04998 if (ext != NULL && !strcasecmp(ext, ".d88")) { 04999 fseeko64(newDisk, 0L, SEEK_END); 05000 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0]; 05001 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */ 05002 setbuf(newDisk, NULL); 05003 newImage = new imageDiskD88(newDisk, (Bit8u *)fname, imagesize, (imagesize > 2880)); 05004 } 05005 else if (!memcmp(tmp, "VFD1.", 5)) { /* FDD files */ 05006 fseeko64(newDisk, 0L, SEEK_END); 05007 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0]; 05008 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */ 05009 setbuf(newDisk, NULL); 05010 newImage = new imageDiskVFD(newDisk, (Bit8u *)fname, imagesize, (imagesize > 2880)); 05011 } 05012 else if (!memcmp(tmp,"T98FDDIMAGE.R0\0\0",16)) { 05013 fseeko64(newDisk, 0L, SEEK_END); 05014 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0]; 05015 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */ 05016 setbuf(newDisk, NULL); 05017 newImage = new imageDiskNFD(newDisk, (Bit8u *)fname, imagesize, (imagesize > 2880), 0); 05018 } 05019 else if (!memcmp(tmp,"T98FDDIMAGE.R1\0\0",16)) { 05020 fseeko64(newDisk, 0L, SEEK_END); 05021 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0]; 05022 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */ 05023 setbuf(newDisk, NULL); 05024 newImage = new imageDiskNFD(newDisk, (Bit8u *)fname, imagesize, (imagesize > 2880), 1); 05025 } 05026 else { 05027 fseeko64(newDisk, 0L, SEEK_END); 05028 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0]; 05029 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */ 05030 setbuf(newDisk, NULL); 05031 newImage = new imageDisk(newDisk, (Bit8u *)fname, imagesize, (imagesize > 2880)); 05032 } 05033 } 05034 05035 /* sometimes imageDisk is able to determine geometry automatically (HDI images) */ 05036 if (newImage) { 05037 if (newImage->sectors != 0 && newImage->heads != 0 && newImage->cylinders != 0 && newImage->sector_size != 0) { 05038 /* prevent the code below from changing the geometry */ 05039 sizes[0] = newImage->sector_size; 05040 sizes[1] = newImage->sectors; 05041 sizes[2] = newImage->heads; 05042 sizes[3] = newImage->cylinders; 05043 } 05044 } 05045 05046 /* try auto-detect */ 05047 if (sizes[3] == 0 && sizes[2] == 0) { 05048 DetectGeometry(fname, sizes); /* NTS: Opens the file again, even though WE have the file open already! */ 05049 } 05050 05051 /* auto-fill: sector/track count */ 05052 if (sizes[1] == 0) sizes[1] = 63; 05053 /* auto-fill: head/cylinder count */ 05054 if (sizes[3]/*cylinders*/ == 0 && sizes[2]/*heads*/ == 0) { 05055 sizes[2] = 16; /* typical hard drive, unless a very old drive */ 05056 sizes[3]/*cylinders*/ = (Bitu)((Bit64u)sectors / (Bit64u)sizes[2]/*heads*/ / (Bit64u)sizes[1]/*sectors/track*/); 05057 05058 if (IS_PC98_ARCH) { 05059 /* TODO: PC-98 has it's own issues with a 4096-cylinder limit */ 05060 } 05061 else { 05062 /* INT 13h mapping, deal with 1024-cyl limit */ 05063 while (sizes[3] > 1024) { 05064 if (sizes[2] >= 255) break; /* nothing more we can do */ 05065 05066 /* try to generate head count 16, 32, 64, 128, 255 */ 05067 sizes[2]/*heads*/ *= 2; 05068 if (sizes[2] >= 256) sizes[2] = 255; 05069 05070 /* and recompute cylinders */ 05071 sizes[3]/*cylinders*/ = (Bitu)((Bit64u)sectors / (Bit64u)sizes[2]/*heads*/ / (Bit64u)sizes[1]/*sectors/track*/); 05072 } 05073 } 05074 } 05075 05076 LOG(LOG_MISC, LOG_NORMAL)("Mounting image as C/H/S %u/%u/%u with %u bytes/sector", 05077 (unsigned int)sizes[3], (unsigned int)sizes[2], (unsigned int)sizes[1], (unsigned int)sizes[0]); 05078 05079 if (imagesize > 2880) newImage->Set_Geometry((Bit32u)sizes[2], (Bit32u)sizes[3], (Bit32u)sizes[1], (Bit32u)sizes[0]); 05080 if (reserved_cylinders > 0) newImage->Set_Reserved_Cylinders((Bitu)reserved_cylinders); 05081 05082 return newImage; 05083 } 05084 }; 05085 05086 void IMGMOUNT_ProgramStart(Program * * make) { 05087 *make=new IMGMOUNT; 05088 } 05089 05090 Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp); 05091 Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile); 05092 const char* DOS_GetLoadedLayout(void); 05093 05094 class KEYB : public Program { 05095 public: 05096 void Run(void); 05097 }; 05098 05099 void KEYB::Run(void) { 05100 // codepage 949 start 05101 std::string temp_codepage; 05102 temp_codepage="949"; 05103 if (cmd->FindString("ko",temp_codepage,false)) { 05104 dos.loaded_codepage=949; 05105 const char* layout_name = DOS_GetLoadedLayout(); 05106 WriteOut(MSG_Get("PROGRAM_KEYB_INFO_LAYOUT"),dos.loaded_codepage,layout_name); 05107 return; 05108 } 05109 // codepage 949 end 05110 if (cmd->FindCommand(1,temp_line)) { 05111 if (cmd->FindString("?",temp_line,false)) { 05112 WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP")); 05113 } else { 05114 /* first parameter is layout ID */ 05115 Bitu keyb_error=0; 05116 std::string cp_string; 05117 Bit32s tried_cp = -1; 05118 if (cmd->FindCommand(2,cp_string)) { 05119 /* second parameter is codepage number */ 05120 tried_cp=atoi(cp_string.c_str()); 05121 char cp_file_name[256]; 05122 if (cmd->FindCommand(3,cp_string)) { 05123 /* third parameter is codepage file */ 05124 strcpy(cp_file_name, cp_string.c_str()); 05125 } else { 05126 /* no codepage file specified, use automatic selection */ 05127 strcpy(cp_file_name, "auto"); 05128 } 05129 05130 keyb_error=DOS_LoadKeyboardLayout(temp_line.c_str(), tried_cp, cp_file_name); 05131 } else { 05132 keyb_error=DOS_SwitchKeyboardLayout(temp_line.c_str(), tried_cp); 05133 } 05134 switch (keyb_error) { 05135 case KEYB_NOERROR: 05136 WriteOut(MSG_Get("PROGRAM_KEYB_NOERROR"),temp_line.c_str(),dos.loaded_codepage); 05137 break; 05138 case KEYB_FILENOTFOUND: 05139 if (temp_line!="/?"&&temp_line!="-?") WriteOut(MSG_Get("PROGRAM_KEYB_FILENOTFOUND"),temp_line.c_str()); 05140 WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP")); 05141 break; 05142 case KEYB_INVALIDFILE: 05143 WriteOut(MSG_Get("PROGRAM_KEYB_INVALIDFILE"),temp_line.c_str()); 05144 break; 05145 case KEYB_LAYOUTNOTFOUND: 05146 WriteOut(MSG_Get("PROGRAM_KEYB_LAYOUTNOTFOUND"),temp_line.c_str(),tried_cp); 05147 break; 05148 case KEYB_INVALIDCPFILE: 05149 WriteOut(MSG_Get("PROGRAM_KEYB_INVCPFILE"),temp_line.c_str()); 05150 WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP")); 05151 break; 05152 default: 05153 LOG(LOG_DOSMISC,LOG_ERROR)("KEYB:Invalid returncode %x",(int)keyb_error); 05154 break; 05155 } 05156 } 05157 } else { 05158 /* no parameter in the command line, just output codepage info and possibly loaded layout ID */ 05159 const char* layout_name = DOS_GetLoadedLayout(); 05160 if (layout_name==NULL) { 05161 WriteOut(MSG_Get("PROGRAM_KEYB_INFO"),dos.loaded_codepage); 05162 } else { 05163 WriteOut(MSG_Get("PROGRAM_KEYB_INFO_LAYOUT"),dos.loaded_codepage,layout_name); 05164 } 05165 } 05166 } 05167 05168 static void KEYB_ProgramStart(Program * * make) { 05169 *make=new KEYB; 05170 } 05171 05172 // MODE 05173 05174 class MODE : public Program { 05175 public: 05176 void Run(void); 05177 }; 05178 05179 void MODE::Run(void) { 05180 Bit16u rate=0,delay=0,mode; 05181 if (!cmd->FindCommand(1,temp_line) || temp_line=="/?") { 05182 WriteOut(MSG_Get("PROGRAM_MODE_USAGE")); 05183 return; 05184 } 05185 else if (strcasecmp(temp_line.c_str(),"con")==0 || strcasecmp(temp_line.c_str(),"con:")==0) { 05186 if (cmd->GetCount()!=3) goto modeparam; 05187 if (cmd->FindStringBegin("rate=", temp_line,false)) rate= atoi(temp_line.c_str()); 05188 if (cmd->FindStringBegin("delay=",temp_line,false)) delay=atoi(temp_line.c_str()); 05189 if (rate<1 || rate>32 || delay<1 || delay>4) goto modeparam; 05190 IO_Write(0x60,0xf3); IO_Write(0x60,(Bit8u)(((delay-1)<<5)|(32-rate))); 05191 return; 05192 } 05193 else if (cmd->GetCount()>1) goto modeparam; 05194 else if (strcasecmp(temp_line.c_str(),"mono")==0) mode=7; 05195 else if (machine==MCH_HERC || machine==MCH_MDA) goto modeparam; 05196 else if (strcasecmp(temp_line.c_str(),"co80")==0) mode=3; 05197 else if (strcasecmp(temp_line.c_str(),"bw80")==0) mode=2; 05198 else if (strcasecmp(temp_line.c_str(),"co40")==0) mode=1; 05199 else if (strcasecmp(temp_line.c_str(),"bw40")==0) mode=0; 05200 else goto modeparam; 05201 mem_writeb(BIOS_CONFIGURATION,(mem_readb(BIOS_CONFIGURATION)&0xcf)|((mode==7)?0x30:0x20)); 05202 reg_ax=mode; 05203 CALLBACK_RunRealInt(0x10); 05204 return; 05205 modeparam: 05206 WriteOut(MSG_Get("PROGRAM_MODE_INVALID_PARAMETERS")); 05207 return; 05208 } 05209 05210 static void MODE_ProgramStart(Program * * make) { 05211 *make=new MODE; 05212 } 05213 /* 05214 // MORE 05215 class MORE : public Program { 05216 public: 05217 void Run(void); 05218 }; 05219 05220 void MORE::Run(void) { 05221 if (cmd->GetCount()) { 05222 WriteOut(MSG_Get("PROGRAM_MORE_USAGE")); 05223 return; 05224 } 05225 Bit16u ncols=mem_readw(BIOS_SCREEN_COLUMNS); 05226 Bit16u nrows=(Bit16u)mem_readb(BIOS_ROWS_ON_SCREEN_MINUS_1); 05227 Bit16u col=1,row=1; 05228 Bit8u c;Bit16u n=1; 05229 WriteOut("\n"); 05230 while (n) { 05231 DOS_ReadFile(STDIN,&c,&n); 05232 if (n==0 || c==0x1a) break; // stop at EOF 05233 switch (c) { 05234 case 0x07: break; 05235 case 0x08: if (col>1) col--; break; 05236 case 0x09: col=((col+7)&~7)+1; break; 05237 case 0x0a: row++; break; 05238 case 0x0d: col=1; break; 05239 default: col++; break; 05240 } 05241 if (col>ncols) {col=1;row++;} 05242 DOS_WriteFile(STDOUT,&c,&n); 05243 if (row>=nrows) { 05244 WriteOut(MSG_Get("PROGRAM_MORE_MORE")); 05245 DOS_ReadFile(STDERR,&c,&n); 05246 if (c==0) DOS_ReadFile(STDERR,&c,&n); // read extended key 05247 WriteOut("\n\n"); 05248 col=row=1; 05249 } 05250 } 05251 } 05252 05253 static void MORE_ProgramStart(Program * * make) { 05254 *make=new MORE; 05255 } 05256 */ 05257 05258 void REDOS_ProgramStart(Program * * make); 05259 void A20GATE_ProgramStart(Program * * make); 05260 void PC98UTIL_ProgramStart(Program * * make); 05261 void VESAMOED_ProgramStart(Program * * make); 05262 05263 #if defined C_DEBUG 05264 class NMITEST : public Program { 05265 public: 05266 void Run(void) { 05267 if (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)) { 05268 WriteOut("Generates a non-maskable interrupt (NMI).\n\nNMITEST\n\nNote: This is a debugging tool to test if the interrupt handler works properly.\n"); 05269 return; 05270 } 05271 CPU_Raise_NMI(); 05272 } 05273 }; 05274 05275 static void NMITEST_ProgramStart(Program * * make) { 05276 *make=new NMITEST; 05277 } 05278 #endif 05279 05280 class CAPMOUSE : public Program 05281 { 05282 public: 05283 void Run() override 05284 { 05285 auto val = 0; 05286 auto tmp = std::string(""); 05287 05288 if(cmd->GetCount() == 0 || cmd->FindExist("/?", true)) 05289 val = 0; 05290 else if(cmd->FindExist("/C", false)) 05291 val = 1; 05292 else if(cmd->FindExist("/R", false)) 05293 val = 2; 05294 05295 auto cap = false; 05296 switch(val) 05297 { 05298 case 2: 05299 break; 05300 case 1: 05301 cap = true; 05302 break; 05303 case 0: 05304 default: 05305 WriteOut("Captures or releases the mouse inside DOSBox-X.\n\n"); 05306 WriteOut("CAPMOUSE [/C|/R]\n"); 05307 WriteOut(" /C Capture the mouse\n"); 05308 WriteOut(" /R Release the mouse\n"); 05309 return; 05310 } 05311 05312 CaptureMouseNotify(!cap); 05313 GFX_CaptureMouse(cap); 05314 std::string msg; 05315 msg.append("Mouse "); 05316 msg.append(Mouse_IsLocked() ? "captured" : "released"); 05317 msg.append("\n"); 05318 WriteOut(msg.c_str()); 05319 } 05320 }; 05321 05322 void CAPMOUSE_ProgramStart(Program** make) 05323 { 05324 *make = new CAPMOUSE; 05325 } 05326 05327 class LABEL : public Program 05328 { 05329 public: 05330 void Help() { 05331 WriteOut("Creates, changes, or deletes the volume label of a drive.\n\nLABEL [drive:][label]\n\n [drive:]\tSpecifies the drive letter\n [label]\tSpecifies the volume label\n"); 05332 } 05333 void Run() override 05334 { 05335 /* MS-DOS behavior: If no label provided at the command line, prompt for one. 05336 * 05337 * LABEL [drive:] [label] 05338 * 05339 * No options are supported in MS-DOS, and the label can have spaces in it. 05340 * This is valid, apparently: 05341 * 05342 * LABEL H E L L O 05343 * 05344 * Will set the volume label to "H E L L O" 05345 * 05346 * Label /? will print help. 05347 */ 05348 std::string label; 05349 Bit8u drive = DOS_GetDefaultDrive(); 05350 const char *raw = cmd->GetRawCmdline().c_str(); 05351 05352 /* skip space */ 05353 while (*raw == ' ') raw++; 05354 05355 /* options */ 05356 if (raw[0] == '/') { 05357 raw++; 05358 if (raw[0] == '?') { 05359 Help(); 05360 return; 05361 } 05362 } 05363 05364 /* is the next part a drive letter? */ 05365 if (raw[0] != 0 && raw[1] != 0) { 05366 if (isalpha(raw[0]) && raw[1] == ':') { 05367 drive = tolower(raw[0]) - 'a'; 05368 raw += 2; 05369 while (*raw == ' ') raw++; 05370 } 05371 } 05372 05373 /* then the label. MS-DOS behavior is to treat the rest of the command line, spaces and all, as the label */ 05374 if (*raw != 0) { 05375 label = raw; 05376 } 05377 05378 /* if the label is longer than 11 chars or contains a dot, MS-DOS will reject it and then prompt for another label */ 05379 if (label.length() > 11) { 05380 WriteOut("Label is too long (more than 11 chars)\n"); 05381 label.clear(); 05382 } 05383 else if (label.find_first_of(".:/\\") != std::string::npos) { 05384 WriteOut("Label has invalid chars.\n"); 05385 label.clear(); 05386 } 05387 05388 /* if no label provided, MS-DOS will display the current label and serial number and prompt the user to type in a new label. */ 05389 if (label.empty()) { 05390 std::string clabel = Drives[drive]->GetLabel(); 05391 05392 if (!clabel.empty()) 05393 WriteOut("Volume in drive %c is %s\n",drive+'A',clabel.c_str()); 05394 else 05395 WriteOut("Volume in drive %c has no label\n",drive+'A'); 05396 } 05397 05398 /* If no label is provided, MS-DOS will prompt the user whether to delete the label. */ 05399 if (label.empty()) { 05400 Bit8u c,ans=0; 05401 Bit16u s; 05402 05403 do { 05404 WriteOut("Delete the volume label (Y/N)? "); 05405 s = 1; 05406 DOS_ReadFile(STDIN,&c,&s); 05407 WriteOut("\n"); 05408 if (s != 1) return; 05409 ans = Bit8u(tolower(char(c))); 05410 } while (!(ans == 'y' || ans == 'n')); 05411 05412 if (ans != 'y') return; 05413 } 05414 05415 /* delete then create the label */ 05416 Drives[drive]->SetLabel("",false,true); 05417 Drives[drive]->SetLabel(label.c_str(),false,true); 05418 } 05419 }; 05420 05421 void LABEL_ProgramStart(Program** make) 05422 { 05423 *make = new LABEL; 05424 } 05425 05426 std::vector<std::string> MAPPER_GetEventNames(const std::string &prefix); 05427 void MAPPER_AutoType(std::vector<std::string> &sequence, const uint32_t wait_ms, const uint32_t pacing_ms); 05428 05429 class AUTOTYPE : public Program { 05430 public: 05431 void Run(); 05432 05433 private: 05434 void PrintUsage(); 05435 void PrintKeys(); 05436 bool ReadDoubleArg(const std::string &name, 05437 const char *flag, 05438 const double &def_value, 05439 const double &min_value, 05440 const double &max_value, 05441 double &value); 05442 }; 05443 05444 void AUTOTYPE_ProgramStart(Program **make); 05445 05446 void AUTOTYPE::PrintUsage() 05447 { 05448 constexpr const char *msg = 05449 "Performs scripted keyboard entry into a running DOS program.\n\n" 05450 "\033[32;1mAUTOTYPE\033[0m [-list] [-w WAIT] [-p PACE] " 05451 "button_1 [button_2 [...]] \n\n" 05452 "Where:\n" 05453 " -list: prints all available button names.\n" 05454 " -w WAIT: seconds before typing begins. Two second default; " 05455 "max of 30.\n" 05456 " -p PACE: seconds between each keystroke. Half-second " 05457 "default; max of 10.\n" 05458 "\n" 05459 " The sequence is comprised of one or more space-separated " 05460 "buttons.\n" 05461 " Autotyping begins after WAIT seconds, and each button is " 05462 "entered \n" 05463 " every PACE seconds. The , character inserts an extra PACE " 05464 "delay.\n" 05465 "\n" 05466 "Some examples:\n" 05467 " \033[32;1mAUTOTYPE\033[0m -w 1 -p 0.3 up enter , right " 05468 "enter\n" 05469 " \033[32;1mAUTOTYPE\033[0m -p 0.2 f1 kp_8 , , enter\n" 05470 " \033[32;1mAUTOTYPE\033[0m -w 1.3 esc enter , p l a y e r " 05471 "enter\n"; 05472 WriteOut_NoParsing(msg); 05473 } 05474 05475 // Prints the key-names for the mapper's currently-bound events. 05476 void AUTOTYPE::PrintKeys() 05477 { 05478 const std::vector<std::string> names = MAPPER_GetEventNames("key_"); 05479 05480 // Keep track of the longest key name 05481 size_t max_length = 0; 05482 for (const auto &name : names) 05483 max_length = (std::max)(name.length(), max_length); 05484 05485 // Sanity check to avoid dividing by 0 05486 if (!max_length) { 05487 WriteOut_NoParsing( 05488 "AUTOTYPE: The mapper has no key bindings\n"); 05489 return; 05490 } 05491 05492 // Setup our rows and columns 05493 const size_t wrap_width = 72; // confortable columns not pushed to the edge 05494 const size_t columns = wrap_width / max_length; 05495 const size_t rows = ceil_udivide(names.size(), columns); 05496 05497 // Build the string output by rows and columns 05498 auto name = names.begin(); 05499 for (size_t row = 0; row < rows; ++row) { 05500 for (size_t i = row; i < names.size(); i += rows) 05501 WriteOut(" %-*s", static_cast<int>(max_length), name[i].c_str()); 05502 WriteOut_NoParsing("\n"); 05503 } 05504 } 05505 05506 /* 05507 * Converts a string to a finite number (such as float or double). 05508 * Returns the number or quiet_NaN, if it could not be parsed. 05509 * This function does not attemp to capture exceptions that may 05510 * be thrown from std::stod(...) 05511 */ 05512 template<typename T> 05513 T to_finite(const std::string& input) { 05514 // Defensively set NaN from the get-go 05515 T result = std::numeric_limits<T>::quiet_NaN(); 05516 size_t bytes_read = 0; 05517 try { 05518 const double interim = std::stod(input, &bytes_read); 05519 if (!input.empty() && bytes_read == input.size()) 05520 result = static_cast<T>(interim); 05521 } 05522 // Capture expected exceptions stod may throw 05523 catch (std::invalid_argument& e) { (void)e; } 05524 catch (std::out_of_range& e) { (void)e; } 05525 return result; 05526 } 05527 05528 /* 05529 * Reads a floating point argument from command line, where: 05530 * - name is a human description for the flag, ie: DELAY 05531 * - flag is the command-line flag, ie: -d or -delay 05532 * - default is the default value if the flag doesn't exist 05533 * - value will be populated with the default or provided value 05534 * 05535 * Returns: 05536 * true if 'value' is set to the default or read from the arg. 05537 * false if the argument was used but could not be parsed. 05538 */ 05539 bool AUTOTYPE::ReadDoubleArg(const std::string &name, 05540 const char *flag, 05541 const double &def_value, 05542 const double &min_value, 05543 const double &max_value, 05544 double &value) 05545 { 05546 bool result = false; 05547 std::string str_value; 05548 05549 // Is the user trying to set this flag? 05550 if (cmd->FindString(flag, str_value, true)) { 05551 // Can the user's value be parsed? 05552 const double user_value = to_finite<double>(str_value); 05553 #if defined(MACOSX) 05554 if (isfinite(user_value)) { /* *sigh* Really, clang, really? */ 05555 #else 05556 if (std::isfinite(user_value)) { 05557 #endif 05558 result = true; 05559 05560 // Clamp the user's value if needed 05561 value = clamp(user_value, min_value, max_value); 05562 05563 // Inform them if we had to clamp their value 05564 if (fabs(user_value - value) > 05565 std::numeric_limits<double>::epsilon()) 05566 WriteOut("AUTOTYPE: bounding %s value of %.2f " 05567 "to %.2f\n", 05568 name.c_str(), user_value, value); 05569 05570 } else { // Otherwise we couldn't parse their value 05571 WriteOut("AUTOTYPE: %s value '%s' is not a valid " 05572 "floating point number\n", 05573 name.c_str(), str_value.c_str()); 05574 } 05575 } else { // Otherwise thay haven't passed this flag 05576 value = def_value; 05577 result = true; 05578 } 05579 return result; 05580 } 05581 05582 void AUTOTYPE::Run() 05583 { 05584 // Hack To allow long commandlines 05585 ChangeToLongCmd(); 05586 05587 // Usage 05588 if (!cmd->GetCount()||(cmd->GetCount()==1 && (cmd->FindExist("-?", false) || cmd->FindExist("/?", false)))) { 05589 PrintUsage(); 05590 return; 05591 } 05592 05593 // Print available keys 05594 if (cmd->FindExist("-list", false)) { 05595 PrintKeys(); 05596 return; 05597 } 05598 05599 // Get the wait delay in milliseconds 05600 double wait_s; 05601 constexpr double def_wait_s = 2.0; 05602 constexpr double min_wait_s = 0.0; 05603 constexpr double max_wait_s = 30.0; 05604 if (!ReadDoubleArg("WAIT", "-w", def_wait_s, min_wait_s, max_wait_s, wait_s)) 05605 return; 05606 const auto wait_ms = static_cast<uint32_t>(wait_s * 1000); 05607 05608 // Get the inter-key pacing in milliseconds 05609 double pace_s; 05610 constexpr double def_pace_s = 0.5; 05611 constexpr double min_pace_s = 0.0; 05612 constexpr double max_pace_s = 10.0; 05613 if (!ReadDoubleArg("PACE", "-p", def_pace_s, min_pace_s, max_pace_s, pace_s)) 05614 return; 05615 const auto pace_ms = static_cast<uint32_t>(pace_s * 1000); 05616 05617 // Get the button sequence 05618 std::vector<std::string> sequence; 05619 cmd->FillVector(sequence); 05620 if (sequence.empty()) { 05621 WriteOut_NoParsing("AUTOTYPE: button sequence is empty\n"); 05622 return; 05623 } 05624 MAPPER_AutoType(sequence, wait_ms, pace_ms); 05625 } 05626 05627 void AUTOTYPE_ProgramStart(Program **make) 05628 { 05629 *make = new AUTOTYPE; 05630 } 05631 05632 void DOS_SetupPrograms(void) { 05633 /*Add Messages */ 05634 05635 MSG_Add("PROGRAM_MOUSE_INSTALL","Installed at PS/2 port.\n"); 05636 MSG_Add("PROGRAM_MOUSE_VERTICAL","Reverse Y-axis enabled.\n"); 05637 MSG_Add("PROGRAM_MOUSE_VERTICAL_BACK","Reverse Y-axis disabled.\n"); 05638 MSG_Add("PROGRAM_MOUSE_UNINSTALL","Driver successfully unloaded...\n"); 05639 MSG_Add("PROGRAM_MOUSE_ERROR","Already installed at PS/2 port.\n"); 05640 MSG_Add("PROGRAM_MOUSE_NOINSTALLED","Driver is not installed.\n"); 05641 MSG_Add("PROGRAM_MOUSE_HELP","Turns on/off mouse.\n\nMOUSE [/?] [/U] [/V]\n /U: Uninstall\n /V: Reverse Y-axis\n"); 05642 MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n"); 05643 MSG_Add("PROGRAM_MOUNT_STATUS_FORMAT","%-5s %-58s %-12s\n"); 05644 MSG_Add("PROGRAM_MOUNT_STATUS_NUMBER_FORMAT","%-12s %-50s\n"); 05645 MSG_Add("PROGRAM_MOUNT_STATUS_ELTORITO", "Drive %c is mounted as El Torito floppy drive\n"); 05646 MSG_Add("PROGRAM_MOUNT_STATUS_RAMDRIVE", "Drive %c is mounted as RAM drive\n"); 05647 MSG_Add("PROGRAM_MOUNT_STATUS_2","Drive %c is mounted as %s\n"); 05648 MSG_Add("PROGRAM_MOUNT_STATUS_1","The currently mounted drives are:\n"); 05649 MSG_Add("PROGRAM_IMGMOUNT_STATUS_2","The currently mounted drive numbers are:\n"); 05650 MSG_Add("PROGRAM_IMGMOUNT_STATUS_1","The currently mounted FAT/ISO drives are:\n"); 05651 MSG_Add("PROGRAM_MOUNT_ERROR_1","Directory %s doesn't exist.\n"); 05652 MSG_Add("PROGRAM_MOUNT_ERROR_2","%s is not a directory\n"); 05653 MSG_Add("PROGRAM_MOUNT_ILL_TYPE","Illegal type %s\n"); 05654 MSG_Add("PROGRAM_MOUNT_ALREADY_MOUNTED","Drive %c already mounted with %s\n"); 05655 MSG_Add("PROGRAM_MOUNT_USAGE", 05656 "Mounts drives from directories or drives in the host system.\n\n" 05657 "Usage: \033[34;1mMOUNT [option] Drive-Letter Local-Directory\033[0m\n\n" 05658 "For example: MOUNT c %s\n" 05659 "This makes the directory %s act as the C: drive inside DOSBox-X.\n" 05660 "The directory has to exist in the host system.\n\n" 05661 "Options are accepted. For example:\n" 05662 "MOUNT -nocachedir c %s mounts C: without caching the drive.\n" 05663 "MOUNT -freesize 128 c %s mounts C: with the specified free disk space.\n" 05664 "MOUNT -ro c %s mounts the C: drive in read-only mode.\n" 05665 "MOUNT -t cdrom c %s mounts the C: drive as a CD-ROM drive.\n" 05666 "MOUNT -u c unmounts the C: drive.\n\n" 05667 "Type MOUNT with no parameters to display a list of mounted drives.\n"); 05668 MSG_Add("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED","Drive %c is not mounted.\n"); 05669 MSG_Add("PROGRAM_MOUNT_UMOUNT_SUCCESS","Drive %c has successfully been removed.\n"); 05670 MSG_Add("PROGRAM_MOUNT_UMOUNT_NUMBER_SUCCESS","Drive number %c has successfully been removed.\n"); 05671 MSG_Add("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL","Virtual Drives can not be unMOUNTed.\n"); 05672 MSG_Add("PROGRAM_MOUNT_WARNING_WIN","\033[31;1mMounting C:\\ is NOT recommended. Please mount a (sub)directory next time.\033[0m\n"); 05673 MSG_Add("PROGRAM_MOUNT_WARNING_OTHER","\033[31;1mMounting / is NOT recommended. Please mount a (sub)directory next time.\033[0m\n"); 05674 MSG_Add("PROGRAM_MOUNT_OVERLAY_NO_BASE","Please MOUNT a normal directory first before adding an overlay on top.\n"); 05675 MSG_Add("PROGRAM_MOUNT_OVERLAY_INCOMPAT_BASE","The overlay is NOT compatible with the drive that is specified.\n"); 05676 MSG_Add("PROGRAM_MOUNT_OVERLAY_STATUS","Overlay %s on drive %c mounted.\n"); 05677 05678 MSG_Add("PROGRAM_LOADFIX_ALLOC","%d kb allocated.\n"); 05679 MSG_Add("PROGRAM_LOADFIX_DEALLOC","%d kb freed.\n"); 05680 MSG_Add("PROGRAM_LOADFIX_DEALLOCALL","Used memory freed.\n"); 05681 MSG_Add("PROGRAM_LOADFIX_ERROR","Memory allocation error.\n"); 05682 MSG_Add("PROGRAM_LOADFIX_HELP", 05683 "Reduces the amount of available conventional or XMS memory.\n\n" 05684 "LOADFIX [-xms] [-{ram}] [{program}] [{options}]\n" 05685 "LOADFIX -f [-xms]\n\n" 05686 " -xms Allocates memory from XMS rather than conventional memory\n" 05687 " -{ram} Specifies the amount of memory to allocate in KB\n" 05688 " Defaults to 64kb for conventional memory; 1MB for XMS memory\n" 05689 " -f Frees previously allocated memory\n" 05690 " {program} Runs the specified program\n" 05691 " {options} Program options (if any)\n\n" 05692 "Examples:\n" 05693 " LOADFIX game.exe Allocates 64KB of conventional memory and runs game.exe\n" 05694 " LOADFIX -128 Allocates 128KB of conventional memory\n" 05695 " LOADFIX -xms Allocates 1MB of XMS memory\n" 05696 " LOADFIX -f Frees allocated conventional memory\n"); 05697 05698 MSG_Add("MSCDEX_SUCCESS","MSCDEX installed.\n"); 05699 MSG_Add("MSCDEX_ERROR_MULTIPLE_CDROMS","MSCDEX: Failure: Drive-letters of multiple CD-ROM drives have to be continuous.\n"); 05700 MSG_Add("MSCDEX_ERROR_NOT_SUPPORTED","MSCDEX: Failure: Not yet supported.\n"); 05701 MSG_Add("MSCDEX_ERROR_PATH","MSCDEX: Specified location is not a CD-ROM drive.\n"); 05702 MSG_Add("MSCDEX_ERROR_OPEN","MSCDEX: Failure: Invalid file or unable to open.\n"); 05703 MSG_Add("MSCDEX_TOO_MANY_DRIVES","MSCDEX: Failure: Too many CD-ROM drives (max: 5). MSCDEX Installation failed.\n"); 05704 MSG_Add("MSCDEX_LIMITED_SUPPORT","MSCDEX: Mounted subdirectory: limited support.\n"); 05705 MSG_Add("MSCDEX_INVALID_FILEFORMAT","MSCDEX: Failure: File is either no ISO/CUE image or contains errors.\n"); 05706 MSG_Add("MSCDEX_UNKNOWN_ERROR","MSCDEX: Failure: Unknown error.\n"); 05707 05708 MSG_Add("PROGRAM_RESCAN_SUCCESS","Drive cache cleared.\n"); 05709 05710 MSG_Add("PROGRAM_INTRO", 05711 "\033[2J\033[32;1mWelcome to DOSBox-X\033[0m, an x86 emulator with sound and graphics.\n" 05712 "DOSBox-X creates a shell for you which looks like old plain DOS.\n" 05713 "\n" 05714 "\033[31;1mDOSBox-X will stop/exit without a warning if an error occurred!\033[0m\n" 05715 "\n" 05716 "\n" ); 05717 if (machine == MCH_PC98) { 05718 MSG_Add("PROGRAM_INTRO_MENU_UP", 05719 "\033[44m\033[K\033[0m\n" 05720 "\033[44m\033[K\033[1m\033[1m\t\t\t\t\t\t\t DOSBox-X Introduction \033[0m\n" 05721 "\033[44m\033[K\033[1m\033[1m \x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\x86\x43\033[0m\n" 05722 "\033[44m\033[K\033[0m\n" 05723 ); 05724 } else { 05725 MSG_Add("PROGRAM_INTRO_MENU_UP", 05726 "\033[44m\033[K\033[0m\n" 05727 "\033[44m\033[K\033[1m\033[1m\t\t\t\t\t\t\t DOSBox-X Introduction \033[0m\n" 05728 "\033[44m\033[K\033[1m\033[1m \xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\xC4\033[0m\n" 05729 "\033[44m\033[K\033[0m\n" 05730 ); 05731 } 05732 MSG_Add("PROGRAM_INTRO_MENU_BASIC","Basic mount"); 05733 MSG_Add("PROGRAM_INTRO_MENU_CDROM","CD-ROM support"); 05734 MSG_Add("PROGRAM_INTRO_MENU_USAGE","Usage"); 05735 MSG_Add("PROGRAM_INTRO_MENU_INFO","Information"); 05736 MSG_Add("PROGRAM_INTRO_MENU_QUIT","Quit"); 05737 MSG_Add("PROGRAM_INTRO_MENU_BASIC_HELP","\n\033[1m \033[1m\033[KMOUNT allows you to connect real hardware to DOSBox-X's emulated PC.\033[0m\n"); 05738 MSG_Add("PROGRAM_INTRO_MENU_CDROM_HELP","\n\033[1m \033[1m\033[KTo mount your CD-ROM in DOSBox-X, you have to specify some additional options\n when mounting the CD-ROM.\033[0m\n"); 05739 MSG_Add("PROGRAM_INTRO_MENU_USAGE_HELP","\n\033[1m \033[1m\033[KAn overview of the command line options you can give to DOSBox-X.\033[0m\n"); 05740 MSG_Add("PROGRAM_INTRO_MENU_INFO_HELP","\n\033[1m \033[1m\033[KHow to get more information about DOSBox-X.\033[0m\n"); 05741 MSG_Add("PROGRAM_INTRO_MENU_QUIT_HELP","\n\033[1m \033[1m\033[KExit from Intro.\033[0m\n"); 05742 MSG_Add("PROGRAM_INTRO_USAGE_TOP", 05743 "\033[2J\033[32;1mAn overview of the command line options you can give to DOSBox-X.\033[0m\n" 05744 "Windows Users must open cmd.exe or command.com or edit the shortcut to\n" 05745 "DOSBox-X.exe for this.\n\n" 05746 "dosbox-x [name] [-exit] [-c command] [-fullscreen] [-conf congfigfile]\n" 05747 " [-lang languagefile] [-machine machinetype] [-noconsole]\n" 05748 " [-startmapper] [-noautoexec] [-scaler scaler | -forcescaler scaler]\n [-version]\n\n" 05749 ); 05750 MSG_Add("PROGRAM_INTRO_USAGE_1", 05751 "\033[33;1m name\033[0m\n" 05752 "\tIf name is a directory it will mount that as the C: drive.\n" 05753 "\tIf name is an executable it will mount the directory of name\n" 05754 "\tas the C: drive and execute name.\n\n" 05755 "\033[33;1m -exit\033[0m\n" 05756 "\tDOSBox-X will close itself when the DOS application name ends.\n\n" 05757 "\033[33;1m -c\033[0m command\n" 05758 "\tRuns the specified command before running name. Multiple commands\n" 05759 "\tcan be specified. Each command should start with -c, though.\n" 05760 "\tA command can be: an Internal Program, a DOS command or an executable\n" 05761 "\ton a mounted drive.\n" 05762 ); 05763 MSG_Add("PROGRAM_INTRO_USAGE_2", 05764 "\033[33;1m -fullscreen\033[0m\n" 05765 "\tStarts DOSBox-X in fullscreen mode.\n\n" 05766 "\033[33;1m -conf\033[0m configfile\n" 05767 "\tStart DOSBox-X with the options specified in configfile.\n" 05768 "\tSee README for more details.\n\n" 05769 "\033[33;1m -lang\033[0m languagefile\n" 05770 "\tStart DOSBox-X using the language specified in languagefile.\n\n" 05771 "\033[33;1m -noconsole\033[0m (Windows Only)\n" 05772 "\tStart DOSBox-X without showing the console window. Output will\n" 05773 "\tbe redirected to stdout.txt and stderr.txt\n" 05774 ); 05775 MSG_Add("PROGRAM_INTRO_USAGE_3", 05776 "\033[33;1m -machine\033[0m machinetype\n" 05777 "\tSetup DOSBox-X to emulate a specific type of machine. Valid choices are:\n" 05778 "\thercules, cga, pcjr, tandy, vga (default). The machinetype affects\n" 05779 "\tboth the videocard and the available soundcards.\n\n" 05780 "\033[33;1m -startmapper\033[0m\n" 05781 "\tEnter the keymapper directly on startup. Useful for people with\n" 05782 "\tkeyboard problems.\n\n" 05783 "\033[33;1m -noautoexec\033[0m\n" 05784 "\tSkips the [autoexec] section of the loaded configuration file.\n\n" 05785 "\033[33;1m -version\033[0m\n" 05786 "\toutput version information and exit. Useful for frontends.\n" 05787 ); 05788 MSG_Add("PROGRAM_INTRO_INFO", 05789 "\033[32;1mInformation:\033[0m\n\n" 05790 "For information about basic mount, type \033[34;1mintro mount\033[0m\n" 05791 "For information about CD-ROM support, type \033[34;1mintro cdrom\033[0m\n" 05792 "For information about special keys, type \033[34;1mintro special\033[0m\n" 05793 "For information about usage, type \033[34;1mintro usage\033[0m\n\n" 05794 "For the latest version of DOSBox-X, go to \033[34;1mhttp://www.dosbox-x.com\033[0m\n" 05795 "\n" 05796 "For more information about DOSBox-X, read README first!\n" 05797 "\n" 05798 "\033[34;1mhttps://github.com/joncampbell123/dosbox-x/wiki\033[0m\n" 05799 "\033[34;1mhttp://vogons.zetafleet.com\033[0m\n" 05800 ); 05801 MSG_Add("PROGRAM_INTRO_MOUNT_START", 05802 "\033[32;1mHere are some commands to get you started:\033[0m\n" 05803 "Before you can use the files located on your own filesystem,\n" 05804 "you have to mount the directory containing the files.\n" 05805 "\n" 05806 ); 05807 if (machine == MCH_PC98) { 05808 MSG_Add("PROGRAM_INTRO_MOUNT_WINDOWS", 05809 "\033[44;1m\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05810 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05811 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05812 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05813 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05814 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x56\n" 05815 "\x86\x46 \033[32mmount a c:\\dosgames\\\033[37m will create an A drive with c:\\dosgames as contents.\x86\x46\n" 05816 "\x86\x46 \x86\x46\n" 05817 "\x86\x46 \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory. \033[37m \x86\x46\n" 05818 "\x86\x5A\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05819 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05820 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05821 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05822 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05823 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x5E\033[0m\n" 05824 ); 05825 MSG_Add("PROGRAM_INTRO_MOUNT_OTHER", 05826 "\033[44;1m\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05827 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05828 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05829 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05830 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05831 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x56\n" 05832 "\x86\x46 \033[32mmount a ~/dosgames\033[37m will create an A drive with ~/dosgames as contents.\x86\x46\n" 05833 "\x86\x46 \x86\x46\n" 05834 "\x86\x46 \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory. \033[37m \x86\x46\n" 05835 "\x86\x5A\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05836 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05837 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05838 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05839 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 05840 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x5E\033[0m\n" 05841 ); 05842 MSG_Add("PROGRAM_INTRO_MOUNT_END", 05843 "When the mount has successfully completed you can type \033[34;1ma:\033[0m to go to your freshly\n" 05844 "mounted A-drive. Typing \033[34;1mdir\033[0m there will show its contents." 05845 " \033[34;1mcd\033[0m will allow you to\n" 05846 "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n" 05847 "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n" 05848 ); 05849 } else { 05850 MSG_Add("PROGRAM_INTRO_MOUNT_WINDOWS", 05851 "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05852 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05853 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" 05854 "\xBA \033[32mmount c c:\\dosgames\\\033[37m will create a C drive with c:\\dosgames as contents.\xBA\n" 05855 "\xBA \xBA\n" 05856 "\xBA \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory. \033[37m \xBA\n" 05857 "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05858 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05859 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" 05860 ); 05861 MSG_Add("PROGRAM_INTRO_MOUNT_OTHER", 05862 "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05863 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05864 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" 05865 "\xBA \033[32mmount c ~/dosgames\033[37m will create a C drive with ~/dosgames as contents.\xBA\n" 05866 "\xBA \xBA\n" 05867 "\xBA \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory.\033[37m \xBA\n" 05868 "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05869 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 05870 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" 05871 ); 05872 MSG_Add("PROGRAM_INTRO_MOUNT_END", 05873 "When the mount has successfully completed you can type \033[34;1mc:\033[0m to go to your freshly\n" 05874 "mounted C: drive. Typing \033[34;1mdir\033[0m there will show its contents." 05875 " \033[34;1mcd\033[0m will allow you to\n" 05876 "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n" 05877 "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n" 05878 ); 05879 } 05880 MSG_Add("PROGRAM_INTRO_CDROM", 05881 "\033[2J\033[32;1mHow to mount a Real/Virtual CD-ROM Drive in DOSBox-X:\033[0m\n" 05882 "DOSBox-X provides CD-ROM emulation on several levels.\n" 05883 "\n" 05884 "The \033[33mbasic\033[0m level works on all CD-ROM drives and normal directories.\n" 05885 "It installs MSCDEX and marks the files read-only.\n" 05886 "Usually this is enough for most games:\n" 05887 "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom\033[0m or \033[34;1mmount d C:\\example -t cdrom\033[0m\n" 05888 "If it doesn't work you might have to tell DOSBox-X the label of the CD-ROM:\n" 05889 "\033[34;1mmount d C:\\example -t cdrom -label CDLABEL\033[0m\n" 05890 "\n" 05891 "The \033[33mnext\033[0m level adds some low-level support.\n" 05892 "Therefore only works on CD-ROM drives:\n" 05893 "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0\033[0m\n" 05894 "\n" 05895 "The \033[33mlast\033[0m level of support depends on your Operating System:\n" 05896 "For \033[1mWindows 2000\033[0m, \033[1mWindows XP\033[0m and \033[1mLinux\033[0m:\n" 05897 "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-ioctl\033[0m\n" 05898 "For \033[1mWindows 9x\033[0m with a ASPI layer installed:\n" 05899 "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-aspi\033[0m\n" 05900 "\n" 05901 "Replace \033[0;31mD:\\\033[0m with the location of your CD-ROM.\n" 05902 "Replace the \033[33;1m0\033[0m in \033[34;1m-usecd \033[33m0\033[0m with the number reported for your CD-ROM if you type:\n" 05903 "\033[34;1mmount -cd\033[0m\n" 05904 ); 05905 MSG_Add("PROGRAM_BOOT_NOT_EXIST","Bootdisk file does not exist. Failing.\n"); 05906 MSG_Add("PROGRAM_BOOT_NOT_OPEN","Cannot open bootdisk file. Failing.\n"); 05907 MSG_Add("PROGRAM_BOOT_WRITE_PROTECTED","Image file is read-only! Boot in write-protected mode.\n"); 05908 MSG_Add("PROGRAM_BOOT_PRINT_ERROR","This command boots DOSBox-X from either a floppy or hard disk image.\n\n" 05909 "For this command, one can specify a succession of floppy disks swappable\n" 05910 "by pressing Ctrl-F4, and -l specifies the mounted drive to boot from. If\n" 05911 "no drive letter is specified, this defaults to booting from the A drive.\n" 05912 "The only bootable drive letters are A, C, and D. For booting from a hard\n" 05913 "drive (C or D), the image should have already been mounted using the\n" 05914 "\033[34;1mIMGMOUNT\033[0m command.\n\n" 05915 "The syntax of this command is:\n\n" 05916 "\033[34;1mBOOT diskimg1.img [diskimg2.img ...] [-L driveletter]\033[0m\n\n" 05917 "Or:\n\n" 05918 "\033[34;1mBOOT driveletter:\033[0m\n\n" 05919 "Note: An image file with a leading colon (:) will be booted in write-protected\n" 05920 "mode if the \"leading colon write protect image\" option is enabled.\n" 05921 ); 05922 MSG_Add("PROGRAM_BOOT_UNABLE","Unable to boot off of drive %c.\n"); 05923 MSG_Add("PROGRAM_BOOT_IMAGE_OPEN","Opening image file: %s\n"); 05924 MSG_Add("PROGRAM_BOOT_IMAGE_NOT_OPEN","Cannot open %s\n"); 05925 MSG_Add("PROGRAM_BOOT_CART_WO_PCJR","PCjr cartridge found, but machine is not PCjr"); 05926 MSG_Add("PROGRAM_BOOT_CART_LIST_CMDS","Available PCjr cartridge commandos:%s"); 05927 MSG_Add("PROGRAM_BOOT_CART_NO_CMDS","No PCjr cartridge commandos found"); 05928 05929 MSG_Add("PROGRAM_LOADROM_HELP","Loads the specified ROM image file.\n\nLOADROM ROM_file\n"); 05930 MSG_Add("PROGRAM_LOADROM_HELP","Must specify ROM file to load.\n"); 05931 MSG_Add("PROGRAM_LOADROM_SPECIFY_FILE","Must specify ROM file to load.\n"); 05932 MSG_Add("PROGRAM_LOADROM_CANT_OPEN","ROM file not accessible.\n"); 05933 MSG_Add("PROGRAM_LOADROM_TOO_LARGE","ROM file too large.\n"); 05934 MSG_Add("PROGRAM_LOADROM_INCOMPATIBLE","Video BIOS not supported by machine type.\n"); 05935 MSG_Add("PROGRAM_LOADROM_UNRECOGNIZED","ROM file not recognized.\n"); 05936 MSG_Add("PROGRAM_LOADROM_BASIC_LOADED","BASIC ROM loaded.\n"); 05937 05938 MSG_Add("VHD_ERROR_OPENING", "Could not open the specified VHD file.\n"); 05939 MSG_Add("VHD_INVALID_DATA", "The specified VHD file is corrupt and cannot be opened.\n"); 05940 MSG_Add("VHD_UNSUPPORTED_TYPE", "The specified VHD file is of an unsupported type.\n"); 05941 MSG_Add("VHD_ERROR_OPENING_PARENT", "The parent of the specified VHD file could not be found.\n"); 05942 MSG_Add("VHD_PARENT_INVALID_DATA", "The parent of the specified VHD file is corrupt and cannot be opened.\n"); 05943 MSG_Add("VHD_PARENT_UNSUPPORTED_TYPE", "The parent of the specified VHD file is of an unsupported type.\n"); 05944 MSG_Add("VHD_PARENT_INVALID_MATCH", "The parent of the specified VHD file does not contain the expected identifier.\n"); 05945 MSG_Add("VHD_PARENT_INVALID_DATE", "The parent of the specified VHD file has been changed and cannot be loaded.\n"); 05946 05947 MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_DRIVE","Must specify drive letter to mount image at.\n"); 05948 MSG_Add("PROGRAM_IMGMOUNT_SPECIFY2","Must specify drive number (0 or 3) to mount image at (0,1=fda,fdb;2,3=hda,hdb).\n"); 05949 MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_GEOMETRY", 05950 "For \033[33mCD-ROM\033[0m images: \033[34;1mIMGMOUNT drive-letter location-of-image -t iso\033[0m\n" 05951 "\n" 05952 "For \033[33mhardrive\033[0m images: Must specify drive geometry for hard drives:\n" 05953 "bytes_per_sector, sectors_per_cylinder, heads_per_cylinder, cylinder_count.\n" 05954 "\033[34;1mIMGMOUNT drive-letter location-of-image -size bps,spc,hpc,cyl\033[0m\n"); 05955 MSG_Add("PROGRAM_IMGMOUNT_INVALID_IMAGE","Could not load image file.\n" 05956 "Check that the path is correct and the image is accessible.\n"); 05957 MSG_Add("PROGRAM_IMGMOUNT_DYNAMIC_VHD_UNSUPPORTED", "Dynamic VHD files are not supported.\n"); 05958 MSG_Add("PROGRAM_IMGMOUNT_INVALID_GEOMETRY","Could not extract drive geometry from image.\n" 05959 "Use parameter -size bps,spc,hpc,cyl to specify the geometry.\n"); 05960 MSG_Add("PROGRAM_IMGMOUNT_AUTODET_VALUES","Image geometry auto detection: -size %u,%u,%u,%u\n"); 05961 MSG_Add("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED","Type \"%s\" is unsupported. Specify \"hdd\" or \"floppy\" or\"iso\".\n"); 05962 MSG_Add("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED","Format \"%s\" is unsupported. Specify \"fat\" or \"iso\" or \"none\".\n"); 05963 MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_FILE","Must specify file-image to mount.\n"); 05964 MSG_Add("PROGRAM_IMGMOUNT_FILE_NOT_FOUND","Image file not found.\n"); 05965 MSG_Add("PROGRAM_IMGMOUNT_MOUNT","To mount directories, use the \033[34;1mMOUNT\033[0m command, not the \033[34;1mIMGMOUNT\033[0m command.\n"); 05966 MSG_Add("PROGRAM_IMGMOUNT_ALREADY_MOUNTED","Drive already mounted at that letter.\n"); 05967 MSG_Add("PROGRAM_IMGMOUNT_CANT_CREATE","Cannot create drive from file.\n"); 05968 MSG_Add("PROGRAM_IMGMOUNT_CANT_CREATE_PHYSFS","Cannot create PhysFS drive.\n"); 05969 MSG_Add("PROGRAM_IMGMOUNT_MOUNT_NUMBER","Drive number %d mounted as %s\n"); 05970 MSG_Add("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE", "The image must be on a host or local drive.\n"); 05971 MSG_Add("PROGRAM_IMGMOUNT_MULTIPLE_NON_CUEISO_FILES", "Using multiple files is only supported for cue/iso images.\n"); 05972 05973 MSG_Add("PROGRAM_IMGMOUNT_HELP", 05974 "Mounts floppy, hard drive and optical disc images.\n" 05975 "IMGMOUNT drive filename [-t floppy] [-fs fat] [-size ss,s,h,c]\n" 05976 "IMGMOUNT drive filename [-t hdd] [-fs fat] [-size ss,s,h,c] [-ide 1m|1s|2m|2s]\n" 05977 "IMGMOUNT driveLoc filename -fs none [-size ss,s,h,c] [-reservecyl #]\n" 05978 "IMGMOUNT drive filename [-t iso] [-fs iso]\n" 05979 "IMGMOUNT drive -t floppy -el-torito cdDrive\n" 05980 "IMGMOUNT drive -t ram -size driveSize\n" 05981 "IMGMOUNT -u drive|driveLocation (or drive|driveLocation filename [options] -u)\n" 05982 " drive Drive letter to mount the image at\n" 05983 " driveLoc Location to mount drive, where 0-1 are FDDs, 2-5 are HDDs\n" 05984 " filename Filename of the image to mount (leading ':' for read-only)\n" 05985 " -t iso Image type is optical disc iso or cue / bin image\n" 05986 " -t floppy Image type is floppy\n" 05987 " -t hdd Image type is hard disk; VHD and HDI files are supported\n" 05988 " -t ram Image type is RAM drive\n" 05989 " -fs iso File system is ISO 9660\n" 05990 " -fs fat File system is FAT; FAT12, FAT16 and FAT32 are supported\n" 05991 " -fs none Do not detect file system\n" 05992 " -reservecyl # Report # number of cylinders less than actual in BIOS\n" 05993 " -ide 1m|1s|2m|2s Specifies the controller to mount drive\n" 05994 " -size ss,s,h,c Specify the geometry: Sector size,Sectors,Heads,Cylinders\n" 05995 " -size driveSize Specify the drive size in KB\n" 05996 " -el-torito cdDrive Specify the CD drive to load the bootable floppy from\n" 05997 " -u Unmount the drive" 05998 ); 05999 MSG_Add("PROGRAM_IMGMAKE_SYNTAX", 06000 "Creates floppy or harddisk images.\n" 06001 "Syntax: IMGMAKE file [-t type] [[-size size] | [-chs geometry]] [-nofs]\n" 06002 " [-bat] [-fat] [-spc] [-fatcopies] [-rootdir]" 06003 #ifdef WIN32 06004 " [-source source] [-r retries]" 06005 #endif 06006 "\n file: The image file that is to be created - !path on the host!\n" 06007 " -t: Type of image.\n" 06008 " Floppy templates (names resolve to floppy sizes in kilobytes):\n" 06009 " fd_160 fd_180 fd_200 fd_320 fd_360 fd_400 fd_720 fd_1200 fd_1440 fd_2880\n" 06010 " Harddisk templates:\n" 06011 " hd_250: 250MB image, hd_520: 520MB image, hd_2gig: 2GB image\n" 06012 " hd_4gig: 4GB image, hd_8gig: 8GB image (maximum size)\n" 06013 " hd_st251: 40MB image, hd_st225: 20MB image (geometry from old drives)\n" 06014 " Custom harddisk images:\n" 06015 " hd (requires -size or -chs)\n" 06016 " -size: Size of a custom harddisk image in MB.\n" 06017 " -chs: Disk geometry in cylinders(1-1023),heads(1-255),sectors(1-63).\n" 06018 " -nofs: Add this parameter if a blank image should be created.\n" 06019 " -bat: Create a .bat file with the IMGMOUNT command required for this image.\n" 06020 " -fat: FAT filesystem type (12, 16, or 32)\n" 06021 " -spc: Sectors per cluster override. Must be a power of 2.\n" 06022 " -fatcopies: Override number of FAT table copies.\n" 06023 " -rootdir: Size of root directory in entries. Ignored for FAT32.\n" 06024 #ifdef WIN32 06025 " -source: drive letter - if specified the image is read from a floppy disk.\n" 06026 " -retries: how often to retry when attempting to read a bad floppy disk(1-99).\n" 06027 #endif 06028 " -examples: Show some usage examples." 06029 ); 06030 MSG_Add("PROGRAM_IMGMAKE_EXAMPLE", 06031 "Some usage examples of IMGMAKE:\n\n" 06032 " imgmake c:\\image.img -t fd_1440 - create a 1.44MB floppy image\n" 06033 " imgmake c:\\image.img -t hd -size 100 - create a 100MB hdd image\n" 06034 " imgmake c:\\image.img -t hd_520 -nofs - create a 520MB blank hdd image\n" 06035 " imgmake c:\\image.img -t hd_2gig -fat 32 - create a 2GB FAT32 hdd image\n" 06036 " imgmake c:\\image.img -t hd -chs 130,2,17 - create a special hdd image\n" 06037 #ifdef WIN32 06038 " imgmake c:\\image.img -source a - read image from physical drive A\n" 06039 #endif 06040 ); 06041 06042 #ifdef WIN32 06043 MSG_Add("PROGRAM_IMGMAKE_FLREAD", 06044 "Disk geometry: %d Cylinders, %d Heads, %d Sectors, %d Kilobytes\n\n"); 06045 MSG_Add("PROGRAM_IMGMAKE_FLREAD2", 06046 "\xdb =good, \xb1 =good after retries, ! =CRC error, x =sector not found, ? =unknown\n\n"); 06047 #endif 06048 MSG_Add("PROGRAM_IMGMAKE_FILE_EXISTS","The file \"%s\" already exists.\n"); 06049 MSG_Add("PROGRAM_IMGMAKE_CANNOT_WRITE","The file \"%s\" cannot be opened for writing.\n"); 06050 MSG_Add("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE","Not enough space availible for the image file. Need %u bytes.\n"); 06051 MSG_Add("PROGRAM_IMGMAKE_PRINT_CHS","Creating an image file with %u cylinders, %u heads and %u sectors\n"); 06052 MSG_Add("PROGRAM_IMGMAKE_CANT_READ_FLOPPY","\n\nUnable to read floppy."); 06053 06054 MSG_Add("PROGRAM_KEYB_INFO","Codepage %i has been loaded\n"); 06055 MSG_Add("PROGRAM_KEYB_INFO_LAYOUT","Codepage %i has been loaded for layout %s\n"); 06056 MSG_Add("PROGRAM_KEYB_SHOWHELP","Configures a keyboard for a specific language.\n\n" 06057 "\033[32;1mKEYB\033[0m [keyboard layout ID[ codepage number[ codepage file]]]\n\n" 06058 "Some examples:\n" 06059 " \033[32;1mKEYB\033[0m: Display currently loaded codepage.\n" 06060 " \033[32;1mKEYB\033[0m sp: Load the spanish (SP) layout, use an appropriate codepage.\n" 06061 " \033[32;1mKEYB\033[0m sp 850: Load the spanish (SP) layout, use codepage 850.\n" 06062 " \033[32;1mKEYB\033[0m sp 850 mycp.cpi: Same as above, but use file mycp.cpi.\n"); 06063 MSG_Add("PROGRAM_KEYB_NOERROR","Keyboard layout %s loaded for codepage %i\n"); 06064 MSG_Add("PROGRAM_KEYB_FILENOTFOUND","Keyboard file %s not found\n\n"); 06065 MSG_Add("PROGRAM_KEYB_INVALIDFILE","Keyboard file %s invalid\n"); 06066 MSG_Add("PROGRAM_KEYB_LAYOUTNOTFOUND","No layout in %s for codepage %i\n"); 06067 MSG_Add("PROGRAM_KEYB_INVCPFILE","None or invalid codepage file for layout %s\n\n"); 06068 MSG_Add("PROGRAM_MODE_USAGE","Configures system devices.\n\n" 06069 "\033[34;1mMODE\033[0m display-type :display-type codes are " 06070 "\033[1mCO80\033[0m, \033[1mBW80\033[0m, \033[1mCO40\033[0m, \033[1mBW40\033[0m, or \033[1mMONO\033[0m\n" 06071 "\033[34;1mMODE CON RATE=\033[0mr \033[34;1mDELAY=\033[0md :typematic rates, r=1-32 (32=fastest), d=1-4 (1=lowest)\n"); 06072 MSG_Add("PROGRAM_MODE_INVALID_PARAMETERS","Invalid parameter(s).\n"); 06073 06074 /*regular setup*/ 06075 PROGRAMS_MakeFile("INTRO.COM",INTRO_ProgramStart); 06076 06077 if (!IS_PC98_ARCH) 06078 PROGRAMS_MakeFile("LOADROM.COM", LOADROM_ProgramStart); 06079 06080 PROGRAMS_MakeFile("IMGMAKE.COM", IMGMAKE_ProgramStart); 06081 PROGRAMS_MakeFile("IMGMOUNT.COM", IMGMOUNT_ProgramStart); 06082 PROGRAMS_MakeFile("MOUNT.COM",MOUNT_ProgramStart); 06083 PROGRAMS_MakeFile("BOOT.COM",BOOT_ProgramStart); 06084 06085 if (!IS_PC98_ARCH) { 06086 PROGRAMS_MakeFile("KEYB.COM", KEYB_ProgramStart); 06087 PROGRAMS_MakeFile("MODE.COM", MODE_ProgramStart); 06088 PROGRAMS_MakeFile("MOUSE.COM", MOUSE_ProgramStart); 06089 } 06090 06091 PROGRAMS_MakeFile("LOADFIX.COM",LOADFIX_ProgramStart); 06092 PROGRAMS_MakeFile("A20GATE.COM",A20GATE_ProgramStart); 06093 PROGRAMS_MakeFile("SHOWGUI.COM",SHOWGUI_ProgramStart); 06094 #if defined C_DEBUG 06095 PROGRAMS_MakeFile("NMITEST.COM",NMITEST_ProgramStart); 06096 #endif 06097 PROGRAMS_MakeFile("RE-DOS.COM",REDOS_ProgramStart); 06098 PROGRAMS_MakeFile("RESCAN.COM",RESCAN_ProgramStart); 06099 06100 if (IS_VGA_ARCH && svgaCard != SVGA_None) 06101 PROGRAMS_MakeFile("VESAMOED.COM",VESAMOED_ProgramStart); 06102 06103 if (IS_PC98_ARCH) 06104 PROGRAMS_MakeFile("PC98UTIL.COM",PC98UTIL_ProgramStart); 06105 06106 PROGRAMS_MakeFile("CAPMOUSE.COM", CAPMOUSE_ProgramStart); 06107 PROGRAMS_MakeFile("LABEL.COM", LABEL_ProgramStart); 06108 PROGRAMS_MakeFile("AUTOTYPE.COM", AUTOTYPE_ProgramStart); 06109 }