DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_programs.cpp
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,&sects,&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 }