DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_programs.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <ctype.h>
00024 #include <string>
00025 #include <vector>
00026 #include "programs.h"
00027 #include "support.h"
00028 #include "drives.h"
00029 #include "cross.h"
00030 #include "regs.h"
00031 #include "ide.h"
00032 #include "cpu.h"
00033 #include "callback.h"
00034 #include "cdrom.h"
00035 #include "bios_disk.h"
00036 #include "dos_system.h"
00037 #include "dos_inc.h"
00038 #include "bios.h"
00039 #include "inout.h"
00040 #include "dma.h"
00041 #include "bios_disk.h"
00042 #include "qcow2_disk.h"
00043 #include "setup.h"
00044 #include "control.h"
00045 #include <time.h>
00046 #include "menu.h"
00047 #include "render.h"
00048 #include "mouse.h"
00049 bool Mouse_Drv=true;
00050 bool Mouse_Vertical = false;
00051 
00052 #if defined(OS2)
00053 #define INCL DOSFILEMGR
00054 #define INCL_DOSERRORS
00055 #include "os2.h"
00056 #endif
00057 
00058 #if C_DEBUG
00059 Bitu DEBUG_EnableDebugger(void);
00060 #endif
00061 
00062 class MOUSE : public Program {
00063 public:
00064     void Run(void);
00065 };
00066 
00067 void MOUSE::Run(void) {
00068     if (cmd->FindExist("/?",false) || cmd->FindExist("/h",false)) {
00069         WriteOut(MSG_Get("PROGRAM_MOUSE_HELP"));
00070         return;
00071     }
00072     switch ((unsigned char)Mouse_Drv) { /* FIXME: Mouse_Drv is boolean, clang/llvm complains here */
00073     case 0:
00074         if (cmd->FindExist("/u",false))
00075             WriteOut(MSG_Get("PROGRAM_MOUSE_NOINSTALLED"));
00076         else {
00077             Mouse_Drv = true;
00078             mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu);
00079             WriteOut(MSG_Get("PROGRAM_MOUSE_INSTALL"));
00080             if (cmd->FindExist("/v",false)) {
00081                 Mouse_Vertical = true;
00082                 WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL"));
00083             } else {
00084                 Mouse_Vertical = false;
00085             }
00086             mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu);
00087         }
00088         break;
00089     case 1:
00090         if (cmd->FindExist("/u",false)) {
00091             Mouse_Drv = false;
00092             mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu);
00093             WriteOut(MSG_Get("PROGRAM_MOUSE_UNINSTALL"));
00094         } else
00095             if (cmd->FindExist("/v",false)) {
00096                 if(!Mouse_Vertical) {
00097                     Mouse_Vertical = true;
00098                     WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL"));
00099                 } else {
00100                     Mouse_Vertical = false;
00101                     WriteOut(MSG_Get("PROGRAM_MOUSE_VERTICAL_BACK"));
00102                 }
00103                 mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu);
00104             } else
00105                 WriteOut(MSG_Get("PROGRAM_MOUSE_ERROR"));
00106         break;
00107     }
00108     return;
00109 }
00110 
00111 static void MOUSE_ProgramStart(Program * * make) {
00112     *make=new MOUSE;
00113 }
00114 
00115 void MSCDEX_SetCDInterface(int intNr, int forceCD);
00116 Bitu ZDRIVE_NUM = 25;
00117 
00118 class MOUNT : public Program {
00119 public:
00120     void ListMounts(void) {
00121         char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
00122         /* Command uses dta so set it to our internal dta */
00123         RealPt save_dta = dos.dta();
00124         dos.dta(dos.tables.tempdta);
00125         DOS_DTA dta(dos.dta());
00126 
00127         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_1"));
00128         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label");
00129         for(int p = 0;p < 8;p++) WriteOut("----------");
00130 
00131         for (int d = 0;d < DOS_DRIVES;d++) {
00132             if (!Drives[d]) continue;
00133 
00134             char root[4] = {(char)('A'+d),':','\\',0};
00135             bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME);
00136             if (ret) {
00137                 dta.GetResult(name,size,date,time,attr);
00138                 DOS_FindNext(); //Mark entry as invalid
00139             } else name[0] = 0;
00140 
00141             /* Change 8.3 to 11.0 */
00142             char* dot = strchr(name,'.');
00143             if(dot && (dot - name == 8) ) { 
00144                 name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0;
00145             }
00146 
00147             root[1] = 0; //This way, the format string can be reused.
00148             WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name);       
00149         }
00150         dos.dta(save_dta);
00151     }
00152 
00153     void Run(void) {
00154         DOS_Drive *newdrive = NULL;
00155         std::string label;
00156         std::string umount;
00157         std::string newz;
00158         bool quiet=false;
00159         char drive;
00160 
00161         //Hack To allow long commandlines
00162         ChangeToLongCmd();
00163         /* Parse the command line */
00164         /* if the command line is empty show current mounts */
00165         if (!cmd->GetCount()) {
00166             ListMounts();
00167             return;
00168         }
00169 
00170         /* In secure mode don't allow people to change mount points. 
00171          * Neither mount nor unmount */
00172         if(control->SecureMode()) {
00173             WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
00174             return;
00175         }
00176 
00177         if (cmd->FindExist("-q",false))
00178             quiet = true;
00179 
00180         /* Check for unmounting */
00181         if (cmd->FindString("-u",umount,false)) {
00182             umount[0] = toupper(umount[0]);
00183             int i_drive = umount[0]-'A';
00184                 if (i_drive < DOS_DRIVES && i_drive >= 0 && Drives[i_drive]) {
00185                     switch (DriveManager::UnmountDrive(i_drive)) {
00186                     case 0:
00187                         Drives[i_drive] = 0;
00188                         if(i_drive == DOS_GetDefaultDrive()) 
00189                             DOS_SetDrive(ZDRIVE_NUM);
00190                         if (!quiet)
00191                             WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"),umount[0]);
00192                         break;
00193                     case 1:
00194                         WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL"));
00195                         break;
00196                     case 2:
00197                         WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"));
00198                         break;
00199                     }
00200                 } else {
00201                     WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"),umount[0]);
00202                 }
00203             return;
00204         }
00205         
00206         /* Check for moving Z: */
00207         /* Only allowing moving it once. It is merely a convenience added for the wine team */
00208         if (ZDRIVE_NUM == 25 && cmd->FindString("-z", newz,false)) {
00209             newz[0] = toupper(newz[0]);
00210             int i_newz = newz[0] - 'A';
00211             if (i_newz >= 0 && i_newz < DOS_DRIVES-1 && !Drives[i_newz]) {
00212                 ZDRIVE_NUM = (unsigned int)i_newz;
00213                 /* remap drives */
00214                 Drives[i_newz] = Drives[25];
00215                 Drives[25] = 0;
00216                 DOS_Shell *fs = static_cast<DOS_Shell *>(first_shell); //dynamic ?              
00217                 /* Update environment */
00218                 std::string line = "";
00219                 char ppp[2] = {newz[0],0};
00220                 std::string tempenv = ppp; tempenv += ":\\";
00221                 if (fs->GetEnvStr("PATH",line)){
00222                     std::string::size_type idx = line.find('=');
00223                     std::string value = line.substr(idx +1 , std::string::npos);
00224                     while ( (idx = value.find("Z:\\")) != std::string::npos ||
00225                             (idx = value.find("z:\\")) != std::string::npos  )
00226                         value.replace(idx,3,tempenv);
00227                     line = value;
00228                 }
00229                 if (!line.size()) line = tempenv;
00230                 fs->SetEnv("PATH",line.c_str());
00231                 tempenv += "COMMAND.COM";
00232                 fs->SetEnv("COMSPEC",tempenv.c_str());
00233 
00234                 /* Update batch file if running from Z: (very likely: autoexec) */
00235                 if(fs->bf) {
00236                     std::string &name = fs->bf->filename;
00237                     if(name.length() >2 &&  name[0] == 'Z' && name[1] == ':') name[0] = newz[0];
00238                 }
00239                 /* Change the active drive */
00240                 if (DOS_GetDefaultDrive() == 25) DOS_SetDrive(i_newz);
00241             }
00242             return;
00243         }
00244         /* Show list of cdroms */
00245         if (cmd->FindExist("-cd",false)) {
00246 #if !defined(C_SDL2)
00247             int num = SDL_CDNumDrives();
00248             WriteOut(MSG_Get("PROGRAM_MOUNT_CDROMS_FOUND"),num);
00249             for (int i=0; i<num; i++) {
00250                 WriteOut("%2d. %s\n",i,SDL_CDName(i));
00251             };
00252 #endif
00253             return;
00254         }
00255 
00256         bool nocachedir = false;
00257         if (cmd->FindExist("-nocachedir",true))
00258             nocachedir = true;
00259 
00260         bool readonly = false;
00261         if (cmd->FindExist("-ro",true))
00262             readonly = true;
00263         if (cmd->FindExist("-rw",true))
00264             readonly = false;
00265 
00266         std::string type="dir";
00267         cmd->FindString("-t",type,true);
00268         bool iscdrom = (type =="cdrom"); //Used for mscdex bug cdrom label name emulation
00269         if (type=="floppy" || type=="dir" || type=="cdrom") {
00270             Bit16u sizes[4];
00271             Bit8u mediaid;
00272             std::string str_size;
00273             if (type=="floppy") {
00274                 str_size="512,1,2880,2880";/* All space free */
00275                 mediaid=0xF0;       /* Floppy 1.44 media */
00276             } else if (type=="dir") {
00277                 // 512*32*32765==~500MB total size
00278                 // 512*32*16000==~250MB total free size
00279 #if defined(__WIN32__) && !defined(C_SDL2) && !defined(HX_DOS)
00280                 GetDefaultSize();
00281                 str_size=hdd_size;
00282 #else
00283                 str_size="512,32,32765,16000";
00284 #endif
00285                 mediaid=0xF8;       /* Hard Disk */
00286             } else if (type=="cdrom") {
00287                 str_size="2048,1,65535,0";
00288                 mediaid=0xF8;       /* Hard Disk */
00289             } else {
00290                 WriteOut(MSG_Get("PROGAM_MOUNT_ILL_TYPE"),type.c_str());
00291                 return;
00292             }
00293             /* Parse the free space in mb's (kb's for floppies) */
00294             std::string mb_size;
00295             if(cmd->FindString("-freesize",mb_size,true)) {
00296                 char teststr[1024];
00297                 Bit16u freesize = static_cast<Bit16u>(atoi(mb_size.c_str()));
00298                 if (type=="floppy") {
00299                     // freesize in kb
00300                     sprintf(teststr,"512,1,2880,%d",freesize*1024/(512*1));
00301                 } else {
00302                     Bit32u total_size_cyl=32765;
00303                     Bit32u free_size_cyl=(Bit32u)freesize*1024*1024/(512*32);
00304                     if (free_size_cyl>65534) free_size_cyl=65534;
00305                     if (total_size_cyl<free_size_cyl) total_size_cyl=free_size_cyl+10;
00306                     if (total_size_cyl>65534) total_size_cyl=65534;
00307                     sprintf(teststr,"512,32,%d,%d",total_size_cyl,free_size_cyl);
00308                 }
00309                 str_size=teststr;
00310             }
00311            
00312             cmd->FindString("-size",str_size,true);
00313             char number[20];const char * scan=str_size.c_str();
00314             Bitu index=0;Bitu count=0;
00315             /* Parse the str_size string */
00316             while (*scan) {
00317                 if (*scan==',') {
00318                     number[index]=0;sizes[count++]=atoi(number);
00319                     index=0;
00320                 } else number[index++]=*scan;
00321                 scan++;
00322             }
00323             number[index]=0;sizes[count++]=atoi(number);
00324         
00325             // get the drive letter
00326             cmd->FindCommand(1,temp_line);
00327             if ((temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) goto showusage;
00328             drive=toupper(temp_line[0]);
00329             if (!isalpha(drive)) goto showusage;
00330 
00331             if (!cmd->FindCommand(2,temp_line)) goto showusage;
00332             if (!temp_line.size()) goto showusage;
00333             bool is_physfs = temp_line.find(':',((temp_line[0]|0x20) >= 'a' && (temp_line[0]|0x20) <= 'z')?2:0) != std::string::npos;
00334             struct stat test;
00335             //Win32 : strip tailing backslashes
00336             //os2: some special drive check
00337             //rest: substiture ~ for home
00338             bool failed = false;
00339 
00340             (void)failed;// MAY BE UNUSED
00341 
00342 #if defined (WIN32) || defined(OS2)
00343             /* nothing */
00344 #else
00345             // Linux: Convert backslash to forward slash
00346             if (!is_physfs && temp_line.size() > 0) {
00347                 for (size_t i=0;i < temp_line.size();i++) {
00348                     if (temp_line[i] == '\\')
00349                         temp_line[i] = '/';
00350                 }
00351             }
00352 #endif
00353 
00354 #if defined (WIN32) || defined(OS2)
00355             /* Removing trailing backslash if not root dir so stat will succeed */
00356             if(temp_line.size() > 3 && temp_line[temp_line.size()-1]=='\\') temp_line.erase(temp_line.size()-1,1);
00357             if (!is_physfs && stat(temp_line.c_str(),&test)) {
00358 #endif
00359 #if defined(WIN32)
00360 // Nothing to do here.
00361 #elif defined (OS2)
00362                 if (temp_line.size() <= 2) // Seems to be a drive.
00363                 {
00364                     failed = true;
00365                     HFILE cdrom_fd = 0;
00366                     ULONG ulAction = 0;
00367 
00368                     APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
00369                         OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
00370                     DosClose(cdrom_fd);
00371                     if (rc != NO_ERROR && rc != ERROR_NOT_READY)
00372                     {
00373                         failed = true;
00374                     } else {
00375                         failed = false;
00376                     }
00377                 }
00378             }
00379             if (failed) {
00380 #else
00381             if (!is_physfs && stat(temp_line.c_str(),&test)) {
00382                 failed = true;
00383                 Cross::ResolveHomedir(temp_line);
00384                 //Try again after resolving ~
00385                 if(!stat(temp_line.c_str(),&test)) failed = false;
00386             }
00387             if(failed) {
00388 #endif
00389                 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_1"),temp_line.c_str());
00390                 return;
00391             }
00392             /* Not a switch so a normal directory/file */
00393             if (!is_physfs && !(test.st_mode & S_IFDIR)) {
00394 #ifdef OS2
00395                 HFILE cdrom_fd = 0;
00396                 ULONG ulAction = 0;
00397 
00398                 APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
00399                     OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
00400                 DosClose(cdrom_fd);
00401                 if (rc != NO_ERROR && rc != ERROR_NOT_READY) {
00402                 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str());
00403                 return;
00404             }
00405 #else
00406                 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str());
00407                 return;
00408 #endif
00409 
00410             }
00411 
00412             if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT;
00413             Bit8u bit8size=(Bit8u) sizes[1];
00414             if (type=="cdrom") {
00415                 int num = -1;
00416                 cmd->FindInt("-usecd",num,true);
00417                 int error = 0;
00418                 if (cmd->FindExist("-aspi",false)) {
00419                     MSCDEX_SetCDInterface(CDROM_USE_ASPI, num);
00420                 } else if (cmd->FindExist("-ioctl_dio",false)) {
00421                     MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
00422                 } else if (cmd->FindExist("-ioctl_dx",false)) {
00423                     MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num);
00424 #if defined (WIN32)
00425                 } else if (cmd->FindExist("-ioctl_mci",false)) {
00426                     MSCDEX_SetCDInterface(CDROM_USE_IOCTL_MCI, num);
00427 #endif
00428                 } else if (cmd->FindExist("-noioctl",false)) {
00429                     MSCDEX_SetCDInterface(CDROM_USE_SDL, num);
00430                 } else {
00431 #if defined (WIN32)
00432                     // Check OS
00433                     OSVERSIONINFO osi;
00434                     osi.dwOSVersionInfoSize = sizeof(osi);
00435                     GetVersionEx(&osi);
00436                     if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>5)) {
00437                         // Vista/above
00438                         MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num);
00439                     } else {
00440                         MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
00441                     }
00442 #else
00443                     MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
00444 #endif
00445                 }
00446                 if (is_physfs) {
00447                     LOG_MSG("ERROR:This build does not support physfs");
00448                 } else {
00449                     newdrive  = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error);
00450                 }
00451                 // Check Mscdex, if it worked out...
00452                 switch (error) {
00453                     case 0  :   WriteOut(MSG_Get("MSCDEX_SUCCESS"));                break;
00454                     case 1  :   WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"));  break;
00455                     case 2  :   WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED"));    break;
00456                     case 3  :   WriteOut(MSG_Get("MSCDEX_ERROR_PATH"));             break;
00457                     case 4  :   WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES"));        break;
00458                     case 5  :   WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT"));        break;
00459                     default :   WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR"));          break;
00460                 };
00461                 if (error && error!=5) {
00462                     delete newdrive;
00463                     return;
00464                 }
00465             } else {
00466                 /* Give a warning when mount c:\ or the / */
00467 #if defined (WIN32) || defined(OS2)
00468                 if( (temp_line == "c:\\") || (temp_line == "C:\\") || 
00469                     (temp_line == "c:/") || (temp_line == "C:/")    )   
00470                     WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_WIN"));
00471 #else
00472                 if(temp_line == "/") WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_OTHER"));
00473 #endif
00474                 if (is_physfs) {
00475                     LOG_MSG("ERROR:This build does not support physfs");
00476                 } else {
00477                     newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid);
00478                     newdrive->nocachedir = nocachedir;
00479                     newdrive->readonly = readonly;
00480                 }
00481             }
00482         } else {
00483             WriteOut(MSG_Get("PROGRAM_MOUNT_ILL_TYPE"),type.c_str());
00484             return;
00485         }
00486         if (Drives[drive-'A']) {
00487             WriteOut(MSG_Get("PROGRAM_MOUNT_ALREADY_MOUNTED"),drive,Drives[drive-'A']->GetInfo());
00488             if (newdrive) delete newdrive;
00489             return;
00490         }
00491         if (!newdrive) E_Exit("DOS:Can't create drive");
00492         Drives[drive-'A']=newdrive;
00493         /* Set the correct media byte in the table */
00494         mem_writeb(Real2Phys(dos.tables.mediaid)+((unsigned int)drive-'A')*2u,newdrive->GetMediaByte());
00495         if (!quiet) WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,newdrive->GetInfo());
00496         /* check if volume label is given and don't allow it to updated in the future */
00497         if (cmd->FindString("-label",label,true)) newdrive->SetLabel(label.c_str(),iscdrom,false);
00498         /* For hard drives set the label to DRIVELETTER_Drive.
00499          * For floppy drives set the label to DRIVELETTER_Floppy.
00500          * This way every drive except cdroms should get a label.*/
00501         else if(type == "dir") { 
00502 #if defined (WIN32) || defined(OS2)
00503             if(temp_line.size()==3 && toupper(drive) == toupper(temp_line[0]))  {
00504                 // automatic mount
00505             } else {
00506                 label = drive; label += "_DRIVE";
00507                 newdrive->SetLabel(label.c_str(),iscdrom,true);
00508             }
00509 #endif
00510         } else if(type == "floppy") {
00511 #if defined (WIN32) || defined(OS2)
00512             if(temp_line.size()==3 && toupper(drive) == toupper(temp_line[0]))  {
00513                 // automatic mount
00514             } else {
00515                 label = drive; label += "_FLOPPY";
00516                 newdrive->SetLabel(label.c_str(),iscdrom,true);
00517             }
00518 #endif
00519         }
00520         if(type == "floppy") incrementFDD();
00521         return;
00522 showusage:
00523 #if defined (WIN32) || defined(OS2)
00524        WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"d:\\dosprogs","d:\\dosprogs");
00525 #else
00526        WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"~/dosprogs","~/dosprogs");         
00527 #endif
00528         return;
00529     }
00530 };
00531 
00532 static void MOUNT_ProgramStart(Program * * make) {
00533     *make=new MOUNT;
00534 }
00535 
00536 #if !defined(C_SDL2)
00537 void GUI_Run(bool pressed);
00538 
00539 class SHOWGUI : public Program {
00540 public:
00541     void Run(void) {
00542         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 */
00543     }
00544 };
00545 
00546 static void SHOWGUI_ProgramStart(Program * * make) {
00547     *make=new SHOWGUI;
00548 }
00549 #endif
00550 
00551 extern bool custom_bios;
00552 extern Bit32u floppytype;
00553 extern bool dos_kernel_disabled;
00554 extern bool boot_debug_break;
00555 extern Bitu BIOS_bootfail_code_offset;
00556 
00557 void DisableINT33();
00558 void EMS_DoShutDown();
00559 void XMS_DoShutDown();
00560 void DOS_DoShutDown();
00561 void GUS_DOS_Shutdown();
00562 void SBLASTER_DOS_Shutdown();
00563 
00564 extern int swapInDisksSpecificDrive;
00565 
00573 class BOOT : public Program {
00574 public:
00575     BOOT() {
00576         for (size_t i=0;i < MAX_SWAPPABLE_DISKS;i++) newDiskSwap[i] = NULL;
00577     }
00578     virtual ~BOOT() {
00579         for (size_t i=0;i < MAX_SWAPPABLE_DISKS;i++) {
00580             if (newDiskSwap[i] != NULL) {
00581                 newDiskSwap[i]->Release();
00582                 newDiskSwap[i] = NULL;
00583             }
00584         }
00585     }
00586 public:
00589     imageDisk *newDiskSwap[MAX_SWAPPABLE_DISKS];
00590 
00591 private:
00592 
00595     FILE *getFSFile_mounted(char const* filename, Bit32u *ksize, Bit32u *bsize, Bit8u *error) {
00596         //if return NULL then put in error the errormessage code if an error was requested
00597         bool tryload = (*error)?true:false;
00598         *error = 0;
00599         Bit8u drive;
00600         FILE *tmpfile;
00601         char fullname[DOS_PATHLENGTH];
00602 
00603         localDrive* ldp=0;
00604         if (!DOS_MakeName(const_cast<char*>(filename),fullname,&drive)) return NULL;
00605 
00606         try {       
00607             ldp=dynamic_cast<localDrive*>(Drives[drive]);
00608             if(!ldp) return NULL;
00609 
00610             tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
00611             if(tmpfile == NULL) {
00612                 if (!tryload) *error=1;
00613                 return NULL;
00614             }
00615 
00616             // get file size
00617             fseek(tmpfile,0L, SEEK_END);
00618             *ksize = (ftell(tmpfile) / 1024);
00619             *bsize = ftell(tmpfile);
00620             fclose(tmpfile);
00621 
00622             tmpfile = ldp->GetSystemFilePtr(fullname, "rb+");
00623             if(tmpfile == NULL) {
00624 //              if (!tryload) *error=2;
00625 //              return NULL;
00626                 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
00627                 tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
00628                 if(tmpfile == NULL) {
00629                     if (!tryload) *error=1;
00630                     return NULL;
00631                 }
00632             }
00633 
00634             return tmpfile;
00635         }
00636         catch(...) {
00637             return NULL;
00638         }
00639     }
00640 
00643     FILE *getFSFile(char const * filename, Bit32u *ksize, Bit32u *bsize,bool tryload=false) {
00644         Bit8u error = tryload?1:0;
00645         FILE* tmpfile = getFSFile_mounted(filename,ksize,bsize,&error);
00646         if(tmpfile) return tmpfile;
00647         //File not found on mounted filesystem. Try regular filesystem
00648         std::string filename_s(filename);
00649         Cross::ResolveHomedir(filename_s);
00650         tmpfile = fopen(filename_s.c_str(),"rb+");
00651         if(!tmpfile) {
00652             if( (tmpfile = fopen(filename_s.c_str(),"rb")) ) {
00653                 //File exists; So can't be opened in correct mode => error 2
00654 //              fclose(tmpfile);
00655 //              if(tryload) error = 2;
00656                 WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
00657                 fseek(tmpfile,0L, SEEK_END);
00658                 *ksize = (ftell(tmpfile) / 1024);
00659                 *bsize = ftell(tmpfile);
00660                 return tmpfile;
00661             }
00662             // Give the delayed errormessages from the mounted variant (or from above)
00663             if(error == 1) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_EXIST"));
00664             if(error == 2) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_OPEN"));
00665             return NULL;
00666         }
00667         fseek(tmpfile,0L, SEEK_END);
00668         *ksize = (ftell(tmpfile) / 1024);
00669         *bsize = ftell(tmpfile);
00670         return tmpfile;
00671     }
00672 
00675     void printError(void) {
00676         WriteOut(MSG_Get("PROGRAM_BOOT_PRINT_ERROR"));
00677     }
00678 
00681     void disable_umb_ems_xms(void) {
00682         Section* dos_sec = control->GetSection("dos");
00683         char test[20];
00684         strcpy(test,"umb=false");
00685         dos_sec->HandleInputline(test);
00686         strcpy(test,"xms=false");
00687         dos_sec->HandleInputline(test);
00688         strcpy(test,"ems=false");
00689         dos_sec->HandleInputline(test);
00690     }
00691 
00692 public:
00693    
00696     void Run(void) {
00697         std::string bios;
00698         bool bios_boot = false;
00699         bool swaponedrive = false;
00700         bool force = false;
00701 
00702         boot_debug_break = false;
00703         if (cmd->FindExist("-debug",true))
00704             boot_debug_break = true;
00705 
00706         if (cmd->FindExist("-swap-one-drive",true))
00707             swaponedrive = true;
00708 
00709         if (cmd->FindExist("-force",true))
00710             force = true;
00711 
00712         if (cmd->FindString("-bios",bios,true))
00713             bios_boot = true;
00714 
00715         //Hack To allow long commandlines
00716         ChangeToLongCmd();
00717         /* In secure mode don't allow people to boot stuff. 
00718          * They might try to corrupt the data on it */
00719         if(control->SecureMode()) {
00720             WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
00721             return;
00722         }
00723 
00724         if (bios_boot) {
00725             Bit32u isz1,isz2;
00726 
00727             if (bios.empty()) {
00728                 WriteOut("Must specify BIOS image to boot\n");
00729                 return;
00730             }
00731 
00732             // NOTES:
00733             // 
00734             // Regarding PC-98 mode, you should use an older BIOS image.
00735             // The PC-9821 ROM image(s) I have appear to rely on bank
00736             // switching parts of itself to boot up and operate.
00737             //
00738             // Update: I found some PC-9801 ROM BIOS images online, which
00739             //         ALSO seem to have a BIOS.ROM, ITF.ROM, etc...
00740             //
00741             //         So, this command will not be able to run those
00742             //         images until port 43Dh (the I/O port used for
00743             //         bank switching) is implemented in DOSBox-X.
00744             //
00745             // In IBM PC/AT mode, this should hopefully allow using old
00746             // 386/486 BIOSes in DOSBox-X.
00747 
00748             /* load it */
00749             FILE *romfp = getFSFile(bios.c_str(), &isz1, &isz2);
00750             if (romfp == NULL) {
00751                 WriteOut("Unable to open BIOS image\n");
00752                 return;
00753             }
00754             Bitu loadsz = (isz2 + 0xFU) & (~0xFU);
00755             if (loadsz == 0) loadsz = 0x10;
00756             if (loadsz > (IS_PC98_ARCH ? 0x18000u : 0x20000u)) loadsz = (IS_PC98_ARCH ? 0x18000u : 0x20000u);
00757             Bitu segbase = 0x100000 - loadsz;
00758             LOG_MSG("Loading BIOS image %s to 0x%lx, 0x%lx bytes",bios.c_str(),(unsigned long)segbase,(unsigned long)loadsz);
00759             fseek(romfp, 0, SEEK_SET);
00760             fread(GetMemBase()+segbase,loadsz,1,romfp);
00761             fclose(romfp);
00762 
00763             custom_bios = true;
00764 
00765             /* boot it */
00766             throw int(8);
00767         }
00768 
00769         bool bootbyDrive=false;
00770         FILE *usefile_1=NULL;
00771         FILE *usefile_2=NULL;
00772         Bitu i=0; 
00773         Bit32u floppysize=0;
00774         Bit32u rombytesize_1=0;
00775         Bit32u rombytesize_2=0;
00776         Bit8u drive = 'A';
00777         std::string cart_cmd="";
00778         Bitu max_seg;
00779 
00780         /* IBM PC:
00781          *    CS:IP = 0000:7C00     Load = 07C0:0000
00782          *    SS:SP = ???
00783          *
00784          * PC-98:
00785          *    CS:IP = 1FE0:0000     Load = 1FE0:0000
00786          *    SS:SP = 0030:00D8
00787          */
00788         Bitu stack_seg=IS_PC98_ARCH ? 0x0030 : 0x7000;
00789         Bitu load_seg;//=IS_PC98_ARCH ? 0x1FE0 : 0x07C0;
00790 
00791         if (MEM_TotalPages() > 0x9C)
00792             max_seg = 0x9C00;
00793         else
00794             max_seg = MEM_TotalPages() << (12 - 4);
00795 
00796         if ((stack_seg+0x20) > max_seg)
00797             stack_seg = max_seg - 0x20;
00798 
00799         if(!cmd->GetCount()) {
00800             printError();
00801             return;
00802         }
00803         while(i<cmd->GetCount()) {
00804             if(cmd->FindCommand(i+1, temp_line)) {
00805                 if((temp_line == "-l") || (temp_line == "-L")) {
00806                     /* Specifying drive... next argument then is the drive */
00807                     bootbyDrive = true;
00808                     i++;
00809                     if(cmd->FindCommand(i+1, temp_line)) {
00810                         drive=toupper(temp_line[0]);
00811                         if ((drive != 'A') && (drive != 'C') && (drive != 'D')) {
00812                             printError();
00813                             return;
00814                         }
00815 
00816                     } else {
00817                         printError();
00818                         return;
00819                     }
00820                     i++;
00821                     continue;
00822                 }
00823 
00824                 if((temp_line == "-e") || (temp_line == "-E")) {
00825                     /* Command mode for PCJr cartridges */
00826                     i++;
00827                     if(cmd->FindCommand(i + 1, temp_line)) {
00828                         for(size_t ct = 0;ct < temp_line.size();ct++) temp_line[ct] = toupper(temp_line[ct]);
00829                         cart_cmd = temp_line;
00830                     } else {
00831                         printError();
00832                         return;
00833                     }
00834                     i++;
00835                     continue;
00836                 }
00837 
00838                 Bit32u rombytesize=0;
00839                 WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"), temp_line.c_str());
00840                 FILE *usefile = getFSFile(temp_line.c_str(), &floppysize, &rombytesize);
00841                 if(usefile != NULL) {
00842                     char tmp[256];
00843 
00844                     if (newDiskSwap[i] != NULL) {
00845                         newDiskSwap[i]->Release();
00846                         newDiskSwap[i] = NULL;
00847                     }
00848 
00849                     fseeko64(usefile, 0L, SEEK_SET);
00850                     fread(tmp,256,1,usefile); // look for magic signatures
00851 
00852                     const char *ext = strrchr(temp_line.c_str(),'.');
00853 
00854                     if (ext != NULL && !strcasecmp(ext, ".d88")) {
00855                         newDiskSwap[i] = new imageDiskD88(usefile, (Bit8u *)temp_line.c_str(), floppysize, false);
00856                     }
00857                     else if (!memcmp(tmp,"VFD1.",5)) { /* FDD files */
00858                         newDiskSwap[i] = new imageDiskVFD(usefile, (Bit8u *)temp_line.c_str(), floppysize, false);
00859                     }
00860                     else {
00861                         newDiskSwap[i] = new imageDisk(usefile, (Bit8u *)temp_line.c_str(), floppysize, false);
00862                     }
00863                     newDiskSwap[i]->Addref();
00864                     if (newDiskSwap[i]->active && !newDiskSwap[i]->hardDrive) incrementFDD(); //moved from imageDisk constructor
00865 
00866                     if (usefile_1==NULL) {
00867                         usefile_1=usefile;
00868                         rombytesize_1=rombytesize;
00869                     } else {
00870                         usefile_2=usefile;
00871                         rombytesize_2=rombytesize;
00872                     }
00873                 } else {
00874                     WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_NOT_OPEN"), temp_line.c_str());
00875                     return;
00876                 }
00877 
00878             }
00879             i++;
00880         }
00881 
00882         if (!bootbyDrive) {
00883             if (i == 0) {
00884                 WriteOut("No images specified");
00885                 return;
00886             }
00887 
00888             if (i > 1) {
00889                 /* if more than one image is given, then this drive becomes the focus of the swaplist */
00890                 if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive != (drive - 65)) {
00891                     WriteOut("Multiple disk images specified and another drive is already connected to the swap list");
00892                     return;
00893                 }
00894                 else if (swapInDisksSpecificDrive < 0 && swaponedrive) {
00895                     swapInDisksSpecificDrive = drive - 65;
00896                 }
00897 
00898                 /* transfer to the diskSwap array */
00899                 for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) {
00900                     if (diskSwap[si] != NULL) {
00901                         diskSwap[si]->Release();
00902                         diskSwap[si] = NULL;
00903                     }
00904 
00905                     diskSwap[si] = newDiskSwap[si];
00906                     newDiskSwap[si] = NULL;
00907                 }
00908 
00909                 swapPosition = 0;
00910                 swapInDisks();
00911             }
00912             else {
00913                 if (swapInDisksSpecificDrive == (drive - 65)) {
00914                     /* if we're replacing the diskSwap drive clear it now */
00915                     for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) {
00916                         if (diskSwap[si] != NULL) {
00917                             diskSwap[si]->Release();
00918                             diskSwap[si] = NULL;
00919                         }
00920                     }
00921 
00922                     swapInDisksSpecificDrive = -1;
00923                 }
00924 
00925                 /* attach directly without using the swap list */
00926                 if (imageDiskList[drive-65] != NULL) {
00927                     imageDiskList[drive-65]->Release();
00928                     imageDiskList[drive-65] = NULL;
00929                 }
00930 
00931                 imageDiskList[drive-65] = newDiskSwap[0];
00932                 newDiskSwap[0] = NULL;
00933             }
00934         }
00935 
00936         if(imageDiskList[drive-65]==NULL) {
00937             WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive);
00938             return;
00939         }
00940 
00941         // .D88 images come from PC-88 which usually means the boot sector is full
00942         // of Z80 executable code, therefore it's very unlikely the boot sector will
00943         // work with our x86 emulation!
00944         //
00945         // If the user is REALLY REALLY SURE they want to try executing Z80 bootsector
00946         // code as x86, they're free to use --force.
00947         //
00948         // However PC-98 games are also distributed as .D88 images and therefore
00949         // we probably CAN boot the image.
00950         //
00951         // It depends on the fd_type field of the image.
00952         if (!force && imageDiskList[drive-65]->class_id == imageDisk::ID_D88) {
00953             if (reinterpret_cast<imageDiskD88*>(imageDiskList[drive-65])->fd_type_major == imageDiskD88::DISKTYPE_2D) {
00954                 WriteOut("The D88 image appears to target PC-88 and cannot be booted.");
00955                 return;
00956             }
00957         }
00958 
00959 
00960         bootSector bootarea;
00961 
00962         if (imageDiskList[drive-65]->getSectSize() > sizeof(bootarea)) {
00963             WriteOut("Bytes/sector too large");
00964             return;
00965         }
00966 
00967         bool has_read = false;
00968         bool pc98_sect128 = false;
00969         unsigned int bootsize = imageDiskList[drive-65]->getSectSize();
00970 
00971         if (!has_read && IS_PC98_ARCH) {
00972             /* this may be one of those odd FDD images where track 0, head 0 is all 128-byte sectors
00973              * and the rest of the disk is 256-byte sectors. */
00974             if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea, 128) == 0 &&
00975                 imageDiskList[drive - 65]->Read_Sector(0, 0, 2, (Bit8u *)&bootarea + 128, 128) == 0) {
00976                 LOG_MSG("First sector is 128 byte/sector. Booting from first two sectors.");
00977                 has_read = true;
00978                 bootsize = 256; // 128 x 2
00979                 pc98_sect128 = true;
00980             }
00981         }
00982 
00983         if (!has_read && IS_PC98_ARCH) {
00984             /* another nonstandard one with track 0 having 256 bytes/sector while the rest have 1024 bytes/sector */
00985             if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea,       256) == 0 &&
00986                 imageDiskList[drive - 65]->Read_Sector(0, 0, 2, (Bit8u *)&bootarea + 256, 256) == 0 &&
00987                 imageDiskList[drive - 65]->Read_Sector(0, 0, 3, (Bit8u *)&bootarea + 512, 256) == 0 &&
00988                 imageDiskList[drive - 65]->Read_Sector(0, 0, 4, (Bit8u *)&bootarea + 768, 256) == 0) {
00989                 LOG_MSG("First sector is 256 byte/sector. Booting from first two sectors.");
00990                 has_read = true;
00991                 bootsize = 1024; // 256 x 4
00992                 pc98_sect128 = true;
00993             }
00994         }
00995 
00996         /* NTS: Load address is 128KB - sector size */
00997         load_seg=IS_PC98_ARCH ? (0x2000 - (bootsize/16U)) : 0x07C0;
00998 
00999         if (!has_read) {
01000             if (imageDiskList[drive - 65]->Read_Sector(0, 0, 1, (Bit8u *)&bootarea) != 0) {
01001                 WriteOut("Error reading drive");
01002                 return;
01003             };
01004         }
01005 
01006         Bitu pcjr_hdr_length = 0;
01007         Bit8u pcjr_hdr_type = 0; // not a PCjr cartridge
01008         if ((bootarea.rawdata[0]==0x50) && (bootarea.rawdata[1]==0x43) && (bootarea.rawdata[2]==0x6a) && (bootarea.rawdata[3]==0x72)) {
01009             pcjr_hdr_type = 1; // JRipCart
01010             pcjr_hdr_length = 0x200;
01011         } else if ((bootarea.rawdata[56]==0x50) && (bootarea.rawdata[57]==0x43) && (bootarea.rawdata[58]==0x4a) && (bootarea.rawdata[59]==0x52)) {
01012             pcjr_hdr_type = 2; // PCJRCart
01013             pcjr_hdr_length = 0x80;
01014         }
01015         
01016         if (pcjr_hdr_type > 0) {
01017             if (machine!=MCH_PCJR) WriteOut(MSG_Get("PROGRAM_BOOT_CART_WO_PCJR"));
01018             else {
01019                 Bit8u rombuf[65536];
01020                 Bits cfound_at=-1;
01021                 if (cart_cmd!="") {
01022                     /* read cartridge data into buffer */
01023                     fseek(usefile_1, (long)pcjr_hdr_length, SEEK_SET);
01024                     fread(rombuf, 1, rombytesize_1-pcjr_hdr_length, usefile_1);
01025 
01026                     char cmdlist[1024];
01027                     cmdlist[0]=0;
01028                     Bitu ct=6;
01029                     Bits clen=rombuf[ct];
01030                     char buf[257];
01031                     if (cart_cmd=="?") {
01032                         while (clen!=0) {
01033                             safe_strncpy(buf,(char*)&rombuf[ct+1],clen);
01034                             buf[clen]=0;
01035                             upcase(buf);
01036                             strcat(cmdlist," ");
01037                             strcat(cmdlist,buf);
01038                             ct+=1u+(Bitu)clen+3u;
01039                             if (ct>sizeof(cmdlist)) break;
01040                             clen=rombuf[ct];
01041                         }
01042                         if (ct>6) {
01043                             WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist);
01044                         } else {
01045                             WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS"));
01046                         }
01047                         for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
01048                             if(diskSwap[dct]!=NULL) {
01049                                 diskSwap[dct]->Release();
01050                                 diskSwap[dct]=NULL;
01051                             }
01052                         }
01053                         //fclose(usefile_1); //delete diskSwap closes the file
01054                         return;
01055                     } else {
01056                         while (clen!=0) {
01057                             safe_strncpy(buf,(char*)&rombuf[ct+1],clen);
01058                             buf[clen]=0;
01059                             upcase(buf);
01060                             strcat(cmdlist," ");
01061                             strcat(cmdlist,buf);
01062                             ct+=1u+(Bitu)clen;
01063 
01064                             if (cart_cmd==buf) {
01065                                 cfound_at=(Bits)ct;
01066                                 break;
01067                             }
01068 
01069                             ct+=3;
01070                             if (ct>sizeof(cmdlist)) break;
01071                             clen=rombuf[ct];
01072                         }
01073                         if (cfound_at<=0) {
01074                             if (ct>6) {
01075                                 WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist);
01076                             } else {
01077                                 WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS"));
01078                             }
01079                             for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
01080                                 if(diskSwap[dct]!=NULL) {
01081                                     diskSwap[dct]->Release();
01082                                     diskSwap[dct]=NULL;
01083                                 }
01084                             }
01085                             //fclose(usefile_1); //Delete diskSwap closes the file
01086                             return;
01087                         }
01088                     }
01089                 }
01090 
01091                 disable_umb_ems_xms();
01092                 void PreparePCJRCartRom(void);
01093                 PreparePCJRCartRom();
01094 
01095                 if (usefile_1==NULL) return;
01096 
01097                 Bit32u sz1,sz2;
01098                 FILE *tfile = getFSFile("system.rom", &sz1, &sz2, true);
01099                 if (tfile!=NULL) {
01100                     fseek(tfile, 0x3000L, SEEK_SET);
01101                     Bit32u drd=(Bit32u)fread(rombuf, 1, 0xb000, tfile);
01102                     if (drd==0xb000) {
01103                         for(i=0;i<0xb000;i++) phys_writeb(0xf3000+i,rombuf[i]);
01104                     }
01105                     fclose(tfile);
01106                 }
01107 
01108                 if (usefile_2!=NULL) {
01109                     unsigned int romseg_pt=0;
01110 
01111                     fseek(usefile_2, 0x0L, SEEK_SET);
01112                     fread(rombuf, 1, pcjr_hdr_length, usefile_2);
01113                     if (pcjr_hdr_type == 1) {
01114                         romseg_pt=host_readw(&rombuf[0x1ce]);
01115                     } else {
01116                         fseek(usefile_2, 0x61L, SEEK_SET);
01117                         fscanf(usefile_2, "%4x", &romseg_pt);
01118                     }
01119                     /* read cartridge data into buffer */
01120                     fseek(usefile_2, (long)pcjr_hdr_length, SEEK_SET);
01121                     fread(rombuf, 1, rombytesize_2-pcjr_hdr_length, usefile_2);
01122                     //fclose(usefile_2); //usefile_2 is in diskSwap structure which should be deleted to close the file
01123 
01124                     /* write cartridge data into ROM */
01125                     for(i=0;i<rombytesize_2-pcjr_hdr_length;i++) phys_writeb((romseg_pt<<4)+i,rombuf[i]);
01126                 }
01127 
01128                 unsigned int romseg=0;
01129 
01130                 fseek(usefile_1, 0x0L, SEEK_SET);
01131                 fread(rombuf, 1, pcjr_hdr_length, usefile_1);
01132                 if (pcjr_hdr_type == 1) {
01133                     romseg=host_readw(&rombuf[0x1ce]);
01134                 } else {
01135                     fseek(usefile_1, 0x61L, SEEK_SET);
01136                     fscanf(usefile_1, "%4x", &romseg);
01137                 }
01138                 /* read cartridge data into buffer */
01139                 fseek(usefile_1,(long)pcjr_hdr_length, SEEK_SET);
01140                 fread(rombuf, 1, rombytesize_1-pcjr_hdr_length, usefile_1);
01141                 //fclose(usefile_1); //usefile_1 is in diskSwap structure which should be deleted to close the file
01142                 /* write cartridge data into ROM */
01143                 for(i=0;i<rombytesize_1-pcjr_hdr_length;i++) phys_writeb((romseg<<4)+i,rombuf[i]);
01144 
01145                 //Close cardridges
01146                 for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
01147                     if(diskSwap[dct]!=NULL) {
01148                         diskSwap[dct]->Release();
01149                         diskSwap[dct]=NULL;
01150                     }
01151                 }
01152 
01153 
01154                 if (cart_cmd=="") {
01155                     Bit32u old_int18=mem_readd(0x60);
01156                     /* run cartridge setup */
01157                     SegSet16(ds,romseg);
01158                     SegSet16(es,romseg);
01159                     SegSet16(ss,0x8000);
01160                     reg_esp=0xfffe;
01161                     CALLBACK_RunRealFar(romseg,0x0003);
01162 
01163                     Bit32u new_int18=mem_readd(0x60);
01164                     if (old_int18!=new_int18) {
01165                         /* boot cartridge (int18) */
01166                         SegSet16(cs,RealSeg(new_int18));
01167                         reg_ip = RealOff(new_int18);
01168                     } 
01169                 } else {
01170                     if (cfound_at>0) {
01171                         /* run cartridge setup */
01172                         SegSet16(ds,dos.psp());
01173                         SegSet16(es,dos.psp());
01174                         CALLBACK_RunRealFar(romseg,cfound_at);
01175                     }
01176                 }
01177             }
01178         } else {
01179             extern const char* RunningProgram;
01180 
01181             if (max_seg < 0x0800) {
01182                 /* TODO: For the adventerous, add a configuration option or command line switch to "BOOT"
01183                  *       that allows us to boot the guest OS anyway in a manner that is non-standard. */
01184                 WriteOut("32KB of RAM is required to boot a guest OS\n");
01185                 return;
01186             }
01187 
01188             /* Other versions of MS-DOS/PC-DOS have their own requirements about memory:
01189              *    - IBM PC-DOS 1.0/1.1: not too picky, will boot with as little as 32KB even though
01190              *                          it was intended for the average model with 64KB of RAM.
01191              *
01192              *    - IBM PC-DOS 2.1: requires at least 44KB of RAM. will crash on boot otherwise.
01193              *
01194              *    - MS-DOS 3.2: requires at least 64KB to boot without crashing, 80KB to make it
01195              *                  to the command line without halting at "configuration too big for
01196              *                  memory"*/
01197 
01198             /* TODO: Need a configuration option or a BOOT command option where the user can
01199              *       dictate where we put the stack: if we put it at 0x7000 or top of memory
01200              *       (default) or just below the boot sector, or... */
01201 
01202             if((bootarea.rawdata[0]==0) && (bootarea.rawdata[1]==0)) {
01203                 WriteOut_NoParsing("PROGRAM_BOOT_UNABLE");
01204                 return;
01205             }
01206 
01207             disable_umb_ems_xms();
01208 
01209             WriteOut(MSG_Get("PROGRAM_BOOT_BOOT"), drive);
01210 
01211             if (IS_PC98_ARCH) {
01212                 for(i=0;i<bootsize;i++) real_writeb(load_seg, i, bootarea.rawdata[i]);
01213             }
01214             else {
01215                 for(i=0;i<bootsize;i++) real_writeb(0, (load_seg<<4) + i, bootarea.rawdata[i]);
01216             }
01217 
01218             /* debug */
01219             LOG_MSG("Booting guest OS stack_seg=0x%04x load_seg=0x%04x\n",(int)stack_seg,(int)load_seg);
01220             RunningProgram = "Guest OS";
01221  
01222             /* WARNING: PC-98 mode does not allocate DMA channel 2 for the floppy! */
01223             /* create appearance of floppy drive DMA usage (Demon's Forge) */
01224             if (!IS_TANDY_ARCH && !IS_PC98_ARCH && floppysize!=0) GetDMAChannel(2)->tcount=true;
01225 
01226             /* standard method */
01227             if (IS_PC98_ARCH) {
01228                 /* Based on a CPU register dump at boot time on a real PC-9821:
01229                  *
01230                  * DUMP:
01231                  *
01232                  * SP: 00D8 SS: 0030 ES: 1FE0 DS: 0000 CS: 1FE0 FL: 0246 BP: 0000
01233                  * DI: 055C SI: 1FE0 DX: 0001 CX: 0200 BX: 0200 AX: 0030 IP: 0000
01234                  *
01235                  * So:
01236                  *
01237                  * Stack at 0030:00D8
01238                  *
01239                  * CS:IP at load_seg:0000
01240                  *
01241                  * load_seg at 0x1FE0 which on the original 128KB PC-98 puts it at the top of memory
01242                  *
01243                  */
01244                 SegSet16(cs, load_seg);
01245                 SegSet16(ds, 0x0000);
01246                 SegSet16(es, load_seg);
01247                 reg_ip = 0;
01248                 reg_ebx = 0x200;
01249                 reg_esp = 0xD8;
01250                 /* set up stack at a safe place */
01251                 SegSet16(ss, stack_seg);
01252                 reg_esi = load_seg;
01253                 reg_ecx = 0x200;
01254                 reg_ebp = 0;
01255                 reg_eax = 0x30;
01256                 reg_edx = 0x1;
01257 
01258                 /* Guess: If the boot sector is smaller than 512 bytes/sector, the PC-98 BIOS
01259                  *        probably sets the graphics layer to 640x200. Some games (Ys) do not
01260                  *        set but assume instead that is the mode of the graphics layer */
01261                 if (pc98_sect128) {
01262                     reg_eax = 0x4200;   // setup 640x200 graphics
01263                     reg_ecx = 0x8000;   // lower
01264                     CALLBACK_RunRealInt(0x18);
01265                 }
01266 
01267                 /* PC-98 MS-DOS boot sector behavior suggests that the BIOS does a CALL FAR
01268                  * to the boot sector, and the boot sector can RETF back to the BIOS on failure. */
01269                 CPU_Push16(BIOS_bootfail_code_offset >> 4); /* segment */
01270                 CPU_Push16(BIOS_bootfail_code_offset & 0xF); /* offset */
01271 
01272                 /* clear the text layer */
01273                 for (unsigned int i=0;i < (80*25*2);i += 2) {
01274                     mem_writew(0xA0000+i,0x0000);
01275                     mem_writew(0xA2000+i,0x00E1);
01276                 }
01277 
01278                 /* There is a byte at 0x584 that describes the boot drive + type.
01279                  * This is confirmed in Neko Project II source and by the behavior
01280                  * of an MS-DOS boot disk formatted by a PC-98 system.
01281                  *
01282                  * There are three values for three different floppy formats, and
01283                  * one for hard drives */
01284                 Bit32u heads,cyls,sects,ssize;
01285 
01286                 imageDiskList[drive-65]->Get_Geometry(&heads,&cyls,&sects,&ssize);
01287 
01288                 Bitu disk_equip = 0,disk_equip_144 = 0,scsi_equip = 0;
01289 
01290                 /* FIXME: MS-DOS appears to be able to see disk image B: but only
01291                  *        if the disk format is the same, for some reason.
01292                  *
01293                  *        So, apparently you cannot put a 1.44MB image in drive A:
01294                  *        and a 1.2MB image in drive B: */
01295 
01296                 for (unsigned int i=0;i < 2;i++) {
01297                     if (imageDiskList[i] != NULL) {
01298                         disk_equip |= (1u << i);
01299                         disk_equip_144 |= (1u << i);
01300                     }
01301                 }
01302 
01303                 for (unsigned int i=0;i < 2;i++) {
01304                     if (imageDiskList[i+2] != NULL) {
01305                         scsi_equip |= (1u << i);
01306 
01307                         Bitu m = 0x460u + (i * 4u);
01308 
01309                         mem_writeb(m+0,sects);
01310                         mem_writeb(m+1,heads);
01311                         mem_writew(m+2,(cyls & 0xFFFu) + (ssize == 512u ? 0x1000u : 0u) + (ssize == 1024u ? 0x2000u : 0) + 0x8000u/*NP2:hwsec*/);
01312                     }
01313                 }
01314 
01315                 if ((ssize == 1024 && heads == 2 && cyls == 77 && sects == 8) || pc98_sect128) {
01316                     mem_writeb(0x584,0x90/*type*/ + (drive - 65)/*drive*/); /* 1.2MB 3-mode */
01317                     mem_writew(0x55C,disk_equip);   /* disk equipment (drive 0 is present) */
01318                     mem_writew(0x5AE,disk_equip_144);   /* disk equipment (drive 0 is present, 1.44MB) */
01319                     mem_writeb(0x482,scsi_equip);
01320                 }
01321                 else if (ssize == 512 && heads == 2 && cyls == 80 && sects == 18) {
01322                     mem_writeb(0x584,0x30/*type*/ + (drive - 65)/*drive*/); /* 1.44MB */
01323                     mem_writew(0x55C,disk_equip);   /* disk equipment (drive 0 is present) */
01324                     mem_writew(0x5AE,disk_equip_144);   /* disk equipment (drive 0 is present, 1.44MB) */
01325                     mem_writeb(0x482,scsi_equip);
01326                 }
01327                 else if (drive >= 'C') {
01328                     /* hard drive */
01329                     mem_writeb(0x584,0xA0/*type*/ + (drive - 'C')/*drive*/);
01330                     mem_writew(0x55C,disk_equip);   /* disk equipment (drive 0 is present) */
01331                     mem_writew(0x5AE,disk_equip_144);   /* disk equipment (drive 0 is present, 1.44MB) */
01332                     mem_writeb(0x482,scsi_equip);
01333                 }
01334                 else {
01335                     // FIXME
01336                     mem_writeb(0x584,0x00);
01337                     mem_writew(0x55C,disk_equip);   /* disk equipment (drive 0 is present) */
01338                     mem_writew(0x5AE,disk_equip_144);   /* disk equipment (drive 0 is present, 1.44MB) */
01339                     mem_writeb(0x482,scsi_equip);
01340                 }
01341             }
01342             else {
01343                 SegSet16(cs, 0);
01344                 SegSet16(ds, 0);
01345                 SegSet16(es, 0);
01346                 reg_ip = load_seg<<4;
01347                 reg_ebx = load_seg<<4; //Real code probably uses bx to load the image
01348                 reg_esp = 0x100;
01349                 /* set up stack at a safe place */
01350                 SegSet16(ss, stack_seg);
01351                 reg_esi = 0;
01352                 reg_ecx = 1;
01353                 reg_ebp = 0;
01354                 reg_eax = 0;
01355                 reg_edx = 0; //Head 0
01356                 if (drive >= 'A' && drive <= 'B')
01357                     reg_edx += (unsigned int)(drive-'A');
01358                 else if (drive >= 'C' && drive <= 'Z')
01359                     reg_edx += 0x80u+(unsigned int)(drive-'C');
01360             }
01361 #ifdef __WIN32__
01362             // let menu know it boots
01363             menu.boot=true;
01364 #endif
01365 
01366             /* forcibly exit the shell, the DOS kernel, and anything else by throwing an exception */
01367             throw int(2);
01368         }
01369     }
01370 };
01371 
01372 static void BOOT_ProgramStart(Program * * make) {
01373     *make=new BOOT;
01374 }
01375 
01376 class LDGFXROM : public Program {
01377 public:
01378     void Run(void) {
01379         if (!(cmd->FindCommand(1, temp_line))) return;
01380 
01381         Bit8u drive;
01382         char fullname[DOS_PATHLENGTH];
01383 
01384         localDrive* ldp=0;
01385         if (!DOS_MakeName((char *)temp_line.c_str(),fullname,&drive)) return;
01386 
01387         try {       
01388             ldp=dynamic_cast<localDrive*>(Drives[drive]);
01389             if(!ldp) return;
01390 
01391             FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
01392             if(tmpfile == NULL) {
01393                 LOG_MSG("BIOS file not accessible.");
01394                 return;
01395             }
01396             fseek(tmpfile, 0L, SEEK_END);
01397             if (ftell(tmpfile)>0x10000) {
01398                 LOG_MSG("BIOS file too large.");
01399                 return;
01400             }
01401             fseek(tmpfile, 0L, SEEK_SET);
01402 
01403             PhysPt rom_base=PhysMake(0xc000,0);
01404 
01405             Bit8u vga_buffer[0x10000];
01406             Bitu data_written=0;
01407             Bitu data_read = (Bitu)fread(vga_buffer, 1, 0x10000, tmpfile);
01408             for (Bitu ct=0; ct<data_read; ct++) {
01409                 phys_writeb(rom_base+(data_written++),vga_buffer[ct]);
01410             }
01411             fclose(tmpfile);
01412 
01413             rom_base=PhysMake(0xf000,0);
01414             phys_writeb(rom_base+0xf065,0xcf);
01415         }
01416         catch(...) {
01417             return;
01418         }
01419 
01420         reg_flags&=~FLAG_IF;
01421         CALLBACK_RunRealFar(0xc000,0x0003);
01422     }
01423 };
01424 
01425 static void LDGFXROM_ProgramStart(Program * * make) {
01426     *make=new LDGFXROM;
01427 }
01428 
01429 const Bit8u freedos_mbr[] = {
01430     0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0x8E,0xD0,0xBC,0x00,0x7C,0xFC,0x8B,0xF4,0xBF,0x00, 
01431     0x06,0xB9,0x00,0x01,0xF2,0xA5,0xEA,0x67,0x06,0x00,0x00,0x8B,0xD5,0x58,0xA2,0x4F, // 10h
01432     0x07,0x3C,0x35,0x74,0x23,0xB4,0x10,0xF6,0xE4,0x05,0xAE,0x04,0x8B,0xF0,0x80,0x7C, // 20h
01433     0x04,0x00,0x74,0x44,0x80,0x7C,0x04,0x05,0x74,0x3E,0xC6,0x04,0x80,0xE8,0xDA,0x00, 
01434     0x8A,0x74,0x01,0x8B,0x4C,0x02,0xEB,0x08,0xE8,0xCF,0x00,0xB9,0x01,0x00,0x32,0xD1, // 40h
01435     0xBB,0x00,0x7C,0xB8,0x01,0x02,0xCD,0x13,0x72,0x1E,0x81,0xBF,0xFE,0x01,0x55,0xAA, 
01436     0x75,0x16,0xEA,0x00,0x7C,0x00,0x00,0x80,0xFA,0x81,0x74,0x02,0xB2,0x80,0x8B,0xEA, 
01437     0x42,0x80,0xF2,0xB3,0x88,0x16,0x41,0x07,0xBF,0xBE,0x07,0xB9,0x04,0x00,0xC6,0x06, 
01438     0x34,0x07,0x31,0x32,0xF6,0x88,0x2D,0x8A,0x45,0x04,0x3C,0x00,0x74,0x23,0x3C,0x05, // 80h
01439     0x74,0x1F,0xFE,0xC6,0xBE,0x31,0x07,0xE8,0x71,0x00,0xBE,0x4F,0x07,0x46,0x46,0x8B, 
01440     0x1C,0x0A,0xFF,0x74,0x05,0x32,0x7D,0x04,0x75,0xF3,0x8D,0xB7,0x7B,0x07,0xE8,0x5A, 
01441     0x00,0x83,0xC7,0x10,0xFE,0x06,0x34,0x07,0xE2,0xCB,0x80,0x3E,0x75,0x04,0x02,0x74, 
01442     0x0B,0xBE,0x42,0x07,0x0A,0xF6,0x75,0x0A,0xCD,0x18,0xEB,0xAC,0xBE,0x31,0x07,0xE8, 
01443     0x39,0x00,0xE8,0x36,0x00,0x32,0xE4,0xCD,0x1A,0x8B,0xDA,0x83,0xC3,0x60,0xB4,0x01, 
01444     0xCD,0x16,0xB4,0x00,0x75,0x0B,0xCD,0x1A,0x3B,0xD3,0x72,0xF2,0xA0,0x4F,0x07,0xEB, 
01445     0x0A,0xCD,0x16,0x8A,0xC4,0x3C,0x1C,0x74,0xF3,0x04,0xF6,0x3C,0x31,0x72,0xD6,0x3C, 
01446     0x35,0x77,0xD2,0x50,0xBE,0x2F,0x07,0xBB,0x1B,0x06,0x53,0xFC,0xAC,0x50,0x24,0x7F, //100h
01447     0xB4,0x0E,0xCD,0x10,0x58,0xA8,0x80,0x74,0xF2,0xC3,0x56,0xB8,0x01,0x03,0xBB,0x00, //110h
01448     0x06,0xB9,0x01,0x00,0x32,0xF6,0xCD,0x13,0x5E,0xC6,0x06,0x4F,0x07,0x3F,0xC3,0x0D, //120h
01449     0x8A,0x0D,0x0A,0x46,0x35,0x20,0x2E,0x20,0x2E,0x20,0x2E,0xA0,0x64,0x69,0x73,0x6B, 
01450     0x20,0x32,0x0D,0x0A,0x0A,0x44,0x65,0x66,0x61,0x75,0x6C,0x74,0x3A,0x20,0x46,0x31, //140h
01451     0xA0,0x00,0x01,0x00,0x04,0x00,0x06,0x03,0x07,0x07,0x0A,0x0A,0x63,0x0E,0x64,0x0E, 
01452     0x65,0x14,0x80,0x19,0x81,0x19,0x82,0x19,0x83,0x1E,0x93,0x24,0xA5,0x2B,0x9F,0x2F, 
01453     0x75,0x33,0x52,0x33,0xDB,0x36,0x40,0x3B,0xF2,0x41,0x00,0x44,0x6F,0xF3,0x48,0x70, 
01454     0x66,0xF3,0x4F,0x73,0xB2,0x55,0x6E,0x69,0xF8,0x4E,0x6F,0x76,0x65,0x6C,0xEC,0x4D, //180h
01455     0x69,0x6E,0x69,0xF8,0x4C,0x69,0x6E,0x75,0xF8,0x41,0x6D,0x6F,0x65,0x62,0xE1,0x46, 
01456     0x72,0x65,0x65,0x42,0x53,0xC4,0x42,0x53,0x44,0xE9,0x50,0x63,0x69,0xF8,0x43,0x70, 
01457     0xED,0x56,0x65,0x6E,0x69,0xF8,0x44,0x6F,0x73,0x73,0x65,0xE3,0x3F,0xBF,0x00,0x00, //1B0h
01458     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
01459     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
01460     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
01461     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA
01462     };
01463 #ifdef WIN32
01464 #include <winioctl.h>
01465 #endif
01466 
01467 class IMGMAKE : public Program {
01468 public:
01469 #ifdef WIN32
01470     bool OpenDisk(HANDLE* f, OVERLAPPED* o, Bit8u* name) {
01471         o->hEvent = INVALID_HANDLE_VALUE;
01472         *f = CreateFile( (LPCSTR)name, GENERIC_READ | GENERIC_WRITE,
01473             0,    // exclusive access 
01474             NULL, // default security attributes 
01475             OPEN_EXISTING,
01476             FILE_FLAG_OVERLAPPED,
01477             NULL );
01478 
01479         if (*f == INVALID_HANDLE_VALUE) return false;
01480 
01481         // init OVERLAPPED 
01482         o->Internal = 0;
01483         o->InternalHigh = 0;
01484         o->Offset = 0;
01485         o->OffsetHigh = 0;
01486         o->hEvent = CreateEvent(
01487             NULL,   // default security attributes 
01488             TRUE,   // manual-reset event 
01489             FALSE,  // not signaled 
01490             NULL    // no name
01491             );
01492         return true;
01493     }
01494 
01495     void CloseDisk(HANDLE f, OVERLAPPED* o) {
01496         if(f != INVALID_HANDLE_VALUE) CloseHandle(f);
01497         if(o->hEvent != INVALID_HANDLE_VALUE) CloseHandle(o->hEvent);
01498     }
01499 
01500     bool StartReadDisk(HANDLE f, OVERLAPPED* o, Bit8u* buffer, Bitu offset, Bitu size) { 
01501         o->Offset = offset;
01502         if (!ReadFile(f, buffer, size, NULL, o) && 
01503             (GetLastError()==ERROR_IO_PENDING)) return true;
01504         return false;
01505     }
01506 
01507     // 0=still waiting, 1=catastrophic faliure, 2=success, 3=sector not found, 4=crc error 
01508     Bitu CheckDiskReadComplete(HANDLE f, OVERLAPPED* o) {
01509         DWORD numret;
01510         BOOL b = GetOverlappedResult( f, o, &numret,false); 
01511         if(b) return 2;
01512         else {
01513             int error = GetLastError();
01514             if(error==ERROR_IO_INCOMPLETE)          return 0;
01515             if(error==ERROR_FLOPPY_UNKNOWN_ERROR)   return 5;
01516             if(error==ERROR_CRC)                    return 4;
01517             if(error==ERROR_SECTOR_NOT_FOUND)       return 3;
01518             return 1;   
01519         }
01520     }
01521 
01522     Bitu ReadDisk(FILE* f, Bit8u driveletter, Bitu retries_max) {
01523         unsigned char data[36*2*512];
01524         HANDLE hFloppy;
01525         DWORD numret;
01526         OVERLAPPED o;
01527         DISK_GEOMETRY geom;
01528 
01529         Bit8u drivestring[] = "\\\\.\\x:"; drivestring[4]=driveletter;
01530         if(!OpenDisk(&hFloppy, &o, drivestring)) return false;
01531 
01532         // get drive geom
01533         DeviceIoControl( hFloppy, IOCTL_DISK_GET_DRIVE_GEOMETRY,NULL,0,
01534         &geom,sizeof(DISK_GEOMETRY),&numret,NULL);
01535 
01536         switch(geom.MediaType) {
01537             case F5_1Pt2_512: case F3_1Pt44_512: case F3_2Pt88_512: case F3_720_512:
01538             case F5_360_512: case F5_320_512: case F5_180_512: case F5_160_512:
01539                 break;
01540             default:
01541                 CloseDisk(hFloppy,&o);
01542                 return false;
01543         }
01544         Bitu total_sect_per_cyl = geom.SectorsPerTrack * geom.TracksPerCylinder;
01545         Bitu cyln_size = 512 * total_sect_per_cyl;
01546         
01547         WriteOut(MSG_Get("PROGRAM_IMGMAKE_FLREAD"),
01548             geom.Cylinders.LowPart,geom.TracksPerCylinder,
01549             geom.SectorsPerTrack,(cyln_size*geom.Cylinders.LowPart)/1024);
01550         WriteOut(MSG_Get("PROGRAM_IMGMAKE_FLREAD2"));
01551             
01552         for(Bitu i = 0; i < geom.Cylinders.LowPart; i++) {
01553             Bitu result;
01554             // for each cylinder
01555             WriteOut("%2u",i);
01556 
01557             if(!StartReadDisk(hFloppy, &o, &data[0], cyln_size*i, cyln_size)){
01558                 CloseDisk(hFloppy,&o);
01559                 return false;
01560             }
01561             do {
01562                 result = CheckDiskReadComplete(hFloppy, &o);
01563                 CALLBACK_Idle();
01564             }
01565             while (result==0);
01566 
01567             switch(result) {
01568             case 1:
01569                 CloseDisk(hFloppy,&o);
01570                 return false;
01571             case 2: // success
01572                 for(Bitu m=0; m < cyln_size/512; m++) WriteOut("\xdb");
01573                 break;
01574             case 3:
01575             case 4: // data errors
01576             case 5:
01577                 for(Bitu k=0; k < total_sect_per_cyl; k++) {
01578                     Bitu retries=retries_max;
01579 restart_int:
01580                     StartReadDisk(hFloppy, &o, &data[512*k], cyln_size*i + 512*k, 512);
01581                     do {
01582                         result = CheckDiskReadComplete(hFloppy, &o);
01583                         CALLBACK_Idle();
01584                     }
01585                     while (result==0);
01586                                         
01587                     switch(result) {
01588                     case 1: // bad error
01589                         CloseDisk(hFloppy,&o);
01590                         return false;
01591                     case 2: // success
01592                         if(retries==retries_max) WriteOut("\xdb");
01593                         else WriteOut("\b\b\b\xb1");
01594                         break;
01595                     case 3:
01596                     case 4: // read errors
01597                     case 5:
01598                         if(retries!=retries_max) WriteOut("\b\b\b");
01599                         retries--;
01600                         switch(result) {
01601                             case 3: WriteOut("x");
01602                             case 4: WriteOut("!");
01603                             case 5: WriteOut("?");
01604                         }
01605                         WriteOut("%2d",retries);
01606 
01607                         if(retries) goto restart_int;
01608                         const Bit8u badfood[]="IMGMAKE BAD FLOPPY SECTOR \xBA\xAD\xF0\x0D";
01609                         for(Bitu z = 0; z < 512/32; z++)
01610                             memcpy(&data[512*k+z*32],badfood,32);
01611                         WriteOut("\b\b");
01612                         break;
01613                     }
01614                 }
01615                 break;
01616             }
01617             fwrite(data, 512, total_sect_per_cyl, f);
01618             WriteOut("%2x%2x\n", data[0], data[1]);
01619         }
01620         // seek to 0
01621         StartReadDisk(hFloppy, &o, &data[0], 0, 512);
01622         CloseDisk(hFloppy,&o);
01623         return true;
01624     }
01625 #endif
01626 
01627     void Run(void) {
01628         std::string disktype;
01629         std::string src;
01630         std::string filename;
01631         std::string path = "";
01632         std::string dpath;
01633 
01634         unsigned int c, h, s, sectors; 
01635         Bit64u size = 0;
01636 
01637         if(cmd->FindExist("-?")) {
01638             printHelp();
01639             return;
01640         }
01641 
01642 /*
01643         this stuff is too frustrating
01644 
01645         // when only a filename is passed try to create the file on the current DOS path
01646         // if directory+filename are passed first see if directory is a host path, if not
01647         // maybe it is a DOSBox path.
01648         
01649         // split filename and path
01650         Bitu spos = temp_line.rfind('\\');
01651         if(spos==std::string::npos) {
01652             temp_line.rfind('/');
01653         }
01654 
01655         if(spos==std::string::npos) {
01656             // no path separator
01657             filename=temp_line;
01658         } else {
01659             path=temp_line.substr(0,spos);
01660             filename=temp_line.substr(spos+1,std::string::npos);
01661         }
01662         if(filename=="") 
01663 
01664         char tbuffer[DOS_PATHLENGTH]= { 0 };
01665         if(path=="") {
01666             if(!DOS_GetCurrentDir(DOS_GetDefaultDrive()+1,tbuffer)){
01667                 printHelp();
01668                 return;
01669             }
01670             dpath=(std::string)tbuffer;
01671         }       
01672         WriteOut("path %s, filename %s, dpath %s",
01673             path.c_str(),filename.c_str(),dpath.c_str());
01674         return;
01675 */
01676             
01677 #ifdef WIN32
01678         // read from real floppy?
01679         if(cmd->FindString("-source",src,true)) {
01680             int retries = 10;
01681             cmd->FindInt("-retries",retries,true);
01682             if((retries < 1)||(retries > 99))  {
01683                 printHelp();
01684                 return;
01685             }
01686             if((src.length()!=1) || !isalpha(src.c_str()[0])) {
01687                 // only one letter allowed
01688                 printHelp();
01689                 return;
01690             }
01691 
01692             // temp_line is the filename
01693             if (!(cmd->FindCommand(1, temp_line))) {
01694                 printHelp();
01695                 return;
01696             }
01697 
01698             // don't trash user's files
01699             FILE* f = fopen(temp_line.c_str(),"r");
01700             if(f) {
01701                 fclose(f);
01702                 WriteOut(MSG_Get("PROGRAM_IMGMAKE_FILE_EXISTS"),temp_line.c_str());
01703                 return;
01704             }
01705             f = fopen(temp_line.c_str(),"wb+");
01706             if (!f) {
01707                 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),temp_line.c_str());
01708                 return;
01709             }
01710             // maybe delete f if it failed?
01711             if(!ReadDisk(f, src.c_str()[0],retries))
01712                 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANT_READ_FLOPPY"));
01713             fclose(f);
01714             return;
01715         }
01716 #endif
01717         // disk type
01718         if (!(cmd->FindString("-t",disktype,true))) {
01719             printHelp();
01720             return;
01721         }
01722 
01723         Bit8u mediadesc = 0xF8; // media descriptor byte; also used to differ fd and hd
01724         Bitu root_ent = 512; // FAT root directory entries: 512 is for harddisks
01725         if(disktype=="fd_160") {
01726             c = 40; h = 1; s = 8; mediadesc = 0xFE; root_ent = 56; // root_ent?
01727         } else if(disktype=="fd_180") {
01728             c = 40; h = 1; s = 9; mediadesc = 0xFC; root_ent = 56; // root_ent?
01729         } else if(disktype=="fd_200") {
01730             c = 40; h = 1; s = 10; mediadesc = 0xFC; root_ent = 56; // root_ent?
01731         } else if(disktype=="fd_320") {
01732             c = 40; h = 2; s = 8; mediadesc = 0xFF; root_ent = 112; // root_ent?
01733         } else if(disktype=="fd_360") {
01734             c = 40; h = 2; s = 9; mediadesc = 0xFD; root_ent = 112;
01735         } else if(disktype=="fd_400") {
01736             c = 40; h = 2; s = 10; mediadesc = 0xFD; root_ent = 112; // root_ent?
01737         } else if(disktype=="fd_720") {
01738             c = 80; h = 2; s = 9; mediadesc = 0xF9; root_ent = 112;
01739         } else if(disktype=="fd_1200") {
01740             c = 80; h = 2; s = 15; mediadesc = 0xF9; root_ent = 224;
01741         } else if(disktype=="fd_1440") {
01742             c = 80; h = 2; s = 18; mediadesc = 0xF0; root_ent = 224;
01743         } else if(disktype=="fd_2880") {
01744             c = 80; h = 2; s = 36; mediadesc = 0xF0; root_ent = 512; // root_ent?
01745         } else if(disktype=="hd_250") {
01746             c = 489; h = 16; s = 63;
01747         } else if(disktype=="hd_520") {
01748             c = 1023; h = 16; s = 63;
01749         } else if(disktype=="hd_2gig") {
01750             c = 1023; h = 64; s = 63;
01751         } else if(disktype=="hd_4gig") { // fseek only supports 2gb
01752             c = 1023; h = 130; s = 63;
01753         } else if(disktype=="hd_8gig") { // fseek only supports 2gb
01754             c = 1023; h = 255; s = 63;
01755         } else if(disktype=="hd_st251") { // old 40mb drive
01756             c = 820; h = 6; s = 17;
01757         } else if(disktype=="hd_st225") { // even older 20mb drive
01758             c = 615; h = 4; s = 17;
01759         } else if(disktype=="hd") {
01760             // get size from parameter
01761             std::string isize;
01762             if (!(cmd->FindString("-size",isize,true))) {
01763                 // maybe -chs?
01764                 if (!(cmd->FindString("-chs",isize,true))){
01765                         // user forgot -size and -chs
01766                         printHelp();
01767                         return;
01768                     }
01769                 else {
01770                     // got chs data: -chs 1023,16,63
01771                     if(sscanf(isize.c_str(),"%u,%u,%u",&c,&h,&s) != 3) {
01772                         printHelp();
01773                         return;
01774                     }
01775                     // sanity-check chs values
01776                     if((h>255)||(c>1023)||(s>63)) {
01777                         printHelp();
01778                         return;
01779                     }
01780                     size = (unsigned long long)c * (unsigned long long)h * (unsigned long long)s * 512ULL;
01781                     if((size < 3u*1024u*1024u) || (size > 0x1FFFFFFFFLL)) {
01782                         // user picked geometry resulting in wrong size
01783                         printHelp();
01784                         return;
01785                     }
01786                 }
01787             } else {
01788                 // got -size
01789                 std::istringstream stream(isize);
01790                 stream >> size;
01791                 size *= 1024*1024LL; // size in megabytes
01792                 // low limit: 3 megs, high limit: 2 gigs
01793                 // Int13 limit would be 8 gigs
01794                 if((size < 3*1024*1024LL) || (size > 0x1FFFFFFFFLL)) {
01795                     // wrong size
01796                     printHelp();
01797                     return;
01798                 }
01799                 sectors = (Bitu)(size / 512);
01800 
01801                 // Now that we finally have the proper size, figure out good CHS values
01802                 h=2;
01803                 while(h*1023*63 < sectors) h <<= 1;
01804                 if(h>255) h=255;
01805                 s=8;
01806                 while(h*s*1023 < sectors) s *= 2;
01807                 if(s>63) s=63;
01808                 c=sectors/(h*s);
01809                 if(c>1023) c=1023;
01810             }
01811         } else {
01812             // user passed a wrong -t argument
01813             printHelp();
01814             return;
01815         }
01816 
01817         std::string t2 = "";
01818         if(cmd->FindExist("-bat",true)) {
01819             t2 = "-bat";
01820         }
01821 
01822         size = (unsigned long long)c * (unsigned long long)h * (unsigned long long)s * 512ULL;
01823         Bits bootsect_pos = 0; // offset of the boot sector in clusters
01824         if(cmd->FindExist("-nofs",true) || (size>(2048*1024*1024LL))) {
01825             bootsect_pos = -1;
01826         }
01827 
01828         // temp_line is the filename
01829         if (!(cmd->FindCommand(1, temp_line))) {
01830             printHelp();
01831             return;
01832         }
01833 
01834         // don't trash user's files
01835         FILE* f = fopen(temp_line.c_str(),"r");
01836         if(f) {
01837             fclose(f);
01838             WriteOut(MSG_Get("PROGRAM_IMGMAKE_FILE_EXISTS"),temp_line.c_str());
01839             return;
01840         }
01841 
01842         WriteOut(MSG_Get("PROGRAM_IMGMAKE_PRINT_CHS"),c,h,s);
01843         WriteOut("%s\r\n",temp_line.c_str());
01844         LOG_MSG(MSG_Get("PROGRAM_IMGMAKE_PRINT_CHS"),c,h,s);
01845 
01846         // do it again for fixed chs values
01847         sectors = (Bitu)(size / 512);
01848 
01849         // create the image file
01850         f = fopen64(temp_line.c_str(),"wb+");
01851         if (!f) {
01852             WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),temp_line.c_str());
01853             return;
01854         }
01855         if(fseeko64(f,static_cast<unsigned long long>(size - 1ull),SEEK_SET)) {
01856             WriteOut(MSG_Get("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE"),size);
01857             return;
01858         }
01859         Bit8u bufferbyte=0;
01860         if(fwrite(&bufferbyte,1,1,f)!=1) {
01861             WriteOut(MSG_Get("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE"),size);
01862             return;
01863         }
01864 
01865         // Format the image if not unrequested (and image size<2GB)
01866         if(bootsect_pos > -1) {
01867             Bit8u sbuf[512];
01868             if(mediadesc == 0xF8) {
01869                 // is a harddisk: write MBR
01870                 memcpy(sbuf,freedos_mbr,512);
01871                 // active partition
01872                 sbuf[0x1be]=0x80;
01873                 // start head - head 0 has the partition table, head 1 first partition
01874                 sbuf[0x1bf]=1;
01875                 // start sector with bits 8-9 of start cylinder in bits 6-7
01876                 sbuf[0x1c0]=1;
01877                 // start cylinder bits 0-7
01878                 sbuf[0x1c1]=0;
01879                 // OS indicator: DOS what else ;)
01880                 sbuf[0x1c2]=0x06;
01881                 // end head (0-based)
01882                 sbuf[0x1c3]= h-1;
01883                 // end sector with bits 8-9 of end cylinder (0-based) in bits 6-7
01884                 sbuf[0x1c4]=s|(((c-1)&0x300)>>2);
01885                 // end cylinder (0-based) bits 0-7
01886                 sbuf[0x1c5]=(c-1)&0xFF;
01887                 // sectors preceding partition1 (one head)
01888                 host_writed(&sbuf[0x1c6],s);
01889                 // length of partition1, align to chs value
01890                 host_writed(&sbuf[0x1ca],((c-1)*h+(h-1))*s);
01891 
01892                 // write partition table
01893                 fseeko64(f,0,SEEK_SET);
01894                 fwrite(&sbuf,512,1,f);
01895                 bootsect_pos = s;
01896             }
01897 
01898             // set boot sector values
01899             memset(sbuf,0,512);
01900             // TODO boot code jump
01901             sbuf[0]=0xEB; sbuf[1]=0x3c; sbuf[2]=0x90;
01902             // OEM
01903             sprintf((char*)&sbuf[0x03],"MSDOS5.0");
01904             // bytes per sector: always 512
01905             host_writew(&sbuf[0x0b],512);
01906             // sectors per cluster: 1,2,4,8,16,...
01907             if(mediadesc == 0xF8) {
01908                 Bitu cval = 1;
01909                 while((sectors/cval) >= 65525) cval <<= 1;
01910                 sbuf[0x0d]=cval;
01911             } else sbuf[0x0d]=sectors/0x1000 + 1; // FAT12 can hold 0x1000 entries TODO
01912             // TODO small floppys have 2 sectors per cluster?
01913             // reserverd sectors: 1 ( the boot sector)
01914             host_writew(&sbuf[0x0e],1);
01915             // Number of FATs - always 2
01916             sbuf[0x10] = 2;
01917             // Root entries - how are these made up? - TODO
01918             host_writew(&sbuf[0x11],root_ent);
01919             // sectors (under 32MB) - will OSes be sore if all HD's use large size?
01920             if(mediadesc != 0xF8) host_writew(&sbuf[0x13],c*h*s);
01921             // media descriptor
01922             sbuf[0x15]=mediadesc;
01923             // sectors per FAT
01924             // needed entries: (sectors per cluster)
01925             Bitu sect_per_fat=0;
01926             Bitu clusters = (sectors-1)/sbuf[0x0d]; // TODO subtract root dir too maybe
01927             if(mediadesc == 0xF8) sect_per_fat = (clusters*2)/512+1;
01928             else sect_per_fat = ((clusters*3)/2)/512+1;
01929             host_writew(&sbuf[0x16],sect_per_fat);
01930             // sectors per track
01931             host_writew(&sbuf[0x18],s);
01932             // heads
01933             host_writew(&sbuf[0x1a],h);
01934             // hidden sectors
01935             host_writed(&sbuf[0x1c],bootsect_pos);
01936             // sectors (large disk) - this is the same as partition length in MBR
01937             if(mediadesc == 0xF8) host_writed(&sbuf[0x20],sectors-s);
01938             // BIOS drive
01939             if(mediadesc == 0xF8) sbuf[0x24]=0x80;
01940             else sbuf[0x24]=0x00;
01941             // ext. boot signature
01942             sbuf[0x26]=0x29;
01943             // volume serial number
01944             // let's use the BIOS time (cheap, huh?)
01945             host_writed(&sbuf[0x27],mem_readd(BIOS_TIMER));
01946             // Volume label
01947             sprintf((char*)&sbuf[0x2b],"NO NAME    ");
01948             // file system type
01949             if(mediadesc == 0xF8) sprintf((char*)&sbuf[0x36],"FAT16   ");
01950             else sprintf((char*)&sbuf[0x36],"FAT12   ");
01951             // boot sector signature
01952             host_writew(&sbuf[0x1fe],0xAA55);
01953 
01954             // write the boot sector
01955             fseeko64(f,bootsect_pos*512,SEEK_SET);
01956             fwrite(&sbuf,512,1,f);
01957             // write FATs
01958             memset(sbuf,0,512);
01959             if(mediadesc == 0xF8) host_writed(&sbuf[0],0xFFFFFFF8);
01960             else host_writed(&sbuf[0],0xFFFFF0);
01961             // 1st FAT
01962             fseeko64(f,(off_t)((bootsect_pos+1ll)*512ll),SEEK_SET);
01963             fwrite(&sbuf,512,1,f);
01964             // 2nd FAT
01965             fseeko64(f,(off_t)(((unsigned long long)bootsect_pos+1ull+(unsigned long long)sect_per_fat)*512ull),SEEK_SET);
01966             fwrite(&sbuf,512,1,f);
01967         }
01968         // write VHD footer if requested, largely copied from RAW2VHD program, no license was included
01969         if((mediadesc = 0xF8) && (temp_line.find(".vhd"))) {
01970             int i;
01971             Bit8u footer[512];
01972             // basic information
01973             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);
01974             memset(footer+40,0,512-40);
01975             // time
01976             struct tm tm20000101 = { /*sec*/0,/*min*/0,/*hours*/0, /*day of month*/1,/*month*/0,/*year*/100, /*wday*/0,/*yday*/0,/*isdst*/0 };
01977             time_t basetime = mktime(&tm20000101);
01978             time_t vhdtime = time(NULL) - basetime;
01979 #if defined (_MSC_VER)
01980             *(Bit32u*)(footer+0x18) = SDL_SwapBE32((__time32_t)vhdtime);
01981 #else
01982             *(Bit32u*)(footer+0x18) = SDL_SwapBE32((long)vhdtime);
01983 #endif
01984             // size and geometry
01985             *(Bit64u*)(footer+0x30) = *(Bit64u*)(footer+0x28) = SDL_SwapBE64(size);
01986 
01987             *(Bit16u*)(footer+0x38) = SDL_SwapBE16(c);
01988             *(Bit8u*)( footer+0x3A) = h;
01989             *(Bit8u*)( footer+0x3B) = s;
01990             *(Bit32u*)(footer+0x3C) = SDL_SwapBE32(2);
01991 
01992             // generate UUID
01993             for (i=0; i<16; ++i) {
01994                 *(footer+0x44+i) = (Bit8u)(rand()>>4);
01995             }
01996 
01997             // calculate checksum
01998             Bit32u sum;
01999             for (i=0,sum=0; i<512; ++i) {
02000                 sum += footer[i];
02001             }
02002 
02003             *(Bit32u*)(footer+0x40) = SDL_SwapBE32(~sum);
02004 
02005             // write footer
02006             fseeko64(f, 0L, SEEK_END);
02007             fwrite(&footer,512,1,f);
02008         }
02009         fclose(f);
02010 
02011         // create the batch file
02012         if(t2 == "-bat") {
02013             if(temp_line.length() > 3) {
02014                 t2 = temp_line.substr(0,temp_line.length()-4);
02015                 t2 = t2.append(".bat");
02016             } else {
02017                 t2 = temp_line.append(".bat");
02018             }
02019             WriteOut("%s\n",t2.c_str());
02020             f = fopen(t2.c_str(),"wb+");
02021             if (!f) {
02022                 WriteOut(MSG_Get("PROGRAM_IMGMAKE_CANNOT_WRITE"),t2.c_str());
02023                 return;
02024             }
02025             fprintf(f,"imgmount c %s -size 512,%u,%u,%u\r\n",temp_line.c_str(),s,h,c);
02026             fclose(f);
02027         }
02028         return;
02029     }
02030     void printHelp() { // maybe hint parameter?
02031         WriteOut(MSG_Get("PROGRAM_IMGMAKE_SYNTAX"));
02032     }
02033 };
02034 
02035 static void IMGMAKE_ProgramStart(Program * * make) {
02036     *make=new IMGMAKE;
02037 }
02038 
02039 // LOADFIX
02040 
02041 class LOADFIX : public Program {
02042 public:
02043     void Run(void);
02044 };
02045 
02046 bool XMS_Active(void);
02047 Bitu XMS_AllocateMemory(Bitu size, Bit16u& handle);
02048 
02049 void LOADFIX::Run(void) 
02050 {
02051     Bit16u commandNr    = 1;
02052     Bitu kb             = 64;
02053     bool xms            = false;
02054 
02055     if (cmd->FindExist("-xms",true)) {
02056         xms = true;
02057         kb = 1024;
02058     }
02059 
02060     if (cmd->FindExist("-?", false)) {
02061         WriteOut(MSG_Get("PROGRAM_LOADFIX_HELP"));
02062         return;
02063     }
02064 
02065     if (cmd->FindCommand(commandNr,temp_line)) {
02066         if (temp_line[0]=='-') {
02067             char ch = temp_line[1];
02068             if ((*upcase(&ch)=='D') || (*upcase(&ch)=='F')) {
02069                 // Deallocate all
02070                 if (xms) {
02071                     WriteOut("XMS deallocation not yet implemented\n");
02072                 }
02073                 else {
02074                     DOS_FreeProcessMemory(0x40);
02075                     WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOCALL"),kb);
02076                 }
02077                 return;
02078             } else {
02079                 // Set mem amount to allocate
02080                 kb = (Bitu)atoi(temp_line.c_str()+1);
02081                 if (kb==0) kb=xms?1024:64;
02082                 commandNr++;
02083             }
02084         }
02085     }
02086 
02087     // Allocate Memory
02088     if (xms) {
02089         if (XMS_Active()) {
02090             Bit16u handle;
02091             Bitu err;
02092 
02093             err = XMS_AllocateMemory(kb,/*&*/handle);
02094             if (err == 0) {
02095                 WriteOut("XMS block allocated (%uKB)\n",kb);
02096             }
02097             else {
02098                 WriteOut("Unable to allocate XMS block\n");
02099             }
02100         }
02101         else {
02102             WriteOut("XMS not active\n");
02103         }
02104     }
02105     else {
02106         Bit16u segment;
02107         Bit16u blocks = kb*1024/16;
02108         if (DOS_AllocateMemory(&segment,&blocks)) {
02109             DOS_MCB mcb((Bit16u)(segment-1));
02110             mcb.SetPSPSeg(0x40);            // use fake segment
02111             WriteOut(MSG_Get("PROGRAM_LOADFIX_ALLOC"),kb);
02112             // Prepare commandline...
02113             if (cmd->FindCommand(commandNr++,temp_line)) {
02114                 // get Filename
02115                 char filename[128];
02116                 safe_strncpy(filename,temp_line.c_str(),128);
02117                 // Setup commandline
02118                 bool ok;
02119                 char args[256];
02120                 args[0] = 0;
02121                 do {
02122                     ok = cmd->FindCommand(commandNr++,temp_line);
02123                     if(sizeof(args)-strlen(args)-1 < temp_line.length()+1)
02124                         break;
02125                     strcat(args,temp_line.c_str());
02126                     strcat(args," ");
02127                 } while (ok);           
02128                 // Use shell to start program
02129                 DOS_Shell shell;
02130                 shell.Execute(filename,args);
02131                 DOS_FreeMemory(segment);        
02132                 WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOC"),kb);
02133             }
02134         } else {
02135             WriteOut(MSG_Get("PROGRAM_LOADFIX_ERROR"),kb);  
02136         }
02137     }
02138 }
02139 
02140 static void LOADFIX_ProgramStart(Program * * make) {
02141     *make=new LOADFIX;
02142 }
02143 
02144 // RESCAN
02145 
02146 class RESCAN : public Program {
02147 public:
02148     void Run(void);
02149 };
02150 
02151 void RESCAN::Run(void) 
02152 {
02153     bool all = false;
02154     
02155     Bit8u drive = DOS_GetDefaultDrive();
02156     
02157     if(cmd->FindCommand(1,temp_line)) {
02158         //-A -All /A /All 
02159         if(temp_line.size() >= 2 && (temp_line[0] == '-' ||temp_line[0] =='/')&& (temp_line[1] == 'a' || temp_line[1] =='A') ) all = true;
02160         else if(temp_line.size() == 2 && temp_line[1] == ':') {
02161             lowcase(temp_line);
02162             drive  = temp_line[0] - 'a';
02163         }
02164     }
02165     // Get current drive
02166     if (all) {
02167         for(Bitu i =0; i<DOS_DRIVES;i++) {
02168             if (Drives[i]) Drives[i]->EmptyCache();
02169         }
02170         WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS"));
02171     } else {
02172         if (drive < DOS_DRIVES && Drives[drive]) {
02173             Drives[drive]->EmptyCache();
02174             WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS"));
02175         }
02176     }
02177 }
02178 
02179 static void RESCAN_ProgramStart(Program * * make) {
02180     *make=new RESCAN;
02181 }
02182 
02183 /* TODO: This menu code sucks. Write a better one. */
02184 class INTRO : public Program {
02185 public:
02186     void DisplayMount(void) {
02187         /* Basic mounting has a version for each operating system.
02188          * This is done this way so both messages appear in the language file*/
02189         WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_START"));
02190 #if (WIN32)
02191         WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS"));
02192 #else           
02193         WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_OTHER"));
02194 #endif
02195         WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_END"));
02196     }
02197     void DisplayUsage(void) {
02198         Bit8u c;Bit16u n=1;
02199         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP"));
02200         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_1"));
02201         DOS_ReadFile (STDIN,&c,&n);
02202         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP"));
02203         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_2"));
02204         DOS_ReadFile (STDIN,&c,&n);
02205         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_TOP"));
02206         WriteOut(MSG_Get("PROGRAM_INTRO_USAGE_3"));
02207         DOS_ReadFile (STDIN,&c,&n);
02208     }
02209     void DisplayIntro(void) {
02210         WriteOut(MSG_Get("PROGRAM_INTRO"));
02211         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_UP"));
02212     }
02213     void DisplayMenuBefore(void) { WriteOut("\033[44m\033[K\033[33m\033[1m   \033[0m "); }
02214     void DisplayMenuCursorStart(void) {
02215         if (machine == MCH_PC98) {
02216             WriteOut("\033[44m\033[K\033[1m\033[33;44m  \x1C\033[0m\033[5;37;44m ");
02217         } else {
02218             WriteOut("\033[44m\033[K\033[1m\033[33;44m  \x10\033[0m\033[5;37;44m ");
02219         }
02220     }
02221     void DisplayMenuCursorEnd(void) { WriteOut("\033[0m\n"); }
02222     void DisplayMenuNone(void) { WriteOut("\033[44m\033[K\033[0m\n"); }
02223 
02224     bool CON_IN(Bit8u * data) {
02225         Bit8u c;
02226         Bit16u n=1;
02227 
02228         /* handle arrow keys vs normal input,
02229          * with special conditions for PC-98 and IBM PC */
02230         if (!DOS_ReadFile(STDIN,&c,&n) || n == 0) return false;
02231 
02232         if (IS_PC98_ARCH) {
02233             /* translate PC-98 arrow keys to IBM PC escape for the caller */
02234                  if (c == 0x0B)
02235                 *data = 0x48 | 0x80;    /* IBM extended code up arrow */
02236             else if (c == 0x0A)
02237                 *data = 0x50 | 0x80;    /* IBM extended code down arrow */
02238             else
02239                 *data = c;
02240         }
02241         else {
02242             if (c == 0) {
02243                 if (!DOS_ReadFile(STDIN,&c,&n) || n == 0) return false;
02244                 *data = c | 0x80; /* extended code */
02245             }
02246             else {
02247                 *data = c;
02248             }
02249         }
02250 
02251         return true;
02252     }
02253 
02254     void Run(void) {
02255         std::string menuname = "BASIC"; // default
02256         /* Only run if called from the first shell (Xcom TFTD runs any intro file in the path) */
02257         if(DOS_PSP(dos.psp()).GetParent() != DOS_PSP(DOS_PSP(dos.psp()).GetParent()).GetParent()) return;
02258         if(cmd->FindExist("cdrom",false)) {
02259             WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
02260             return;
02261         }
02262         if(cmd->FindExist("mount",false)) {
02263             WriteOut("\033[2J");//Clear screen before printing
02264             DisplayMount();
02265             return;
02266         }
02267         if(cmd->FindExist("special",false)) {
02268             WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL"));
02269             return;
02270         }
02271 
02272         if(cmd->FindExist("usage",false)) { DisplayUsage(); return; }
02273         Bit8u c;Bit16u n=1;
02274 
02275 #define CURSOR(option) \
02276     if (menuname==option) DisplayMenuCursorStart(); \
02277     else DisplayMenuBefore(); \
02278     WriteOut(MSG_Get("PROGRAM_INTRO_MENU_" option "")); \
02279     if (menuname==option) DisplayMenuCursorEnd(); \
02280     else WriteOut("\n");
02281 
02282         /* Intro */
02283 
02284 menufirst:
02285         DisplayIntro();
02286         CURSOR("BASIC")
02287         CURSOR("CDROM")
02288         CURSOR("SPECIAL")
02289         CURSOR("USAGE")
02290         DisplayMenuNone(); // None
02291         CURSOR("INFO")
02292         CURSOR("QUIT")
02293         DisplayMenuNone(); // None
02294 
02295         if (menuname=="BASIC") goto basic;
02296         else if (menuname=="CDROM") goto cdrom;
02297         else if (menuname=="SPECIAL") goto special;
02298         else if (menuname=="USAGE") goto usage;
02299         else if (menuname=="INFO") goto info;
02300         else if (menuname=="QUIT") goto quit;
02301         else if (menuname=="GOTO_EXIT") goto goto_exit;
02302 
02303 goto_exit:
02304         WriteOut("\n"); // Give a line
02305         return;
02306 
02307 basic:
02308         menuname="BASIC";
02309         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_BASIC_HELP")); 
02310         CON_IN(&c);
02311         do switch (c) {
02312             case 0x48|0x80: menuname="QUIT"; goto menufirst; // Up
02313             case 0x50|0x80: menuname="CDROM"; goto menufirst; // Down
02314             case 0xD:   // Run
02315                 WriteOut("\033[2J");
02316                 WriteOut(MSG_Get("PROGRAM_INTRO"));
02317                 WriteOut("\n");
02318                 DisplayMount();
02319                 DOS_ReadFile (STDIN,&c,&n);
02320                 goto menufirst;
02321         } while (CON_IN(&c));
02322 
02323 cdrom:
02324         menuname="CDROM";
02325         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_CDROM_HELP")); 
02326         CON_IN(&c);
02327         do switch (c) {
02328             case 0x48|0x80: menuname="BASIC"; goto menufirst; // Up
02329             case 0x50|0x80: menuname="SPECIAL"; goto menufirst; // Down
02330             case 0xD:   // Run
02331                 WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
02332                 DOS_ReadFile (STDIN,&c,&n);
02333                 goto menufirst;
02334         } while (CON_IN(&c));
02335 
02336 special:
02337         menuname="SPECIAL";
02338         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_SPECIAL_HELP")); 
02339         CON_IN(&c);
02340         do switch (c) {
02341             case 0x48|0x80: menuname="CDROM"; goto menufirst; // Up
02342             case 0x50|0x80: menuname="USAGE"; goto menufirst; // Down
02343             case 0xD:   // Run
02344                 WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL"));
02345                 DOS_ReadFile (STDIN,&c,&n);
02346                 goto menufirst;
02347         } while (CON_IN(&c));
02348 
02349 usage:
02350         menuname="USAGE";
02351         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_USAGE_HELP")); 
02352         CON_IN(&c);
02353         do switch (c) {
02354             case 0x48|0x80: menuname="SPECIAL"; goto menufirst; // Up
02355             case 0x50|0x80: menuname="INFO"; goto menufirst; // Down
02356             case 0xD:   // Run
02357                 DisplayUsage();
02358                 goto menufirst;
02359         } while (CON_IN(&c));
02360 
02361 info:
02362         menuname="INFO";
02363         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_INFO_HELP"));
02364         CON_IN(&c);
02365         do switch (c) {
02366             case 0x48|0x80: menuname="USAGE"; goto menufirst; // Up
02367             case 0x50|0x80: menuname="QUIT"; goto menufirst; // Down
02368             case 0xD:   // Run
02369                 WriteOut("\033[2J");
02370                 WriteOut(MSG_Get("PROGRAM_INTRO"));
02371                 WriteOut("\n");
02372                 WriteOut(MSG_Get("PROGRAM_INTRO_INFO"));
02373                 DOS_ReadFile (STDIN,&c,&n);
02374                 goto menufirst;
02375         } while (CON_IN(&c));
02376 
02377 quit:
02378         menuname="QUIT";
02379         WriteOut(MSG_Get("PROGRAM_INTRO_MENU_QUIT_HELP")); 
02380         CON_IN(&c);
02381         do switch (c) {
02382             case 0x48|0x80: menuname="INFO"; goto menufirst; // Up
02383             case 0x50|0x80: menuname="BASIC"; goto menufirst; // Down
02384             case 0xD:   // Run
02385                 menuname="GOTO_EXIT";
02386                 return;
02387         } while (CON_IN(&c));
02388     }   
02389 };
02390 
02391 bool ElTorito_ChecksumRecord(unsigned char *entry/*32 bytes*/) {
02392     unsigned int word,chk=0,i;
02393 
02394     for (i=0;i < 16;i++) {
02395         word = ((unsigned int)entry[0]) + ((unsigned int)entry[1] << 8);
02396         chk += word;
02397         entry += 2;
02398     }
02399     chk &= 0xFFFF;
02400     return (chk == 0);
02401 }
02402 
02403 static void INTRO_ProgramStart(Program * * make) {
02404     *make=new INTRO;
02405 }
02406 
02407 bool ElTorito_ScanForBootRecord(CDROM_Interface *drv,unsigned long &boot_record,unsigned long &el_torito_base) {
02408     unsigned char buffer[2048];
02409     unsigned int sec;
02410 
02411     for (sec=16;sec < 32;sec++) {
02412         if (!drv->ReadSectorsHost(buffer,false,sec,1))
02413             break;
02414 
02415         /* stop at terminating volume record */
02416         if (buffer[0] == 0xFF) break;
02417 
02418         /* match boot record and whether it conforms to El Torito */
02419         if (buffer[0] == 0x00 && memcmp(buffer+1,"CD001",5) == 0 && buffer[6] == 0x01 &&
02420             memcmp(buffer+7,"EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0",32) == 0) {
02421             boot_record = sec;
02422             el_torito_base = (unsigned long)buffer[71] +
02423                     ((unsigned long)buffer[72] << 8UL) +
02424                     ((unsigned long)buffer[73] << 16UL) +
02425                     ((unsigned long)buffer[74] << 24UL);
02426 
02427             return true;
02428         }
02429     }
02430 
02431     return false;
02432 }
02433 
02434 
02435 /* C++ class implementing El Torito floppy emulation */
02436 class imageDiskElToritoFloppy : public imageDisk {
02437 public:
02438     /* Read_Sector and Write_Sector take care of geometry translation for us,
02439      * then call the absolute versions. So, we override the absolute versions only */
02440     virtual Bit8u Read_AbsoluteSector(Bit32u sectnum, void * data) {
02441         unsigned char buffer[2048];
02442 
02443         bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom);
02444 
02445         CDROM_Interface *src_drive=NULL;
02446         if (!GetMSCDEXDrive(CDROM_drive-'A',&src_drive)) return 0x05;
02447 
02448         if (!src_drive->ReadSectorsHost(buffer,false,cdrom_sector_offset+(sectnum>>2)/*512 byte/sector to 2048 byte/sector conversion*/,1))
02449             return 0x05;
02450 
02451         memcpy(data,buffer+((sectnum&3)*512),512);
02452         return 0x00;
02453     }
02454     virtual Bit8u Write_AbsoluteSector(Bit32u sectnum,const void * data) {
02455         (void)sectnum;//UNUSED
02456         (void)data;//UNUSED
02457         return 0x05; /* fail, read only */
02458     }
02459     imageDiskElToritoFloppy(unsigned char new_CDROM_drive,unsigned long new_cdrom_sector_offset,unsigned char floppy_emu_type) : imageDisk(NULL,NULL,0,false) {
02460         diskimg = NULL;
02461         sector_size = 512;
02462         CDROM_drive = new_CDROM_drive;
02463         cdrom_sector_offset = new_cdrom_sector_offset;
02464         class_id = ID_EL_TORITO_FLOPPY;
02465 
02466         if (floppy_emu_type == 1) { /* 1.2MB */
02467             heads = 2;
02468             cylinders = 80;
02469             sectors = 15;
02470         }
02471         else if (floppy_emu_type == 2) { /* 1.44MB */
02472             heads = 2;
02473             cylinders = 80;
02474             sectors = 18;
02475         }
02476         else if (floppy_emu_type == 3) { /* 2.88MB */
02477             heads = 2;
02478             cylinders = 80;
02479             sectors = 36; /* FIXME: right? */
02480         }
02481         else {
02482             heads = 2;
02483             cylinders = 69;
02484             sectors = 14;
02485             LOG_MSG("BUG! unsupported floppy_emu_type in El Torito floppy object\n");
02486         }
02487 
02488         diskSizeK = ((Bit64u)heads * cylinders * sectors * sector_size) / 1024;
02489         active = true;
02490     }
02491     virtual ~imageDiskElToritoFloppy() {
02492     }
02493 
02494     unsigned long cdrom_sector_offset;
02495     unsigned char CDROM_drive;
02496 /*
02497     int class_id;
02498 
02499     bool hardDrive;
02500     bool active;
02501     FILE *diskimg;
02502     std::string diskname;
02503     Bit8u floppytype;
02504 
02505     Bit32u sector_size;
02506     Bit32u heads,cylinders,sectors;
02507     Bit32u reserved_cylinders;
02508     Bit64u current_fpos; */
02509 };
02510 
02511 bool FDC_AssignINT13Disk(unsigned char drv);
02512 bool FDC_UnassignINT13Disk(unsigned char drv);
02513 
02514 class IMGMOUNT : public Program {
02515 public:
02516     void Run(void) {
02517         //Hack To allow long commandlines
02518         ChangeToLongCmd();
02519         /* In secure mode don't allow people to change imgmount points. 
02520          * Neither mount nor unmount */
02521         if(control->SecureMode()) {
02522             WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
02523             return;
02524         }
02525         //initialize variables
02526         imageDisk * newImage = NULL;
02527         char drive;
02528         std::vector<std::string> paths;
02529         //show help if no arguments or -?
02530         if (cmd->GetCount() == 0 || cmd->FindExist("-?", true) || cmd->FindExist("-help", true)) {
02531             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_HELP"));
02532             return;
02533         }
02534         /* Check for unmounting */
02535         std::string umount;
02536         if (cmd->FindString("-u",umount,false)) {
02537             Unmount(umount[0]);
02538             return;
02539         }
02540 
02541         //initialize more variables
02542         unsigned long el_torito_floppy_base=~0UL;
02543         unsigned char el_torito_floppy_type=0xFF;
02544         bool ide_slave = false;
02545         signed char ide_index = -1;
02546         char el_torito_cd_drive = 0;
02547         std::string el_torito;
02548         std::string ideattach="auto";
02549         std::string type="hdd";
02550 
02551         //this code simply sets default type to "floppy" if mounting at A: or B: --- nothing else
02552         // 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:
02553         // default to floppy for drive letters A and B and numbers 0 and 1
02554         if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) ||
02555             ((temp_line.size()>1) && (temp_line[1]!=':'))) {
02556             // drive not valid
02557         } else {
02558             Bit8u tdr = toupper(temp_line[0]);
02559             if(tdr=='A'||tdr=='B'||tdr=='0'||tdr=='1') type="floppy";
02560         }
02561         //get the type
02562         cmd->FindString("-t", type, true);
02563         if (type == "cdrom") type = "iso"; //Tiny hack for people who like to type -t cdrom
02564         if (!(type == "floppy" || type == "hdd" || type == "iso" || type == "ram")) {
02565             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED"), type.c_str());
02566             return;
02567         }
02568 
02569         //look for -el-torito parameter and remove it from the command line
02570         cmd->FindString("-el-torito",el_torito,true);
02571         if (el_torito != "") {
02572             //get el-torito floppy from cdrom mounted at drive letter el_torito_cd_drive
02573             el_torito_cd_drive = toupper(el_torito[0]);
02574             //validate the el_torito loading (look for boot image on the cdrom, etc), and
02575             //  find the el_torito_floppy_base and el_torito_floppy_type values
02576             if (!PrepElTorito(type, el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type)) return;
02577         }
02578 
02579         //default fstype is fat
02580         std::string fstype="fat";
02581         cmd->FindString("-fs",fstype,true);
02582         
02583         Bitu sizes[4] = { 0,0,0,0 };
02584         int reserved_cylinders=0;
02585         std::string reservecyl;
02586 
02587         /* DOSBox-X: to please certain 32-bit drivers like Windows 3.1 WDCTRL, or to emulate older h/w configurations,
02588             *           we allow the user or script to specify the number of reserved cylinders. older BIOSes were known
02589             *           to subtract 1 or 2 additional cylinders from the total in the fixed disk param table. the -reservecyl
02590             *           option allows the number we subtract from the total in INT 13H to be set */
02591         cmd->FindString("-reservecyl",reservecyl,true);
02592         if (reservecyl != "") reserved_cylinders = atoi(reservecyl.c_str());
02593 
02594         /* DOSBox-X: we allow "-ide" to allow controlling which IDE controller and slot to attach the hard disk/CD-ROM to */
02595         cmd->FindString("-ide",ideattach,true);
02596 
02597         if (ideattach == "auto") {
02598             if (type == "floppy") {
02599             }
02600             else {
02601                 IDE_Auto(ide_index,ide_slave);
02602             }
02603                 
02604             LOG_MSG("IDE: index %d slave=%d",ide_index,ide_slave?1:0);
02605         }
02606         else if (ideattach != "none" && isdigit(ideattach[0]) && ideattach[0] > '0') { /* takes the form [controller]<m/s> such as: 1m for primary master */
02607             ide_index = ideattach[0] - '1';
02608             if (ideattach.length() >= 1) ide_slave = (ideattach[1] == 's');
02609             LOG_MSG("IDE: index %d slave=%d",ide_index,ide_slave?1:0);
02610         }
02611 
02612         //if floppy, don't attach to ide controller
02613         //if cdrom, file system is iso
02614         if (type=="floppy") {
02615             ideattach="none";
02616         } else if (type=="iso") {
02617             //str_size=="2048,1,60000,0";   // ignored, see drive_iso.cpp (AllocationInfo)
02618             fstype = "iso";
02619         } 
02620 
02621         //load the size parameter
02622         //auto detect hard drives if not specified
02623         std::string str_size;
02624         std::string str_chs;
02625         cmd->FindString("-size", str_size, true);
02626         cmd->FindString("-chs", str_chs, true);
02627         if (!ReadSizeParameter(str_size, str_chs, type, sizes)) return;
02628         
02629         //for floppies, hard drives, and cdroms, require a drive letter
02630         //for -fs none, require a number indicating where to mount at
02631         if(fstype=="fat" || fstype=="iso") {
02632             // get the drive letter
02633             if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) {
02634                 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE"));
02635                 return;
02636             }
02637             drive=toupper(temp_line[0]);
02638             if (!isalpha(drive)) {
02639                 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE"));
02640                 return;
02641             }
02642         } else if (fstype=="none") {
02643             cmd->FindCommand(1,temp_line);
02644             if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) {
02645                 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2"));
02646                 return;
02647             }
02648             drive=temp_line[0];
02649             if ((drive<'0') || (drive>=MAX_DISK_IMAGES+'0')) {
02650                 WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2"));
02651                 return;
02652             }
02653         } else {
02654             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED"),fstype.c_str());
02655             return;
02656         }
02657             
02658         // find all file parameters, assuming that all option parameters have been removed
02659         ParseFiles(temp_line, paths);
02660 
02661         // some generic checks
02662         if (el_torito != "") {
02663             if (paths.size() != 0) {
02664                 WriteOut("Do not specify files when mounting virtual floppy disk images from El Torito bootable CDs\n");
02665                 return;
02666             }
02667         }
02668         else if (type == "ram") {
02669             if (paths.size() != 0) {
02670                 WriteOut("Do not specify files when mounting ramdrives\n");
02671                 return;
02672             }
02673         }
02674         else {
02675             if (paths.size() == 0) {
02676                 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_FILE"));
02677                 return; 
02678             }
02679         }
02680 
02681         //====== call the proper subroutine ======
02682         if(fstype=="fat") {
02683             //mount floppy or hard drive
02684             if (el_torito != "") {
02685                 if (!MountElToritoFat(drive, sizes, el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type)) return;
02686             }
02687             else if (type == "ram") {
02688                 if (!MountRam(sizes, drive, ide_index, ide_slave)) return;
02689             }
02690             else {
02691                 //supports multiple files
02692                 if (!MountFat(sizes, drive, type == "hdd", str_size, paths, ide_index, ide_slave)) return;
02693             }
02694         } else if (fstype=="iso") {
02695             if (el_torito != "") {
02696                 WriteOut("El Torito bootable CD: -fs iso mounting not supported\n"); /* <- NTS: Will never implement, either */
02697                 return;
02698             }
02699             //supports multiple files
02700             if (!MountIso(drive, paths, ide_index, ide_slave)) return;
02701         } else if (fstype=="none") {
02702             unsigned char driveIndex = drive - '0';
02703 
02704             if (paths.size() > 1) {
02705                 if (driveIndex <= 1) {
02706                     if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive <= 1 &&
02707                         swapInDisksSpecificDrive != driveIndex) {
02708                         WriteOut("Multiple images given and another drive already uses multiple images");
02709                         return;
02710                     }
02711                 }
02712                 else {
02713                     WriteOut("Multiple disk images not supported for that drive");
02714                     return;
02715                 }
02716             }
02717 
02718             if (el_torito != "") {
02719                 newImage = new imageDiskElToritoFloppy((unsigned char)el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type);
02720             }
02721             else if (type == "ram") {
02722                 newImage = MountImageNoneRam(sizes, reserved_cylinders, driveIndex < 2);
02723             }
02724             else {
02725                 newImage = MountImageNone(paths[0].c_str(), sizes, reserved_cylinders);
02726             }
02727             if (newImage == NULL) return;
02728             newImage->Addref();
02729             if (newImage->hardDrive && (driveIndex < 2)) {
02730                 WriteOut("Cannot mount hard drive in floppy position");
02731             }
02732             else if (!newImage->hardDrive && (driveIndex >= 2)) {
02733                 WriteOut("Cannot mount floppy in hard drive position");
02734             }
02735             else {
02736                 if (AttachToBiosAndIdeByIndex(newImage, (unsigned char)driveIndex, (unsigned char)ide_index, ide_slave)) {
02737                     WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT_NUMBER"), drive - '0', (!paths.empty()) ? paths[0].c_str() : "");
02738 
02739                     if (paths.size() > 1) {
02740                         for (size_t si=0;si < MAX_SWAPPABLE_DISKS;si++) {
02741                             if (diskSwap[si] != NULL) {
02742                                 diskSwap[si]->Release();
02743                                 diskSwap[si] = NULL;
02744                             }
02745                         }
02746 
02747                         /* slot 0 is the image we already assigned */
02748                         diskSwap[0] = newImage;
02749                         diskSwap[0]->Addref();
02750                         swapPosition = 0;
02751                         swapInDisksSpecificDrive = driveIndex;
02752 
02753                         for (size_t si=1;si < MAX_SWAPPABLE_DISKS && si < paths.size();si++) {
02754                             imageDisk *img = MountImageNone(paths[si].c_str(), sizes, reserved_cylinders);
02755 
02756                             if (img != NULL) {
02757                                 diskSwap[si] = img;
02758                                 diskSwap[si]->Addref();
02759                             }
02760                         }
02761                     }
02762                 }
02763                 else {
02764                     WriteOut("Invalid mount number");
02765                 }
02766             }
02767             newImage->Release();
02768             return;
02769         }
02770         else {
02771             WriteOut("Invalid fstype\n");
02772             return;
02773         }
02774 
02775         return;
02776     }
02777 
02778 private:
02779     bool ReadSizeParameter(const std::string &str_size, const std::string &str_chs, const std::string &type, Bitu sizes[]) {
02780         bool isCHS = false;
02781         const char * scan;
02782         if (str_chs.size() != 0) {
02783             if (str_size.size() != 0) {
02784                 WriteOut("Size and chs parameter cannot both be specified\n");
02785                 return false;
02786             }
02787             isCHS = true;
02788             scan = str_chs.c_str();
02789         }
02790         else if (str_size.size() != 0) {
02791             scan = str_size.c_str();
02792         }
02793         else {
02794             //nothing specified, so automatic size detection
02795             return true;
02796         }
02797         char number[20];
02798         Bitu index = 0;
02799         Bitu count = 0;
02800         int val;
02801 
02802         //scan through input string
02803         while (*scan) {
02804             //separate string by ','
02805             if (*scan == ',') {
02806                 number[index] = 0; //add null char
02807                 val = atoi(number);
02808                 if (val <= 0) {
02809                     //out of range
02810                     WriteOut("Invalid size parameter\n");
02811                     return false;
02812                 }
02813                 sizes[count++] = (unsigned int)val;
02814                 index = 0;
02815                 if (count == 4) {
02816                     //too many commas
02817                     WriteOut("Invalid size parameter\n");
02818                     return false;
02819                 }
02820             }
02821             else if (index >= 19) {
02822                 //number too large (too many characters, anyway)
02823                 WriteOut("Invalid size parameter\n");
02824                 return false;
02825             }
02826             else {
02827                 number[index++] = *scan;
02828             }
02829             scan++;
02830         }
02831         number[index] = 0;
02832         val = atoi(number);
02833         if (val <= 0) {
02834             //out of range
02835             WriteOut("Invalid size parameter\n");
02836             return false;
02837         }
02838         sizes[count++] = (unsigned int)val;
02839         if (isCHS) {
02840             if (count == 3) sizes[count++] = 512; //set sector size automatically
02841             if (count != 4) {
02842                 WriteOut("Invalid chs parameter\n");
02843                 return false;
02844             }
02845             Bitu temp = sizes[3]; //hold on to sector size temporarily
02846             sizes[3] = sizes[0]; //put cylinders in the right spot
02847             sizes[0] = temp; //put sector size in the right spot
02848             temp = sizes[2]; //hold on to sectors temporarily
02849             sizes[2] = sizes[1]; //put heads in the right spot
02850             sizes[1] = temp; //put sectors in the right spot
02851         }
02852         if (!((type == "ram" && count == 1) || count == 4)) {
02853             //ram drives require 1 or 4 numbers
02854             //other drives require 4 numbers
02855             WriteOut("Invalid size parameter\n");
02856             return false;
02857         }
02858         return true;
02859     }
02860     void ParseFiles(std::string &commandLine, std::vector<std::string> &paths) {
02861         while (cmd->FindCommand((unsigned int)(paths.size() + 2), commandLine) && commandLine.size()) {
02862 #if defined (WIN32) || defined(OS2)
02863             /* nothing */
02864 #else
02865             // Linux: Convert backslash to forward slash
02866             if (commandLine.size() > 0) {
02867                 for (size_t i = 0; i < commandLine.size(); i++) {
02868                     if (commandLine[i] == '\\')
02869                         commandLine[i] = '/';
02870                 }
02871             }
02872 #endif
02873 
02874             pref_struct_stat test;
02875             if (pref_stat(commandLine.c_str(), &test)) {
02876                 //See if it works if the ~ are written out
02877                 std::string homedir(commandLine);
02878                 Cross::ResolveHomedir(homedir);
02879                 if (!pref_stat(homedir.c_str(), &test)) {
02880                     commandLine = homedir;
02881                 }
02882                 else {
02883                     // convert dosbox filename to system filename
02884                     char fullname[CROSS_LEN];
02885                     char tmp[CROSS_LEN];
02886                     safe_strncpy(tmp, commandLine.c_str(), CROSS_LEN);
02887 
02888                     Bit8u dummy;
02889                     if (!DOS_MakeName(tmp, fullname, &dummy) || strncmp(Drives[dummy]->GetInfo(), "local directory", 15)) {
02890                         WriteOut(MSG_Get("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE"));
02891                         return;
02892                     }
02893 
02894                     localDrive *ldp = dynamic_cast<localDrive*>(Drives[dummy]);
02895                     if (ldp == NULL) {
02896                         WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND"));
02897                         return;
02898                     }
02899                     ldp->GetSystemFilename(tmp, fullname);
02900                     commandLine = tmp;
02901 
02902                     if (pref_stat(commandLine.c_str(), &test)) {
02903                         WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND"));
02904                         return;
02905                     }
02906                 }
02907             }
02908             if ((test.st_mode & S_IFDIR)) {
02909                 WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT"));
02910                 return;
02911             }
02912             paths.push_back(commandLine);
02913         }
02914     }
02915 
02916     bool Unmount(char &letter) {
02917         letter = toupper(letter);
02918         if (isalpha(letter)) { /* if it's a drive letter, then traditional usage applies */
02919             int i_drive = letter - 'A';
02920             if (i_drive < DOS_DRIVES && i_drive >= 0 && Drives[i_drive]) {
02921                 //if drive A: or B:
02922                 if (i_drive <= 1)
02923                     FDC_UnassignINT13Disk(i_drive);
02924 
02925                 //get reference to image and cdrom before they are possibly destroyed
02926                 fatDrive * drive = dynamic_cast<fatDrive*>(Drives[i_drive]);
02927                 imageDisk* image = drive ? drive->loadedDisk : NULL;
02928                 isoDrive * cdrom = dynamic_cast<isoDrive*>(Drives[i_drive]);
02929 
02930                 switch (DriveManager::UnmountDrive(i_drive)) {
02931                 case 0: //success
02932                 {
02933                     //detatch hard drive or floppy drive from bios and ide controller
02934                     if (image) DetachFromBios(image);
02935 
02936                     /* If the drive letter is also a CD-ROM drive attached to IDE, then let the IDE code know */
02937                     if (cdrom) IDE_CDROM_Detach(i_drive);
02938 
02939                     Drives[i_drive] = NULL;
02940                     if (i_drive == DOS_GetDefaultDrive())
02941                         DOS_SetDrive(toupper('Z') - 'A');
02942                     WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"), letter);
02943                     return true;
02944                 }
02945                 case 1:
02946                     WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL"));
02947                     return false;
02948                 case 2:
02949                     WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"));
02950                     return false;
02951                 default:
02952                     return false;
02953                 }
02954             }
02955             else {
02956                 WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"), letter);
02957                 return false;
02958             }
02959         }
02960         else if (isdigit(letter)) { /* DOSBox-X: drives mounted by number (INT 13h) can be unmounted this way */
02961             int index = letter - '0';
02962 
02963             //detatch hard drive or floppy drive from bios and ide controller
02964             if (index < MAX_DISK_IMAGES && imageDiskList[index]) {
02965                 if (index > 1) IDE_Hard_Disk_Detach(index);
02966                 imageDiskList[index]->Release();
02967                 imageDiskList[index] = NULL;
02968                 return true;
02969             }
02970             WriteOut("No drive loaded at specified point\n");
02971             return false;
02972         }
02973         else {
02974             WriteOut("Unknown imgmount unmount usage");
02975             return false;
02976         }
02977     }
02978 
02979     bool PrepElTorito(const std::string type, const char &el_torito_cd_drive, unsigned long &el_torito_floppy_base, unsigned char &el_torito_floppy_type) {
02980         el_torito_floppy_base = ~0UL;
02981         el_torito_floppy_type = 0xFF;
02982 
02983         unsigned char entries[2048], *entry, ent_num = 0;
02984         int header_platform = -1, header_count = 0;
02985         bool header_final = false;
02986         int header_more = -1;
02987 
02988         /* must be valid drive letter, C to Z */
02989         if (!isalpha(el_torito_cd_drive) || el_torito_cd_drive < 'C') {
02990             WriteOut("-el-torito requires a proper drive letter corresponding to your CD-ROM drive\n");
02991             return false;
02992         }
02993 
02994         /* drive must not exist (as a hard drive) */
02995         if (imageDiskList[el_torito_cd_drive - 'A'] != NULL) {
02996             WriteOut("-el-torito CD-ROM drive specified already exists as a non-CD-ROM device\n");
02997             return false;
02998         }
02999 
03000         bool GetMSCDEXDrive(unsigned char drive_letter, CDROM_Interface **_cdrom);
03001 
03002         /* get the CD-ROM drive */
03003         CDROM_Interface *src_drive = NULL;
03004         if (!GetMSCDEXDrive(el_torito_cd_drive - 'A', &src_drive)) {
03005             WriteOut("-el-torito CD-ROM drive specified is not actually a CD-ROM drive\n");
03006             return false;
03007         }
03008 
03009         /* FIXME: We only support the floppy emulation mode at this time.
03010         *        "Superfloppy" or hard disk emulation modes are not yet implemented */
03011         if (type != "floppy") {
03012             WriteOut("-el-torito must be used with -t floppy at this time\n");
03013             return false;
03014         }
03015 
03016         /* Okay. Step #1: Scan the volume descriptors for the Boot Record. */
03017         unsigned long el_torito_base = 0, boot_record_sector = 0;
03018         if (!ElTorito_ScanForBootRecord(src_drive, boot_record_sector, el_torito_base)) {
03019             WriteOut("El Torito boot record not found\n");
03020             return false;
03021         }
03022 
03023         LOG_MSG("El Torito emulation: Found ISO 9660 Boot Record in sector %lu, pointing to sector %lu\n",
03024             boot_record_sector, el_torito_base);
03025 
03026         /* Step #2: Parse the records. Each one is 32 bytes long */
03027         if (!src_drive->ReadSectorsHost(entries, false, el_torito_base, 1)) {
03028             WriteOut("El Torito entries unreadable\n");
03029             return false;
03030         }
03031 
03032         /* for more information about what this loop is doing, read:
03033         * http://download.intel.com/support/motherboards/desktop/sb/specscdrom.pdf
03034         */
03035         /* FIXME: Somebody find me an example of a CD-ROM with bootable code for both x86, PowerPC, and Macintosh.
03036         *        I need an example of such a CD since El Torito allows multiple "headers" */
03037         /* TODO: Is it possible for this record list to span multiple sectors? */
03038         for (ent_num = 0; ent_num < (2048 / 0x20); ent_num++) {
03039             entry = entries + (ent_num * 0x20);
03040 
03041             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)
03042                 break;
03043 
03044             if (entry[0] == 0x01/*header*/) {
03045                 if (!ElTorito_ChecksumRecord(entry)) {
03046                     LOG_MSG("Warning: El Torito checksum error in header(0x01) entry\n");
03047                     continue;
03048                 }
03049 
03050                 if (header_count != 0) {
03051                     LOG_MSG("Warning: El Torito has more than one Header/validation entry\n");
03052                     continue;
03053                 }
03054 
03055                 if (header_final) {
03056                     LOG_MSG("Warning: El Torito has an additional header past the final header\n");
03057                     continue;
03058                 }
03059 
03060                 header_more = -1;
03061                 header_platform = entry[1];
03062                 LOG_MSG("El Torito entry: first header platform=0x%02x\n", header_platform);
03063                 header_count++;
03064             }
03065             else if (entry[0] == 0x90/*header, more follows*/ || entry[0] == 0x91/*final header*/) {
03066                 if (header_final) {
03067                     LOG_MSG("Warning: El Torito has an additional header past the final header\n");
03068                     continue;
03069                 }
03070 
03071                 header_final = (entry[0] == 0x91);
03072                 header_more = (int)(((unsigned int)entry[2]) + (((unsigned int)entry[3]) << 8u));
03073                 header_platform = entry[1];
03074                 LOG_MSG("El Torito entry: first header platform=0x%02x more=%u final=%u\n", header_platform, header_more, header_final);
03075                 header_count++;
03076             }
03077             else {
03078                 if (header_more == 0) {
03079                     LOG_MSG("El Torito entry: Non-header entry count expired, ignoring record 0x%02x\n", entry[0]);
03080                     continue;
03081                 }
03082                 else if (header_more > 0) {
03083                     header_more--;
03084                 }
03085 
03086                 if (entry[0] == 0x44) {
03087                     LOG_MSG("El Torito entry: ignoring extension record\n");
03088                 }
03089                 else if (entry[0] == 0x00/*non-bootable*/) {
03090                     LOG_MSG("El Torito entry: ignoring non-bootable record\n");
03091                 }
03092                 else if (entry[0] == 0x88/*bootable*/) {
03093                     if (header_platform == 0x00/*x86*/) {
03094                         unsigned char mediatype = entry[1] & 0xF;
03095                         unsigned short load_segment = ((unsigned int)entry[2]) + (((unsigned int)entry[3]) << 8);
03096                         unsigned char system_type = entry[4];
03097                         unsigned short sector_count = ((unsigned int)entry[6]) + (((unsigned int)entry[7]) << 8);
03098                         unsigned long load_rba = ((unsigned int)entry[8]) + (((unsigned int)entry[9]) << 8) +
03099                             (((unsigned int)entry[10]) << 16) + (((unsigned int)entry[11]) << 24);
03100 
03101                         LOG_MSG("El Torito entry: bootable x86 record mediatype=%u load_segment=0x%04x "
03102                             "system_type=0x%02x sector_count=%u load_rba=%lu\n",
03103                             mediatype, load_segment, system_type, sector_count, load_rba);
03104 
03105                         /* already chose one, ignore */
03106                         if (el_torito_floppy_base != ~0UL)
03107                             continue;
03108 
03109                         if (load_segment != 0 && load_segment != 0x7C0)
03110                             LOG_MSG("El Torito boot warning: load segments other than 0x7C0 not supported yet\n");
03111                         if (sector_count != 1)
03112                             LOG_MSG("El Torito boot warning: sector counts other than 1 are not supported yet\n");
03113 
03114                         if (mediatype < 1 || mediatype > 3) {
03115                             LOG_MSG("El Torito boot entry: media types other than floppy emulation not supported yet\n");
03116                             continue;
03117                         }
03118 
03119                         el_torito_floppy_base = load_rba;
03120                         el_torito_floppy_type = mediatype;
03121                     }
03122                     else {
03123                         LOG_MSG("El Torito entry: ignoring bootable non-x86 (platform_id=0x%02x) record\n", header_platform);
03124                     }
03125                 }
03126                 else {
03127                     LOG_MSG("El Torito entry: ignoring unknown record ID %02x\n", entry[0]);
03128                 }
03129             }
03130         }
03131 
03132         if (el_torito_floppy_type == 0xFF || el_torito_floppy_base == ~0UL) {
03133             WriteOut("El Torito bootable floppy not found\n");
03134             return false;
03135         }
03136 
03137         return true;
03138     }
03139 
03140     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) {
03141         unsigned char driveIndex = drive - 'A';
03142 
03143         (void)sizes;//UNUSED
03144 
03145         if (driveIndex > 1) {
03146             WriteOut("Invalid drive letter");
03147             return false;
03148         }
03149 
03150         if (Drives[driveIndex]) {
03151             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
03152             return false;
03153         }
03154 
03155         imageDisk * newImage = new imageDiskElToritoFloppy((unsigned char)el_torito_cd_drive, el_torito_floppy_base, el_torito_floppy_type);
03156         newImage->Addref();
03157 
03158         DOS_Drive* newDrive = new fatDrive(newImage);
03159         newImage->Release(); //fatDrive calls Addref, and this will release newImage if fatDrive doesn't use it
03160         if (!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) {
03161             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));
03162             return false;
03163         }
03164 
03165         AddToDriveManager(drive, newDrive, 0xF0);
03166         AttachToBiosByLetter(newImage, drive);
03167 
03168         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_ELTORITO"), drive);
03169 
03170         return true;
03171     }
03172 
03173     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) {
03174         if (Drives[drive - 'A']) {
03175             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
03176             return false;
03177         }
03178 
03179         bool imgsizedetect = isHardDrive && sizes[0] == 0;
03180         
03181         std::vector<DOS_Drive*> imgDisks;
03182         std::vector<std::string>::size_type i;
03183         std::vector<DOS_Drive*>::size_type ct;
03184 
03185         for (i = 0; i < paths.size(); i++) {
03186             char* errorMessage = NULL;
03187             imageDisk* vhdImage = NULL;
03188 
03189             //detect hard drive geometry
03190             if (imgsizedetect) {
03191                 bool skipDetectGeometry = false;
03192                 sizes[0] = 0;
03193                 sizes[1] = 0;
03194                 sizes[2] = 0;
03195                 sizes[3] = 0;
03196 
03197                 /* .HDI images contain the geometry explicitly in the header. */
03198                 if (str_size.size() == 0) {
03199                     const char *ext = strrchr(paths[i].c_str(), '.');
03200                     if (ext != NULL) {
03201                         if (!strcasecmp(ext, ".hdi")) {
03202                             skipDetectGeometry = true;
03203                         }
03204                         //for all vhd files where the system will autodetect the chs values,
03205                         if (!strcasecmp(ext, ".vhd")) {
03206                             //load the file with imageDiskVHD, which supports fixed/dynamic/differential disks
03207                             imageDiskVHD::ErrorCodes ret = imageDiskVHD::Open(paths[i].c_str(), false, &vhdImage);
03208                             switch (ret) {
03209                             case imageDiskVHD::OPEN_SUCCESS: {
03210                                 //upon successful, go back to old code if using a fixed disk, which patches chs values for incorrectly identified disks
03211                                 skipDetectGeometry = true;
03212                                 imageDiskVHD* vhdDisk = dynamic_cast<imageDiskVHD*>(vhdImage);
03213                                 if (vhdDisk == NULL || vhdDisk->vhdType == imageDiskVHD::VHD_TYPE_FIXED) { //fixed disks would be null here
03214                                     delete vhdDisk;
03215                                     vhdDisk = 0;
03216                                     skipDetectGeometry = false;
03217                                 }
03218                                 break;
03219                             }
03220                             case imageDiskVHD::ERROR_OPENING: 
03221                                 errorMessage = (char*)MSG_Get("VHD_ERROR_OPENING"); break;
03222                             case imageDiskVHD::INVALID_DATA: 
03223                                 errorMessage = (char*)MSG_Get("VHD_INVALID_DATA"); break;
03224                             case imageDiskVHD::UNSUPPORTED_TYPE: 
03225                                 errorMessage = (char*)MSG_Get("VHD_UNSUPPORTED_TYPE"); break;
03226                             case imageDiskVHD::ERROR_OPENING_PARENT: 
03227                                 errorMessage = (char*)MSG_Get("VHD_ERROR_OPENING_PARENT"); break;
03228                             case imageDiskVHD::PARENT_INVALID_DATA: 
03229                                 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_DATA"); break;
03230                             case imageDiskVHD::PARENT_UNSUPPORTED_TYPE: 
03231                                 errorMessage = (char*)MSG_Get("VHD_PARENT_UNSUPPORTED_TYPE"); break;
03232                             case imageDiskVHD::PARENT_INVALID_MATCH: 
03233                                 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_MATCH"); break;
03234                             case imageDiskVHD::PARENT_INVALID_DATE: 
03235                                 errorMessage = (char*)MSG_Get("VHD_PARENT_INVALID_DATE"); break;
03236                             default: break;
03237                             }
03238                         }
03239                     }
03240                 }
03241                 if (!skipDetectGeometry && !DetectGeometry(paths[i].c_str(), sizes)) {
03242                     errorMessage = (char*)("Unable to detect geometry\n");
03243                 }
03244             }
03245 
03246             DOS_Drive* newDrive = NULL;
03247             if (!errorMessage) {
03248                 if (vhdImage) {
03249                     newDrive = new fatDrive(vhdImage);
03250                     vhdImage = NULL;
03251                 }
03252                 else {
03253                     newDrive = new fatDrive(paths[i].c_str(), sizes[0], sizes[1], sizes[2], sizes[3]);
03254                 }
03255                 imgDisks.push_back(newDrive);
03256                 if (!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) {
03257                     errorMessage = (char*)MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE");
03258                 }
03259             }
03260             if (errorMessage) {
03261                 WriteOut(errorMessage);
03262                 for (ct = 0; ct < imgDisks.size(); ct++) {
03263                     delete imgDisks[ct];
03264                 }
03265                 return false;
03266             }
03267         }
03268 
03269         AddToDriveManager(drive, imgDisks, isHardDrive ? 0xF8 : 0xF0);
03270 
03271         std::string tmp(paths[0]);
03272         for (i = 1; i < paths.size(); i++) {
03273             tmp += "; " + paths[i];
03274         }
03275         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str());
03276 
03277         if (imgDisks.size() == 1) {
03278             imageDisk* image = ((fatDrive*)imgDisks[0])->loadedDisk;
03279             AttachToBiosAndIdeByLetter(image, drive, (unsigned char)ide_index, ide_slave);
03280         }
03281         return true;
03282     }
03283 
03284     imageDiskMemory* CreateRamDrive(Bitu sizes[], const int reserved_cylinders, const bool forceFloppy) {
03285         imageDiskMemory* dsk = NULL;
03286         //if chs not specified
03287         if (sizes[1] == 0) {
03288             Bit32u imgSizeK = sizes[0];
03289             //default to 1.44mb floppy
03290             if (forceFloppy && imgSizeK == 0) imgSizeK = 1440;
03291             //search for floppy geometry that matches specified size in KB
03292             int index = 0;
03293             while (DiskGeometryList[index].cylcount != 0) {
03294                 if (DiskGeometryList[index].ksize == imgSizeK) {
03295                     //create floppy
03296                     dsk = new imageDiskMemory(DiskGeometryList[index]);
03297                     break;
03298                 }
03299                 index++;
03300             }
03301             if (dsk == NULL) {
03302                 //create hard drive
03303                 if (forceFloppy) {
03304                     WriteOut("Floppy size not recognized\n");
03305                     return NULL;
03306                 }
03307 
03308                 // The fatDrive class is hard-coded to assume that disks 2880KB or smaller are floppies,
03309                 //   whether or not they are attached to a floppy controller.  So, let's enforce a minimum
03310                 //   size of 4096kb for hard drives.  Use the other constructor for floppy drives.
03311                 // Note that a size of 0 means to auto-select a size
03312                 if (imgSizeK < 4096) imgSizeK = 4096;
03313 
03314                 dsk = new imageDiskMemory(imgSizeK);
03315             }
03316         }
03317         else {
03318             //search for floppy geometry that matches specified geometry
03319             int index = 0;
03320             while (DiskGeometryList[index].cylcount != 0) {
03321                 if (DiskGeometryList[index].cylcount == sizes[3] &&
03322                     DiskGeometryList[index].headscyl == sizes[2] &&
03323                     DiskGeometryList[index].secttrack == sizes[1] &&
03324                     DiskGeometryList[index].bytespersect == sizes[0]) {
03325                     //create floppy
03326                     dsk = new imageDiskMemory(DiskGeometryList[index]);
03327                     break;
03328                 }
03329                 index++;
03330             }
03331             if (dsk == NULL) {
03332                 //create hard drive
03333                 if (forceFloppy) {
03334                     WriteOut("Floppy size not recognized\n");
03335                     return NULL;
03336                 }
03337                 dsk = new imageDiskMemory(sizes[3], sizes[2], sizes[1], sizes[0]);
03338             }
03339         }
03340         if (!dsk->active) {
03341             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));
03342             delete dsk;
03343             return NULL;
03344         }
03345         dsk->Set_Reserved_Cylinders((Bitu)reserved_cylinders);
03346         return dsk;
03347     }
03348 
03349     imageDisk* MountImageNoneRam(Bitu sizes[], const int reserved_cylinders, const bool forceFloppy) {
03350         imageDiskMemory* dsk = CreateRamDrive(sizes, reserved_cylinders, forceFloppy);
03351         if (dsk == NULL) return NULL;
03352         //formatting might fail; just log the failure and continue
03353         Bit8u ret = dsk->Format();
03354         if (ret != 0x00) {
03355             LOG_MSG("Warning: could not format ramdrive - error code %u\n", (unsigned int)ret);
03356         }
03357         return dsk;
03358     }
03359 
03360     bool MountRam(Bitu sizes[], const char drive, const signed char ide_index, const bool ide_slave) {
03361         if (Drives[drive - 'A']) {
03362             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
03363             return false;
03364         }
03365 
03366         //by default, make a floppy disk if A: or B: is specified (still makes a hard drive if not a recognized size)
03367         imageDiskMemory* dsk = CreateRamDrive(sizes, 0, (drive - 'A') < 2 && sizes[0] == 0);
03368         if (dsk == NULL) return false;
03369         if (dsk->Format() != 0x00) {
03370             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));
03371             delete dsk;
03372             return false;
03373         }
03374         dsk->Addref();
03375         DOS_Drive* newDrive = new fatDrive(dsk);
03376         dsk->Release();
03377         if (!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) {
03378             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));
03379             delete newDrive; //this executes dsk.Release() which executes delete dsk
03380             return false;
03381         }
03382 
03383         AddToDriveManager(drive, newDrive, dsk->hardDrive ? 0xF8 : 0xF0);
03384 
03385         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_RAMDRIVE"), drive);
03386 
03387         AttachToBiosAndIdeByLetter(dsk, drive, (unsigned char)ide_index, ide_slave);
03388 
03389         return true;
03390     }
03391 
03392     bool AttachToBiosByIndex(imageDisk* image, const unsigned char bios_drive_index) {
03393         if (bios_drive_index >= MAX_DISK_IMAGES) return false;
03394         if (imageDiskList[bios_drive_index] != NULL) {
03395             /* Notify IDE ATA emulation if a drive is already there */
03396             if (bios_drive_index >= 2) IDE_Hard_Disk_Detach(bios_drive_index);
03397             imageDiskList[bios_drive_index]->Release();
03398         }
03399         imageDiskList[bios_drive_index] = image;
03400         image->Addref();
03401 
03402         // let FDC know if we mounted a floppy
03403         if (bios_drive_index <= 1) {
03404             FDC_AssignINT13Disk(bios_drive_index);
03405             incrementFDD();
03406         }
03407         
03408         return true;
03409     }
03410 
03411     bool AttachToBiosAndIdeByIndex(imageDisk* image, const unsigned char bios_drive_index, const unsigned char ide_index, const bool ide_slave) {
03412         if (!AttachToBiosByIndex(image, bios_drive_index)) return false;
03413         //if hard drive image, and if ide controller is specified
03414         if (bios_drive_index >= 2 || bios_drive_index < MAX_DISK_IMAGES) {
03415             IDE_Hard_Disk_Attach((signed char)ide_index, ide_slave, bios_drive_index);
03416             updateDPT();
03417         }
03418         return true;
03419     }
03420 
03421     bool AttachToBiosByLetter(imageDisk* image, const char drive) {
03422         if (image->hardDrive) {
03423             //for hard drives, mount hard drives at first available index
03424             for (int index = 2; index < MAX_DISK_IMAGES; index++) {
03425                 if (imageDiskList[index] == NULL) {
03426                     return AttachToBiosByIndex(image, index);
03427                 }
03428             }
03429         }
03430         else if (IS_PC98_ARCH) {
03431             //for pc-98 machines, mount floppies at first available index
03432             for (int index = 0; index < 2; index++) {
03433                 if (imageDiskList[index] == NULL) {
03434                     return AttachToBiosByIndex(image, index);
03435                 }
03436             }
03437         }
03438         else if ((drive - 'A') < 2) {
03439             //for PCs, mount floppies only if A: or B: is specified, and then if so, at specified index
03440             return AttachToBiosByIndex(image, drive - 'A');
03441         }
03442         return false;
03443     }
03444 
03445     bool AttachToBiosAndIdeByLetter(imageDisk* image, const char drive, const unsigned char ide_index, const bool ide_slave) {
03446         if (image->hardDrive) {
03447             //for hard drives, mount hard drives at first available index
03448             for (int index = 2; index < MAX_DISK_IMAGES; index++) {
03449                 if (imageDiskList[index] == NULL) {
03450                     return AttachToBiosAndIdeByIndex(image, index, ide_index, ide_slave);
03451                 }
03452             }
03453         }
03454         else if (IS_PC98_ARCH) {
03455             //for pc-98 machines, mount floppies at first available index
03456             for (int index = 0; index < 2; index++) {
03457                 if (imageDiskList[index] == NULL) {
03458                     return AttachToBiosByIndex(image, index);
03459                 }
03460             }
03461         } else if ((drive - 'A') < 2) {
03462             //for PCs, mount floppies only if A: or B: is specified, and then if so, at specified index
03463             return AttachToBiosByIndex(image, drive - 'A');
03464         }
03465         return false;
03466     }
03467 
03468     void DetachFromBios(imageDisk* image) {
03469         if (image) {
03470             for (int index = 0; index < MAX_DISK_IMAGES; index++) {
03471                 if (imageDiskList[index] == image) {
03472                     if (index > 1) IDE_Hard_Disk_Detach(index);
03473                     imageDiskList[index]->Release();
03474                     imageDiskList[index] = NULL;
03475                 }
03476             }
03477         }
03478     }
03479 
03480     void AddToDriveManager(const char drive, DOS_Drive* imgDisk, const Bit8u mediaid) {
03481         std::vector<DOS_Drive*> imgDisks = { imgDisk };
03482         AddToDriveManager(drive, imgDisks, mediaid);
03483     }
03484 
03485     void AddToDriveManager(const char drive, const std::vector<DOS_Drive*> &imgDisks, const Bit8u mediaid) {
03486         std::vector<DOS_Drive*>::size_type ct;
03487 
03488         // Update DriveManager
03489         for (ct = 0; ct < imgDisks.size(); ct++) {
03490             DriveManager::AppendDisk(drive - 'A', imgDisks[ct]);
03491         }
03492         DriveManager::InitializeDrive(drive - 'A');
03493 
03494         // Set the correct media byte in the table 
03495         mem_writeb(Real2Phys(dos.tables.mediaid) + ((unsigned int)drive - 'A') * 2u, mediaid);
03496 
03497         /* Command uses dta so set it to our internal dta */
03498         RealPt save_dta = dos.dta();
03499         dos.dta(dos.tables.tempdta);
03500 
03501         for (ct = 0; ct < imgDisks.size(); ct++) {
03502             DriveManager::CycleAllDisks();
03503 
03504             char root[4] = { drive, ':', '\\', 0 };
03505             DOS_FindFirst(root, DOS_ATTR_VOLUME); // force obtaining the label and saving it in dirCache
03506         }
03507         dos.dta(save_dta);
03508 
03509     }
03510 
03511     bool DetectGeometry(const char* fileName, Bitu sizes[]) {
03512         bool yet_detected = false;
03513         FILE * diskfile = fopen64(fileName, "rb+");
03514         if (!diskfile) {
03515             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
03516             return false;
03517         }
03518         fseeko64(diskfile, 0L, SEEK_END);
03519         Bit32u fcsize = (Bit32u)(ftello64(diskfile) / 512L);
03520         Bit8u buf[512];
03521         // check for vhd signature
03522         fseeko64(diskfile, -512, SEEK_CUR);
03523         if (fread(buf, sizeof(Bit8u), 512, diskfile)<512) {
03524             fclose(diskfile);
03525             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
03526             return false;
03527         }
03528         if (!strcmp((const char*)buf, "conectix")) {
03529             fcsize--;   // skip footer (512 bytes)
03530             sizes[0] = 512; // sector size
03531             sizes[1] = buf[0x3b];   // sectors
03532             sizes[2] = buf[0x3a];   // heads
03533             sizes[3] = SDL_SwapBE16((Bit16u)(*(Bit16s*)(buf + 0x38)));    // cylinders
03534 
03535                                                                 // Do translation (?)
03536             while ((sizes[2] < 128u) && (sizes[3] > 1023u)) {
03537                 sizes[2] <<= 1u;
03538                 sizes[3] >>= 1u;
03539             }
03540 
03541             if (sizes[3]>1023) {
03542                 // Set x/255/63
03543                 sizes[2] = 255;
03544                 sizes[3] = fcsize / sizes[2] / sizes[1];
03545             }
03546 
03547             LOG_MSG("VHD image detected: %u,%u,%u,%u",
03548                 (unsigned int)sizes[0], (unsigned int)sizes[1], (unsigned int)sizes[2], (unsigned int)sizes[3]);
03549             if (sizes[3]>1023) LOG_MSG("WARNING: cylinders>1023, INT13 will not work unless extensions are used");
03550             yet_detected = true;
03551         }
03552 
03553         fseeko64(diskfile, 0L, SEEK_SET);
03554         if (fread(buf, sizeof(Bit8u), 512, diskfile)<512) {
03555             fclose(diskfile);
03556             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
03557             return false;
03558         }
03559         fclose(diskfile);
03560         // check it is not dynamic VHD image
03561         if (!strcmp((const char*)buf, "conectix")) {
03562             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_DYNAMIC_VHD_UNSUPPORTED"));
03563             return false;
03564         }
03565         // check MBR signature for unknown images
03566         if (!yet_detected && ((buf[510] != 0x55) || (buf[511] != 0xaa))) {
03567             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY"));
03568             return false;
03569         }
03570         // check MBR partition entry 1
03571         Bitu starthead = buf[0x1bf];
03572         Bitu startsect = (buf[0x1c0] & 0x3fu) - 1u;
03573         Bitu startcyl = (unsigned char)buf[0x1c1] | (unsigned int)((buf[0x1c0] & 0xc0) << 2u);
03574         Bitu endcyl = (unsigned char)buf[0x1c5] | (unsigned int)((buf[0x1c4] & 0xc0) << 2u);
03575 
03576         Bitu heads = buf[0x1c3] + 1u;
03577         Bitu sectors = buf[0x1c4] & 0x3fu;
03578 
03579         Bitu pe1_size = host_readd(&buf[0x1ca]);
03580         if (pe1_size != 0) {
03581             Bitu part_start = startsect + sectors * starthead +
03582                 startcyl * sectors*heads;
03583             Bitu part_end = heads * sectors*endcyl;
03584             Bits part_len = (Bits)(part_end - part_start);
03585             // partition start/end sanity check
03586             // partition length should not exceed file length
03587             // real partition size can be a few cylinders less than pe1_size
03588             // if more than 1023 cylinders see if first partition fits
03589             // into 1023, else bail.
03590             if ((part_len<0) || ((Bitu)part_len > pe1_size) || (pe1_size > fcsize) ||
03591                 ((pe1_size - (Bitu)part_len) / (sectors*heads)>2u) ||
03592                 ((pe1_size / (heads*sectors))>1023u)) {
03593                 //LOG_MSG("start(c,h,s) %u,%u,%u",startcyl,starthead,startsect);
03594                 //LOG_MSG("endcyl %u heads %u sectors %u",endcyl,heads,sectors);
03595                 //LOG_MSG("psize %u start %u end %u",pe1_size,part_start,part_end);
03596             }
03597             else if (!yet_detected) {
03598                 sizes[0] = 512; sizes[1] = sectors;
03599                 sizes[2] = heads; sizes[3] = (Bit16u)(fcsize / (heads*sectors));
03600                 if (sizes[3]>1023) sizes[3] = 1023;
03601                 yet_detected = true;
03602             }
03603         }
03604         if (!yet_detected) {
03605             // Try bximage disk geometry
03606             Bitu cylinders = (Bitu)(fcsize / (16 * 63));
03607             // Int13 only supports up to 1023 cylinders
03608             // For mounting unknown images we could go up with the heads to 255
03609             if ((cylinders * 16 * 63 == fcsize) && (cylinders<1024)) {
03610                 yet_detected = true;
03611                 sizes[0] = 512; sizes[1] = 63; sizes[2] = 16; sizes[3] = cylinders;
03612             }
03613         }
03614 
03615         if (yet_detected)
03616         {
03617             //"Image geometry auto detection: -size %u,%u,%u,%u\r\n",
03618             //sizes[0],sizes[1],sizes[2],sizes[3]);
03619             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_AUTODET_VALUES"), sizes[0], sizes[1], sizes[2], sizes[3]);
03620             return true;
03621         }
03622         else {
03623             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY"));
03624             return false;
03625         }
03626     }
03627 
03628     bool MountIso(const char drive, const std::vector<std::string> &paths, const signed char ide_index, const bool ide_slave) {
03629         //mount cdrom
03630         if (Drives[drive - 'A']) {
03631             WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
03632             return false;
03633         }
03634         Bit8u mediaid = 0xF8;
03635         MSCDEX_SetCDInterface(CDROM_USE_SDL, -1);
03636         // create new drives for all images
03637         std::vector<DOS_Drive*> isoDisks;
03638         std::vector<std::string>::size_type i;
03639         std::vector<DOS_Drive*>::size_type ct;
03640         for (i = 0; i < paths.size(); i++) {
03641             int error = -1;
03642             DOS_Drive* newDrive = new isoDrive(drive, paths[i].c_str(), mediaid, error);
03643             isoDisks.push_back(newDrive);
03644             switch (error) {
03645             case 0: break;
03646             case 1: WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"));  break;
03647             case 2: WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED"));    break;
03648             case 3: WriteOut(MSG_Get("MSCDEX_ERROR_OPEN"));             break;
03649             case 4: WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES"));        break;
03650             case 5: WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT"));        break;
03651             case 6: WriteOut(MSG_Get("MSCDEX_INVALID_FILEFORMAT"));     break;
03652             default: WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR"));         break;
03653             }
03654             // error: clean up and leave
03655             if (error) {
03656                 for (ct = 0; ct < isoDisks.size(); ct++) {
03657                     delete isoDisks[ct];
03658                 }
03659                 return false;
03660             }
03661         }
03662         // Update DriveManager
03663         for (ct = 0; ct < isoDisks.size(); ct++) {
03664             DriveManager::AppendDisk(drive - 'A', isoDisks[ct]);
03665         }
03666         DriveManager::InitializeDrive(drive - 'A');
03667 
03668         // Set the correct media byte in the table 
03669         mem_writeb(Real2Phys(dos.tables.mediaid) + ((unsigned int)drive - 'A') * 2u, mediaid);
03670 
03671         // If instructed, attach to IDE controller as ATAPI CD-ROM device
03672         if (ide_index >= 0) IDE_CDROM_Attach(ide_index, ide_slave, drive - 'A');
03673 
03674         // Print status message (success)
03675         WriteOut(MSG_Get("MSCDEX_SUCCESS"));
03676         std::string tmp(paths[0]);
03677         for (i = 1; i < paths.size(); i++) {
03678             tmp += "; " + paths[i];
03679         }
03680         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str());
03681         return true;
03682     }
03683 
03684     imageDisk* MountImageNone(const char* fileName, const Bitu sizesOriginal[], const int reserved_cylinders) {
03685         imageDisk* newImage = 0;
03686         Bitu sizes[4];
03687         sizes[0] = sizesOriginal[0];
03688         sizes[1] = sizesOriginal[1];
03689         sizes[2] = sizesOriginal[2];
03690         sizes[3] = sizesOriginal[3];
03691 
03692         //check for VHD files
03693         if (sizes[0] == 0 /* auto detect size */) {
03694             const char *ext = strrchr(fileName, '.');
03695             if (ext != NULL) {
03696                 if (!strcasecmp(ext, ".vhd")) {
03697                     imageDiskVHD::ErrorCodes ret = imageDiskVHD::Open(fileName, false, &newImage);
03698                     switch (ret) {
03699                     case imageDiskVHD::ERROR_OPENING: WriteOut(MSG_Get("VHD_ERROR_OPENING")); break;
03700                     case imageDiskVHD::INVALID_DATA: WriteOut(MSG_Get("VHD_INVALID_DATA")); break;
03701                     case imageDiskVHD::UNSUPPORTED_TYPE: WriteOut(MSG_Get("VHD_UNSUPPORTED_TYPE")); break;
03702                     case imageDiskVHD::ERROR_OPENING_PARENT: WriteOut(MSG_Get("VHD_ERROR_OPENING_PARENT")); break;
03703                     case imageDiskVHD::PARENT_INVALID_DATA: WriteOut(MSG_Get("VHD_PARENT_INVALID_DATA")); break;
03704                     case imageDiskVHD::PARENT_UNSUPPORTED_TYPE: WriteOut(MSG_Get("VHD_PARENT_UNSUPPORTED_TYPE")); break;
03705                     case imageDiskVHD::PARENT_INVALID_MATCH: WriteOut(MSG_Get("VHD_PARENT_INVALID_MATCH")); break;
03706                     case imageDiskVHD::PARENT_INVALID_DATE: WriteOut(MSG_Get("VHD_PARENT_INVALID_DATE")); break;
03707                     default: break;
03708                     }
03709                     return newImage;
03710                 }
03711             }
03712         }
03713 
03714         Bit32u imagesize;
03715         /* auto-fill: sector size */
03716         if (sizes[0] == 0) sizes[0] = 512;
03717 
03718         FILE *newDisk = fopen64(fileName, "rb+");
03719         if (!newDisk) {
03720             WriteOut("Unable to open '%s'\n", fileName);
03721             return NULL;
03722         }
03723 
03724         QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(newDisk);
03725 
03726         Bit64u sectors;
03727         if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)) {
03728             Bit32u cluster_size = 1u << qcow2_header.cluster_bits;
03729             if ((sizes[0] < 512) || ((cluster_size % sizes[0]) != 0)) {
03730                 WriteOut("Sector size must be larger than 512 bytes and evenly divide the image cluster size of %lu bytes.\n", cluster_size);
03731                 return 0;
03732             }
03733             sectors = (Bit64u)qcow2_header.size / (Bit64u)sizes[0];
03734             imagesize = (Bit32u)(qcow2_header.size / 1024L);
03735             setbuf(newDisk, NULL);
03736             newImage = new QCow2Disk(qcow2_header, newDisk, (Bit8u *)fileName, imagesize, sizes[0], (imagesize > 2880));
03737         }
03738         else {
03739             char tmp[256];
03740 
03741             fseeko64(newDisk, 0L, SEEK_SET);
03742             fread(tmp, 256, 1, newDisk); // look for magic signatures
03743 
03744             const char *ext = strrchr(fileName,'.');
03745 
03746             if (ext != NULL && !strcasecmp(ext, ".d88")) {
03747                 fseeko64(newDisk, 0L, SEEK_END);
03748                 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0];
03749                 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */
03750                 setbuf(newDisk, NULL);
03751                 newImage = new imageDiskD88(newDisk, (Bit8u *)fileName, imagesize, (imagesize > 2880));
03752             }
03753             else if (!memcmp(tmp, "VFD1.", 5)) { /* FDD files */
03754                 fseeko64(newDisk, 0L, SEEK_END);
03755                 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0];
03756                 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */
03757                 setbuf(newDisk, NULL);
03758                 newImage = new imageDiskVFD(newDisk, (Bit8u *)fileName, imagesize, (imagesize > 2880));
03759             }
03760             else {
03761                 fseeko64(newDisk, 0L, SEEK_END);
03762                 sectors = (Bit64u)ftello64(newDisk) / (Bit64u)sizes[0];
03763                 imagesize = (Bit32u)(sectors / 2); /* orig. code wants it in KBs */
03764                 setbuf(newDisk, NULL);
03765                 newImage = new imageDisk(newDisk, (Bit8u *)fileName, imagesize, (imagesize > 2880));
03766             }
03767         }
03768 
03769         /* sometimes imageDisk is able to determine geometry automatically (HDI images) */
03770         if (newImage) {
03771             if (newImage->sectors != 0 && newImage->heads != 0 && newImage->cylinders != 0 && newImage->sector_size != 0) {
03772                 /* prevent the code below from changing the geometry */
03773                 sizes[0] = newImage->sector_size;
03774                 sizes[1] = newImage->sectors;
03775                 sizes[2] = newImage->heads;
03776                 sizes[3] = newImage->cylinders;
03777             }
03778         }
03779 
03780         /* auto-fill: sector/track count */
03781         if (sizes[1] == 0) sizes[1] = 63;
03782         /* auto-fill: head/cylinder count */
03783         if (sizes[3]/*cylinders*/ == 0 && sizes[2]/*heads*/ == 0) {
03784             sizes[2] = 16; /* typical hard drive, unless a very old drive */
03785             sizes[3]/*cylinders*/ = (Bitu)((Bit64u)sectors / (Bit64u)sizes[2]/*heads*/ / (Bit64u)sizes[1]/*sectors/track*/);
03786 
03787             if (IS_PC98_ARCH) {
03788                 /* TODO: PC-98 has it's own issues with a 4096-cylinder limit */
03789             }
03790             else {
03791                 /* INT 13h mapping, deal with 1024-cyl limit */
03792                 while (sizes[3] > 1024) {
03793                     if (sizes[2] >= 255) break; /* nothing more we can do */
03794 
03795                     /* try to generate head count 16, 32, 64, 128, 255 */
03796                     sizes[2]/*heads*/ *= 2;
03797                     if (sizes[2] >= 256) sizes[2] = 255;
03798 
03799                     /* and recompute cylinders */
03800                     sizes[3]/*cylinders*/ = (Bitu)((Bit64u)sectors / (Bit64u)sizes[2]/*heads*/ / (Bit64u)sizes[1]/*sectors/track*/);
03801                 }
03802             }
03803         }
03804 
03805         LOG(LOG_MISC, LOG_NORMAL)("Mounting image as C/H/S %u/%u/%u with %u bytes/sector",
03806             (unsigned int)sizes[3], (unsigned int)sizes[2], (unsigned int)sizes[1], (unsigned int)sizes[0]);
03807 
03808         if (imagesize > 2880) newImage->Set_Geometry(sizes[2], sizes[3], sizes[1], sizes[0]);
03809         if (reserved_cylinders > 0) newImage->Set_Reserved_Cylinders((Bitu)reserved_cylinders);
03810 
03811         return newImage;
03812     }
03813 };
03814 
03815 void IMGMOUNT_ProgramStart(Program * * make) {
03816     *make=new IMGMOUNT;
03817 }
03818 
03819 Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp);
03820 Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile);
03821 const char* DOS_GetLoadedLayout(void);
03822 
03823 class KEYB : public Program {
03824 public:
03825     void Run(void);
03826 };
03827 
03828 void KEYB::Run(void) {
03829     // codepage 949 start
03830     std::string temp_codepage;
03831     temp_codepage="949";
03832     if (cmd->FindString("ko",temp_codepage,false)) {
03833         dos.loaded_codepage=949;
03834         const char* layout_name = DOS_GetLoadedLayout();
03835         WriteOut(MSG_Get("PROGRAM_KEYB_INFO_LAYOUT"),dos.loaded_codepage,layout_name);
03836         return;
03837     }
03838     // codepage 949 end
03839     if (cmd->FindCommand(1,temp_line)) {
03840         if (cmd->FindString("?",temp_line,false)) {
03841             WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
03842         } else {
03843             /* first parameter is layout ID */
03844             Bitu keyb_error=0;
03845             std::string cp_string;
03846             Bit32s tried_cp = -1;
03847             if (cmd->FindCommand(2,cp_string)) {
03848                 /* second parameter is codepage number */
03849                 tried_cp=atoi(cp_string.c_str());
03850                 char cp_file_name[256];
03851                 if (cmd->FindCommand(3,cp_string)) {
03852                     /* third parameter is codepage file */
03853                     strcpy(cp_file_name, cp_string.c_str());
03854                 } else {
03855                     /* no codepage file specified, use automatic selection */
03856                     strcpy(cp_file_name, "auto");
03857                 }
03858 
03859                 keyb_error=DOS_LoadKeyboardLayout(temp_line.c_str(), tried_cp, cp_file_name);
03860             } else {
03861                 keyb_error=DOS_SwitchKeyboardLayout(temp_line.c_str(), tried_cp);
03862             }
03863             switch (keyb_error) {
03864                 case KEYB_NOERROR:
03865                     WriteOut(MSG_Get("PROGRAM_KEYB_NOERROR"),temp_line.c_str(),dos.loaded_codepage);
03866                     break;
03867                 case KEYB_FILENOTFOUND:
03868                     WriteOut(MSG_Get("PROGRAM_KEYB_FILENOTFOUND"),temp_line.c_str());
03869                     WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
03870                     break;
03871                 case KEYB_INVALIDFILE:
03872                     WriteOut(MSG_Get("PROGRAM_KEYB_INVALIDFILE"),temp_line.c_str());
03873                     break;
03874                 case KEYB_LAYOUTNOTFOUND:
03875                     WriteOut(MSG_Get("PROGRAM_KEYB_LAYOUTNOTFOUND"),temp_line.c_str(),tried_cp);
03876                     break;
03877                 case KEYB_INVALIDCPFILE:
03878                     WriteOut(MSG_Get("PROGRAM_KEYB_INVCPFILE"),temp_line.c_str());
03879                     WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
03880                     break;
03881                 default:
03882                     LOG(LOG_DOSMISC,LOG_ERROR)("KEYB:Invalid returncode %x",(int)keyb_error);
03883                     break;
03884             }
03885         }
03886     } else {
03887         /* no parameter in the command line, just output codepage info and possibly loaded layout ID */
03888         const char* layout_name = DOS_GetLoadedLayout();
03889         if (layout_name==NULL) {
03890             WriteOut(MSG_Get("PROGRAM_KEYB_INFO"),dos.loaded_codepage);
03891         } else {
03892             WriteOut(MSG_Get("PROGRAM_KEYB_INFO_LAYOUT"),dos.loaded_codepage,layout_name);
03893         }
03894     }
03895 }
03896 
03897 static void KEYB_ProgramStart(Program * * make) {
03898     *make=new KEYB;
03899 }
03900 
03901 // MODE
03902 
03903 class MODE : public Program {
03904 public:
03905     void Run(void);
03906 };
03907 
03908 void MODE::Run(void) {
03909     Bit16u rate=0,delay=0,mode;
03910     if (!cmd->FindCommand(1,temp_line) || temp_line=="/?") {
03911         WriteOut(MSG_Get("PROGRAM_MODE_USAGE"));
03912         return;
03913     }
03914     else if (strcasecmp(temp_line.c_str(),"con")==0 || strcasecmp(temp_line.c_str(),"con:")==0) {
03915         if (cmd->GetCount()!=3) goto modeparam;
03916         if (cmd->FindStringBegin("rate=", temp_line,false)) rate= atoi(temp_line.c_str());
03917         if (cmd->FindStringBegin("delay=",temp_line,false)) delay=atoi(temp_line.c_str());
03918         if (rate<1 || rate>32 || delay<1 || delay>4) goto modeparam;
03919         IO_Write(0x60,0xf3); IO_Write(0x60,(Bit8u)(((delay-1)<<5)|(32-rate)));
03920         return;
03921     }
03922     else if (cmd->GetCount()>1) goto modeparam;
03923     else if (strcasecmp(temp_line.c_str(),"mono")==0) mode=7;
03924     else if (machine==MCH_HERC) goto modeparam;
03925     else if (strcasecmp(temp_line.c_str(),"co80")==0) mode=3;
03926     else if (strcasecmp(temp_line.c_str(),"bw80")==0) mode=2;
03927     else if (strcasecmp(temp_line.c_str(),"co40")==0) mode=1;
03928     else if (strcasecmp(temp_line.c_str(),"bw40")==0) mode=0;
03929     else goto modeparam;
03930     mem_writeb(BIOS_CONFIGURATION,(mem_readb(BIOS_CONFIGURATION)&0xcf)|((mode==7)?0x30:0x20));
03931     reg_ax=mode;
03932     CALLBACK_RunRealInt(0x10);
03933     return;
03934 modeparam:
03935     WriteOut(MSG_Get("PROGRAM_MODE_INVALID_PARAMETERS"));
03936     return;
03937 }
03938 
03939 static void MODE_ProgramStart(Program * * make) {
03940     *make=new MODE;
03941 }
03942 /*
03943 // MORE
03944 class MORE : public Program {
03945 public:
03946     void Run(void);
03947 };
03948 
03949 void MORE::Run(void) {
03950     if (cmd->GetCount()) {
03951         WriteOut(MSG_Get("PROGRAM_MORE_USAGE"));
03952         return;
03953     }
03954     Bit16u ncols=mem_readw(BIOS_SCREEN_COLUMNS);
03955     Bit16u nrows=(Bit16u)mem_readb(BIOS_ROWS_ON_SCREEN_MINUS_1);
03956     Bit16u col=1,row=1;
03957     Bit8u c;Bit16u n=1;
03958     WriteOut("\n");
03959     while (n) {
03960         DOS_ReadFile(STDIN,&c,&n);
03961         if (n==0 || c==0x1a) break; // stop at EOF
03962         switch (c) {
03963             case 0x07: break;
03964             case 0x08: if (col>1) col--; break;
03965             case 0x09: col=((col+7)&~7)+1; break;
03966             case 0x0a: row++; break;
03967             case 0x0d: col=1; break;
03968             default: col++; break;
03969         }
03970         if (col>ncols) {col=1;row++;}
03971         DOS_WriteFile(STDOUT,&c,&n);
03972         if (row>=nrows) {
03973             WriteOut(MSG_Get("PROGRAM_MORE_MORE"));
03974             DOS_ReadFile(STDERR,&c,&n);
03975             if (c==0) DOS_ReadFile(STDERR,&c,&n); // read extended key
03976             WriteOut("\n\n");
03977             col=row=1;
03978         }
03979     }
03980 }
03981 
03982 static void MORE_ProgramStart(Program * * make) {
03983     *make=new MORE;
03984 }
03985 */
03986 
03987 void REDOS_ProgramStart(Program * * make);
03988 void A20GATE_ProgramStart(Program * * make);
03989 void PC98UTIL_ProgramStart(Program * * make);
03990 void VESAMOED_ProgramStart(Program * * make);
03991 
03992 class NMITEST : public Program {
03993 public:
03994     void Run(void) {
03995         CPU_Raise_NMI();
03996     }
03997 };
03998 
03999 static void NMITEST_ProgramStart(Program * * make) {
04000     *make=new NMITEST;
04001 }
04002 
04003 class CAPMOUSE : public Program
04004 {
04005 public:
04006         void Run() override
04007     {
04008         auto val = 0;
04009         auto tmp = std::string("");
04010 
04011         if(cmd->GetCount() == 0 || cmd->FindExist("/?", true))
04012             val = 0;
04013         else if(cmd->FindExist("/C", false))
04014             val = 1;
04015         else if(cmd->FindExist("/R", false))
04016             val = 2;
04017 
04018         auto cap = false;
04019         switch(val)
04020         {
04021         case 2:
04022             break;
04023         case 1:
04024             cap = true;
04025             break;
04026         case 0:
04027         default:
04028             WriteOut("Mouse capture/release.\n\n");
04029             WriteOut("CAPMOUSE /[?|C|R]\n");
04030             WriteOut("  /? help\n");
04031             WriteOut("  /C capture mouse\n");
04032             WriteOut("  /R release mouse\n");
04033             return;
04034         }
04035 
04036         CaptureMouseNotify(!cap);
04037         GFX_CaptureMouse(cap);
04038         std::string msg;
04039         msg.append("Mouse ");
04040         msg.append(Mouse_IsLocked() ? "captured" : "released");
04041         msg.append("\n");
04042         WriteOut(msg.c_str());
04043     }
04044 };
04045 
04046 void CAPMOUSE_ProgramStart(Program** make)
04047 {
04048         *make = new CAPMOUSE;
04049 }
04050 
04051 void DOS_SetupPrograms(void) {
04052     /*Add Messages */
04053 
04054     MSG_Add("PROGRAM_MOUSE_INSTALL","Installed at PS/2 port.\n");
04055     MSG_Add("PROGRAM_MOUSE_VERTICAL","Reverse Y-axis enabled.\n");
04056     MSG_Add("PROGRAM_MOUSE_VERTICAL_BACK","Reverse Y-axis disabled.\n");
04057     MSG_Add("PROGRAM_MOUSE_UNINSTALL","Driver successfully unloaded...\n");
04058     MSG_Add("PROGRAM_MOUSE_ERROR","Already installed at PS/2 port.\n");
04059     MSG_Add("PROGRAM_MOUSE_NOINSTALLED","Driver is not installed.\n");
04060     MSG_Add("PROGRAM_MOUSE_HELP","Turns on/off mouse.\n\nMOUSE [/?] [/U] [/V]\n  /U: Uninstall\n  /V: Reverse Y-axis\n");
04061     MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n");
04062     MSG_Add("PROGRAM_MOUNT_STATUS_FORMAT","%-5s  %-58s %-12s\n");
04063     MSG_Add("PROGRAM_MOUNT_STATUS_ELTORITO", "Drive %c is mounted as el torito floppy\n");
04064     MSG_Add("PROGRAM_MOUNT_STATUS_RAMDRIVE", "Drive %c is mounted as ram drive\n");
04065     MSG_Add("PROGRAM_MOUNT_STATUS_2","Drive %c is mounted as %s\n");
04066     MSG_Add("PROGRAM_MOUNT_STATUS_1","The currently mounted drives are:\n");
04067     MSG_Add("PROGRAM_MOUNT_ERROR_1","Directory %s doesn't exist.\n");
04068     MSG_Add("PROGRAM_MOUNT_ERROR_2","%s isn't a directory\n");
04069     MSG_Add("PROGRAM_MOUNT_ILL_TYPE","Illegal type %s\n");
04070     MSG_Add("PROGRAM_MOUNT_ALREADY_MOUNTED","Drive %c already mounted with %s\n");
04071     MSG_Add("PROGRAM_MOUNT_USAGE",
04072         "Usage \033[34;1mMOUNT Drive-Letter Local-Directory\033[0m\n"
04073         "For example: MOUNT c %s\n"
04074         "This makes the directory %s act as the C: drive inside DOSBox.\n"
04075         "The directory has to exist.\n");
04076     MSG_Add("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED","Drive %c isn't mounted.\n");
04077     MSG_Add("PROGRAM_MOUNT_UMOUNT_SUCCESS","Drive %c has successfully been removed.\n");
04078     MSG_Add("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL","Virtual Drives can not be unMOUNTed.\n");
04079     MSG_Add("PROGRAM_MOUNT_WARNING_WIN","\033[31;1mMounting c:\\ is NOT recommended. Please mount a (sub)directory next time.\033[0m\n");
04080     MSG_Add("PROGRAM_MOUNT_WARNING_OTHER","\033[31;1mMounting / is NOT recommended. Please mount a (sub)directory next time.\033[0m\n");
04081 
04082     MSG_Add("PROGRAM_LOADFIX_ALLOC","%d kb allocated.\n");
04083     MSG_Add("PROGRAM_LOADFIX_DEALLOC","%d kb freed.\n");
04084     MSG_Add("PROGRAM_LOADFIX_DEALLOCALL","Used memory freed.\n");
04085     MSG_Add("PROGRAM_LOADFIX_ERROR","Memory allocation error.\n");
04086     MSG_Add("PROGRAM_LOADFIX_HELP",
04087         "Reduces the amount of available conventional or XMS memory\n\n"
04088         "LOADFIX [-xms] [-{ram}] [{program}]\n"
04089         "LOADFIX -f [-xms]\n\n"
04090         "  -xms        Allocates memory from XMS rather than conventional memory\n"
04091         "  -{ram}      Specifies the amount of memory to allocate in KB\n"
04092         "                 Defaults to 64kb for conventional memory; 1MB for XMS memory\n"
04093         "  -f          Frees previously allocated memory\n"
04094         "  {program}   Runs the specified program\n\n"
04095         "Examples:\n"
04096         "  LOADFIX game.exe     Allocates 64KB of conventional memory and runs game.exe\n"
04097         "  LOADFIX -128         Allocates 128KB of conventional memory\n"
04098         "  LOADFIX -xms         Allocates 1MB of XMS memory\n"
04099         "  LOADFIX -f           Frees allocated conventional memory\n");
04100 
04101     MSG_Add("MSCDEX_SUCCESS","MSCDEX installed.\n");
04102     MSG_Add("MSCDEX_ERROR_MULTIPLE_CDROMS","MSCDEX: Failure: Drive-letters of multiple CD-ROM drives have to be continuous.\n");
04103     MSG_Add("MSCDEX_ERROR_NOT_SUPPORTED","MSCDEX: Failure: Not yet supported.\n");
04104     MSG_Add("MSCDEX_ERROR_PATH","MSCDEX: Specified location is not a CD-ROM drive.\n");
04105     MSG_Add("MSCDEX_ERROR_OPEN","MSCDEX: Failure: Invalid file or unable to open.\n");
04106     MSG_Add("MSCDEX_TOO_MANY_DRIVES","MSCDEX: Failure: Too many CD-ROM drives (max: 5). MSCDEX Installation failed.\n");
04107     MSG_Add("MSCDEX_LIMITED_SUPPORT","MSCDEX: Mounted subdirectory: limited support.\n");
04108     MSG_Add("MSCDEX_INVALID_FILEFORMAT","MSCDEX: Failure: File is either no ISO/CUE image or contains errors.\n");
04109     MSG_Add("MSCDEX_UNKNOWN_ERROR","MSCDEX: Failure: Unknown error.\n");
04110 
04111     MSG_Add("PROGRAM_RESCAN_SUCCESS","Drive cache cleared.\n");
04112 
04113     MSG_Add("PROGRAM_INTRO",
04114         "\033[2J\033[32;1mWelcome to DOSBox\033[0m, an x86 emulator with sound and graphics.\n"
04115         "DOSBox creates a shell for you which looks like old plain DOS.\n"
04116         "\n"
04117         "\033[31;1mDOSBox will stop/exit without a warning if an error occurred!\033[0m\n"
04118         "\n"
04119         "\n" );
04120     if (machine == MCH_PC98) {
04121         MSG_Add("PROGRAM_INTRO_MENU_UP",
04122             "\033[44m\033[K\033[0m\n"
04123             "\033[44m\033[K\033[1m\033[1m\t\t\t\t\t\t\t  DOSBox Introduction \033[0m\n"
04124             "\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"
04125             "\033[44m\033[K\033[0m\n"
04126             );
04127     } else {
04128         MSG_Add("PROGRAM_INTRO_MENU_UP",
04129             "\033[44m\033[K\033[0m\n"
04130             "\033[44m\033[K\033[1m\033[1m\t\t\t\t\t\t\t  DOSBox Introduction \033[0m\n"
04131             "\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"
04132             "\033[44m\033[K\033[0m\n"
04133             );
04134     }
04135     MSG_Add("PROGRAM_INTRO_MENU_BASIC","Basic mount");
04136     MSG_Add("PROGRAM_INTRO_MENU_CDROM","CD-ROM support");
04137     MSG_Add("PROGRAM_INTRO_MENU_SPECIAL","Special keys");
04138     MSG_Add("PROGRAM_INTRO_MENU_USAGE","Usage");
04139     MSG_Add("PROGRAM_INTRO_MENU_INFO","Information");
04140     MSG_Add("PROGRAM_INTRO_MENU_QUIT","Quit");
04141     MSG_Add("PROGRAM_INTRO_MENU_BASIC_HELP","\n\033[1m   \033[1m\033[KMOUNT allows you to connect real hardware to DOSBox's emulated PC.\033[0m\n");
04142     MSG_Add("PROGRAM_INTRO_MENU_CDROM_HELP","\n\033[1m   \033[1m\033[KTo mount your CD-ROM in DOSBox, you have to specify some additional options\n   when mounting the CD-ROM.\033[0m\n");
04143     MSG_Add("PROGRAM_INTRO_MENU_SPECIAL_HELP","\n\033[1m   \033[1m\033[KSpecial key combinations used in DOSBox.\033[0m\n");
04144     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.\033[0m\n");
04145     MSG_Add("PROGRAM_INTRO_MENU_INFO_HELP","\n\033[1m   \033[1m\033[KHow to get more information about DOSBox.\033[0m\n");
04146     MSG_Add("PROGRAM_INTRO_MENU_QUIT_HELP","\n\033[1m   \033[1m\033[KExit from Intro.\033[0m\n");
04147     MSG_Add("PROGRAM_INTRO_USAGE_TOP",
04148         "\033[2J\033[32;1mAn overview of the command line options you can give to DOSBox.\033[0m\n"
04149         "Windows Users must open cmd.exe or command.com or edit the shortcut to\n"
04150         "DOSBox.exe for this.\n\n"
04151         "dosbox [name] [-exit] [-c command] [-fullscreen] [-conf congfigfile]\n"
04152         "       [-lang languagefile] [-machine machinetype] [-noconsole]\n"
04153         "       [-startmapper] [-noautoexec] [-scaler scaler | -forcescaler scaler]\n       [-version]\n\n"
04154         );
04155     MSG_Add("PROGRAM_INTRO_USAGE_1",
04156         "\033[33;1m  name\033[0m\n"
04157         "\tIf name is a directory it will mount that as the C: drive.\n"
04158         "\tIf name is an executable it will mount the directory of name\n"
04159         "\tas the C: drive and execute name.\n\n"
04160         "\033[33;1m  -exit\033[0m\n"
04161         "\tDOSBox will close itself when the DOS application name ends.\n\n"
04162         "\033[33;1m  -c\033[0m command\n"
04163         "\tRuns the specified command before running name. Multiple commands\n"
04164         "\tcan be specified. Each command should start with -c, though.\n"
04165         "\tA command can be: an Internal Program, a DOS command or an executable\n"
04166         "\ton a mounted drive.\n"
04167         );
04168     MSG_Add("PROGRAM_INTRO_USAGE_2",
04169         "\033[33;1m  -fullscreen\033[0m\n"
04170         "\tStarts DOSBox in fullscreen mode.\n\n"
04171         "\033[33;1m  -conf\033[0m configfile\n"
04172         "\tStart DOSBox with the options specified in configfile.\n"
04173         "\tSee README for more details.\n\n"
04174         "\033[33;1m  -lang\033[0m languagefile\n"
04175         "\tStart DOSBox using the language specified in languagefile.\n\n"
04176         "\033[33;1m  -noconsole\033[0m (Windows Only)\n"
04177         "\tStart DOSBox without showing the console window. Output will\n"
04178         "\tbe redirected to stdout.txt and stderr.txt\n"
04179         );
04180     MSG_Add("PROGRAM_INTRO_USAGE_3",
04181         "\033[33;1m  -machine\033[0m machinetype\n"
04182         "\tSetup DOSBox to emulate a specific type of machine. Valid choices are:\n"
04183         "\thercules, cga, pcjr, tandy, vga (default). The machinetype affects\n"
04184         "\tboth the videocard and the available soundcards.\n\n"
04185         "\033[33;1m  -startmapper\033[0m\n"
04186         "\tEnter the keymapper directly on startup. Useful for people with\n"
04187         "\tkeyboard problems.\n\n"
04188         "\033[33;1m  -noautoexec\033[0m\n"
04189         "\tSkips the [autoexec] section of the loaded configuration file.\n\n"
04190         "\033[33;1m  -version\033[0m\n"
04191         "\toutput version information and exit. Useful for frontends.\n"
04192         );
04193     MSG_Add("PROGRAM_INTRO_INFO",
04194         "\033[32;1mInformation:\033[0m\n\n"
04195         "For information about basic mount, type \033[34;1mintro mount\033[0m\n"
04196         "For information about CD-ROM support, type \033[34;1mintro cdrom\033[0m\n"
04197         "For information about special keys, type \033[34;1mintro special\033[0m\n"
04198         "For information about usage, type \033[34;1mintro usage\033[0m\n\n"
04199         "For the latest version of DOSBox, go to \033[34;1mhttp://www.dosbox.com\033[0m\n"
04200         "\n"
04201         "For more information about DOSBox, read README first!\n"
04202         "\n"
04203         "\033[34;1mhttp://www.dosbox.com/wiki\033[0m\n"
04204         "\033[34;1mhttp://vogons.zetafleet.com\033[0m\n"
04205         );
04206     MSG_Add("PROGRAM_INTRO_MOUNT_START",
04207         "\033[32;1mHere are some commands to get you started:\033[0m\n"
04208         "Before you can use the files located on your own filesystem,\n"
04209         "you have to mount the directory containing the files.\n"
04210         "\n"
04211         );
04212     if (machine == MCH_PC98) {
04213         MSG_Add("PROGRAM_INTRO_MOUNT_WINDOWS",
04214             "\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"
04215             "\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"
04216             "\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"
04217             "\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"
04218             "\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"
04219             "\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"
04220             "\x86\x46 \033[32mmount a c:\\dosgames\\\033[37m will create an A drive with c:\\dosgames as contents.\x86\x46\n"
04221             "\x86\x46                                                                          \x86\x46\n"
04222             "\x86\x46 \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory.  \033[37m  \x86\x46\n"
04223             "\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"
04224             "\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"
04225             "\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"
04226             "\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"
04227             "\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"
04228             "\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"
04229             );
04230         MSG_Add("PROGRAM_INTRO_MOUNT_OTHER",
04231             "\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"
04232             "\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"
04233             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04234             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04235             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04236             "\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"
04237             "\x86\x46 \033[32mmount a ~/dosgames\033[37m will create an A drive with ~/dosgames as contents.\x86\x46\n"
04238             "\x86\x46                                                                       \x86\x46\n"
04239             "\x86\x46 \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory. \033[37m  \x86\x46\n"
04240             "\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"
04241             "\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"
04242             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04243             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04244             "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
04245             "\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"
04246             );
04247         MSG_Add("PROGRAM_INTRO_MOUNT_END",
04248             "When the mount has successfully completed you can type \033[34;1ma:\033[0m to go to your freshly\n"
04249             "mounted A-drive. Typing \033[34;1mdir\033[0m there will show its contents."
04250             " \033[34;1mcd\033[0m will allow you to\n"
04251             "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n"
04252             "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n"
04253             );
04254     } else {
04255         MSG_Add("PROGRAM_INTRO_MOUNT_WINDOWS",
04256             "\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"
04257             "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
04258             "\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"
04259             "\xBA \033[32mmount c c:\\dosgames\\\033[37m will create a C drive with c:\\dosgames as contents.\xBA\n"
04260             "\xBA                                                                         \xBA\n"
04261             "\xBA \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory.  \033[37m \xBA\n"
04262             "\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"
04263             "\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"
04264             "\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"
04265             );
04266         MSG_Add("PROGRAM_INTRO_MOUNT_OTHER",
04267             "\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"
04268             "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
04269             "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n"
04270             "\xBA \033[32mmount c ~/dosgames\033[37m will create a C drive with ~/dosgames as contents.\xBA\n"
04271             "\xBA                                                                      \xBA\n"
04272             "\xBA \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory.\033[37m  \xBA\n"
04273             "\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"
04274             "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
04275             "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n"
04276             );
04277         MSG_Add("PROGRAM_INTRO_MOUNT_END",
04278             "When the mount has successfully completed you can type \033[34;1mc:\033[0m to go to your freshly\n"
04279             "mounted C-drive. Typing \033[34;1mdir\033[0m there will show its contents."
04280             " \033[34;1mcd\033[0m will allow you to\n"
04281             "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n"
04282             "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n"
04283             );
04284     }
04285     MSG_Add("PROGRAM_INTRO_CDROM",
04286         "\033[2J\033[32;1mHow to mount a Real/Virtual CD-ROM Drive in DOSBox:\033[0m\n"
04287         "DOSBox provides CD-ROM emulation on several levels.\n"
04288         "\n"
04289         "The \033[33mbasic\033[0m level works on all CD-ROM drives and normal directories.\n"
04290         "It installs MSCDEX and marks the files read-only.\n"
04291         "Usually this is enough for most games:\n"
04292         "\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"
04293         "If it doesn't work you might have to tell DOSBox the label of the CD-ROM:\n"
04294         "\033[34;1mmount d C:\\example -t cdrom -label CDLABEL\033[0m\n"
04295         "\n"
04296         "The \033[33mnext\033[0m level adds some low-level support.\n"
04297         "Therefore only works on CD-ROM drives:\n"
04298         "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0\033[0m\n"
04299         "\n"
04300         "The \033[33mlast\033[0m level of support depends on your Operating System:\n"
04301         "For \033[1mWindows 2000\033[0m, \033[1mWindows XP\033[0m and \033[1mLinux\033[0m:\n"
04302         "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-ioctl\033[0m\n"
04303         "For \033[1mWindows 9x\033[0m with a ASPI layer installed:\n"
04304         "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-aspi\033[0m\n"
04305         "\n"
04306         "Replace \033[0;31mD:\\\033[0m with the location of your CD-ROM.\n"
04307         "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"
04308         "\033[34;1mmount -cd\033[0m\n"
04309         );
04310     MSG_Add("PROGRAM_INTRO_SPECIAL",
04311         "\033[2J\033[32;1mSpecial keys:\033[0m\n"
04312         "These are the default keybindings.\n"
04313         "They can be changed in the \033[33mkeymapper\033[0m.\n"
04314         "\n"
04315         "\033[33;1mALT-ENTER\033[0m   : Go full screen and back.\n"
04316         "\033[33;1mALT-PAUSE\033[0m   : Pause DOSBox.\n"
04317         "\033[33;1mCTRL-1~4\033[0m    : Use normal/full/dynamic/simple core.\n"
04318         "\033[33;1mCTRL-=\033[0m      : Maximize CPU cycles.\n"
04319         "\033[33;1mALT-F11\033[0m     : Unlock/Lock speed.\n"
04320         "\033[33;1mCTRL-F1\033[0m     : Start the \033[33mkeymapper\033[0m.\n"
04321         "\033[33;1mCTRL-F4\033[0m     : Update directory cache for all drives! Swap mounted disk-image.\n"
04322         "\033[33;1mCTRL-ALT-F5\033[0m : Start/Stop creating a movie of the screen.\n"
04323         "\033[33;1mCTRL-F5\033[0m     : Save a screenshot.\n"
04324         "\033[33;1mCTRL-F6\033[0m     : Start/Stop recording sound output to a wave file.\n"
04325         "\033[33;1mCTRL-ALT-F7\033[0m : Start/Stop recording of OPL commands.\n"
04326         "\033[33;1mCTRL-ALT-F8\033[0m : Start/Stop the recording of raw MIDI commands.\n"
04327         "\033[33;1mCTRL-F7\033[0m     : Decrease frameskip.\n"
04328         "\033[33;1mCTRL-F8\033[0m     : Increase frameskip.\n"
04329         "\033[33;1mCTRL-F9\033[0m     : Kill DOSBox.\n"
04330         "\033[33;1mCTRL-F10\033[0m    : Capture/Release the mouse.\n"
04331         "\033[33;1mCTRL-F11\033[0m    : Slow down emulation (Decrease DOSBox Cycles).\n"
04332         "\033[33;1mCTRL-F12\033[0m    : Speed up emulation (Increase DOSBox Cycles).\n"
04333         "\033[33;1mALT-F12\033[0m     : Unlock speed (turbo button/fast forward).\n"
04334         );
04335     MSG_Add("PROGRAM_BOOT_NOT_EXIST","Bootdisk file does not exist.  Failing.\n");
04336     MSG_Add("PROGRAM_BOOT_NOT_OPEN","Cannot open bootdisk file.  Failing.\n");
04337     MSG_Add("PROGRAM_BOOT_WRITE_PROTECTED","Image file is read-only! Might create problems.\n");
04338     MSG_Add("PROGRAM_BOOT_PRINT_ERROR","This command boots DOSBox from either a floppy or hard disk image.\n\n"
04339         "For this command, one can specify a succession of floppy disks swappable\n"
04340         "by pressing Ctrl-F4, and -l specifies the mounted drive to boot from.  If\n"
04341         "no drive letter is specified, this defaults to booting from the A drive.\n"
04342         "The only bootable drive letters are A, C, and D.  For booting from a hard\n"
04343         "drive (C or D), the image should have already been mounted using the\n"
04344         "\033[34;1mIMGMOUNT\033[0m command.\n\n"
04345         "The syntax of this command is:\n\n"
04346         "\033[34;1mBOOT [diskimg1.img diskimg2.img] [-l driveletter]\033[0m\n"
04347         );
04348     MSG_Add("PROGRAM_BOOT_UNABLE","Unable to boot off of drive %c");
04349     MSG_Add("PROGRAM_BOOT_IMAGE_OPEN","Opening image file: %s\n");
04350     MSG_Add("PROGRAM_BOOT_IMAGE_NOT_OPEN","Cannot open %s");
04351     MSG_Add("PROGRAM_BOOT_BOOT","Booting from drive %c...\n");
04352     MSG_Add("PROGRAM_BOOT_CART_WO_PCJR","PCjr cartridge found, but machine is not PCjr");
04353     MSG_Add("PROGRAM_BOOT_CART_LIST_CMDS","Available PCjr cartridge commandos:%s");
04354     MSG_Add("PROGRAM_BOOT_CART_NO_CMDS","No PCjr cartridge commandos found");
04355 
04356     MSG_Add("VHD_ERROR_OPENING", "Could not open the specified VHD file.\n");
04357     MSG_Add("VHD_INVALID_DATA", "The specified VHD file is corrupt and cannot be opened.\n");
04358     MSG_Add("VHD_UNSUPPORTED_TYPE", "The specified VHD file is of an unsupported type.\n");
04359     MSG_Add("VHD_ERROR_OPENING_PARENT", "The parent of the specified VHD file could not be found.\n");
04360     MSG_Add("VHD_PARENT_INVALID_DATA", "The parent of the specified VHD file is corrupt and cannot be opened.\n");
04361     MSG_Add("VHD_PARENT_UNSUPPORTED_TYPE", "The parent of the specified VHD file is of an unsupported type.\n");
04362     MSG_Add("VHD_PARENT_INVALID_MATCH", "The parent of the specified VHD file does not contain the expected identifier.\n");
04363     MSG_Add("VHD_PARENT_INVALID_DATE", "The parent of the specified VHD file has been changed and cannot be loaded.\n");
04364 
04365     MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_DRIVE","Must specify drive letter to mount image at.\n");
04366     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");
04367     MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_GEOMETRY",
04368         "For \033[33mCD-ROM\033[0m images:   \033[34;1mIMGMOUNT drive-letter location-of-image -t iso\033[0m\n"
04369         "\n"
04370         "For \033[33mhardrive\033[0m images: Must specify drive geometry for hard drives:\n"
04371         "bytes_per_sector, sectors_per_cylinder, heads_per_cylinder, cylinder_count.\n"
04372         "\033[34;1mIMGMOUNT drive-letter location-of-image -size bps,spc,hpc,cyl\033[0m\n");
04373     MSG_Add("PROGRAM_IMGMOUNT_INVALID_IMAGE","Could not load image file.\n"
04374         "Check that the path is correct and the image is accessible.\n");
04375     MSG_Add("PROGRAM_IMGMOUNT_DYNAMIC_VHD_UNSUPPORTED", "Dynamic VHD files are not supported.\n");
04376     MSG_Add("PROGRAM_IMGMOUNT_INVALID_GEOMETRY","Could not extract drive geometry from image.\n"
04377         "Use parameter -size bps,spc,hpc,cyl to specify the geometry.\n");
04378     MSG_Add("PROGRAM_IMGMOUNT_AUTODET_VALUES","Image geometry auto detection: -size %u,%u,%u,%u\n");
04379     MSG_Add("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED","Type \"%s\" is unsupported. Specify \"hdd\" or \"floppy\" or\"iso\".\n");
04380     MSG_Add("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED","Format \"%s\" is unsupported. Specify \"fat\" or \"iso\" or \"none\".\n");
04381     MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_FILE","Must specify file-image to mount.\n");
04382     MSG_Add("PROGRAM_IMGMOUNT_FILE_NOT_FOUND","Image file not found.\n");
04383     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");
04384     MSG_Add("PROGRAM_IMGMOUNT_ALREADY_MOUNTED","Drive already mounted at that letter.\n");
04385     MSG_Add("PROGRAM_IMGMOUNT_CANT_CREATE","Can't create drive from file.\n");
04386     MSG_Add("PROGRAM_IMGMOUNT_MOUNT_NUMBER","Drive number %d mounted as %s\n");
04387     MSG_Add("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE", "The image must be on a host or local drive.\n");
04388     MSG_Add("PROGRAM_IMGMOUNT_MULTIPLE_NON_CUEISO_FILES", "Using multiple files is only supported for cue/iso images.\n");
04389 
04390     MSG_Add("PROGRAM_IMGMOUNT_HELP",
04391         "Mounts hard drive and optical disc images.\n"
04392         "IMGMOUNT drive filename [-t floppy] [-fs fat] [-size ss,s,h,c]\n"
04393         "IMGMOUNT drive filename [-t hdd] [-fs fat] [-size ss,s,h,c] [-ide 1m|1s|2m|2s]\n"
04394         "IMGMOUNT driveLoc filename -fs none [-size ss,s,h,c] [-reservecyl #]\n"
04395         "IMGMOUNT drive filename -t iso [-fs iso]\n"
04396         "IMGMOUNT drive -t floppy -el-torito cdDrive\n"
04397         "IMGMOUNT drive -t ram -size driveSize\n"
04398         "IMGMOUNT -u drive|driveLocation\n"
04399         " drive               Drive letter to mount the image at\n"
04400         " driveLoc            Location to mount drive, where 0-1 are FDDs, 2-5 are HDDs\n"
04401         " filename            Filename of the image to mount\n"
04402         " -t iso              Image type is optical disc iso or cue / bin image\n"
04403         " -t floppy           Image type is floppy\n"
04404         " -t hdd              Image type is hard disk; VHD and HDI files are supported\n"
04405         " -t ram              Image type is ramdrive\n"
04406         " -fs iso             File system is ISO 9660\n"
04407         " -fs fat             File system is FAT; FAT12 and FAT16 are supported\n"
04408         " -fs none            Do not detect file system\n"
04409         " -reservecyl #       Report # number of cylinders less than actual in BIOS\n"
04410         " -ide 1m|1s|2m|2s    Specifies the controller to mount drive\n"
04411         " -size ss,s,h,c      Specify the geometry: Sector size,Sectors,Heads,Cylinders\n"
04412         " -size driveSize     Specify the drive size in KB\n"
04413         " -el-torito cdDrive  Specify the CD drive to load the bootable floppy from\n"
04414         " -u                  Unmount the drive"
04415     );
04416     MSG_Add("PROGRAM_IMGMAKE_SYNTAX",
04417         "Creates floppy or harddisk images.\n"
04418         "Syntax: IMGMAKE file [-t type] [[-size size] | [-chs geometry]] [-nofs]\n"
04419         "  [-source source] [-r retries] [-bat]\n"
04420         "  file: The image file that is to be created - !path on the host!\n"
04421         "  -type: Type of image.\n"
04422         "    Floppy templates (names resolve to floppy sizes in kilobytes):\n"
04423         "     fd_160 fd_180 fd_200 fd_320 fd_360 fd_400 fd_720 fd_1200 fd_1440 fd_2880\n"
04424         "    Harddisk templates:\n"
04425         "     hd_250: 250MB image, hd_520: 520MB image, hd_2gig: 2GB image\n"
04426         "     hd_4gig:  4GB image, hd_8gig: 8GB image (maximum size)\n"
04427         "     hd_st251: 40MB image, hd_st225: 20MB image (geometry from old drives)\n"
04428         "    Custom harddisk images:\n"
04429         "     hd (requires -size or -chs)\n"
04430         "  -size: size of a custom harddisk image in MB.\n"
04431         "  -geometry: disk geometry in cylinders(1-1023),heads(1-255),sectors(1-63).\n"
04432         "  -nofs: add this parameter if a blank image should be created.\n"
04433         "  -bat: creates a .bat file with the IMGMOUNT command required for this image.\n"
04434 #ifdef WIN32
04435         "  -source: drive letter - if specified the image is read from a floppy disk.\n"
04436         "  -retries: how often to retry when attempting to read a bad floppy disk(1-99).\n"
04437 #endif
04438         " Examples:\n"
04439         "    imgmake c:\\image.img -t fd_1440          - create a 1.44MB floppy image\n"
04440         "    imgmake c:\\image.img -t hd -size 100     - create a 100MB hdd image\n"
04441         "    imgmake c:\\image.img -t hd -chs 130,2,17 - create a special hd image"
04442 #ifdef WIN32
04443         "\n    imgmake c:\\image.img -source a           - read image from physical drive A"
04444 #endif
04445         );
04446 #ifdef WIN32
04447     MSG_Add("PROGRAM_IMGMAKE_FLREAD",
04448         "Disk geometry: %d Cylinders, %d Heads, %d Sectors, %d Kilobytes\n\n");
04449     MSG_Add("PROGRAM_IMGMAKE_FLREAD2",
04450         "\xdb =good, \xb1 =good after retries, ! =CRC error, x =sector not found, ? =unknown\n\n");
04451 #endif
04452     MSG_Add("PROGRAM_IMGMAKE_FILE_EXISTS","The file \"%s\" already exists.\n");
04453     MSG_Add("PROGRAM_IMGMAKE_CANNOT_WRITE","The file \"%s\" cannot be opened for writing.\n");
04454     MSG_Add("PROGRAM_IMGMAKE_NOT_ENOUGH_SPACE","Not enough space availible for the image file. Need %u bytes.\n");
04455     MSG_Add("PROGRAM_IMGMAKE_PRINT_CHS","Creating an image file with %u cylinders, %u heads and %u sectors\n");
04456     MSG_Add("PROGRAM_IMGMAKE_CANT_READ_FLOPPY","\n\nUnable to read floppy.");
04457 
04458     MSG_Add("PROGRAM_KEYB_INFO","Codepage %i has been loaded\n");
04459     MSG_Add("PROGRAM_KEYB_INFO_LAYOUT","Codepage %i has been loaded for layout %s\n");
04460     MSG_Add("PROGRAM_KEYB_SHOWHELP",
04461         "\033[32;1mKEYB\033[0m [keyboard layout ID[ codepage number[ codepage file]]]\n\n"
04462         "Some examples:\n"
04463         "  \033[32;1mKEYB\033[0m: Display currently loaded codepage.\n"
04464         "  \033[32;1mKEYB\033[0m sp: Load the spanish (SP) layout, use an appropriate codepage.\n"
04465         "  \033[32;1mKEYB\033[0m sp 850: Load the spanish (SP) layout, use codepage 850.\n"
04466         "  \033[32;1mKEYB\033[0m sp 850 mycp.cpi: Same as above, but use file mycp.cpi.\n");
04467     MSG_Add("PROGRAM_KEYB_NOERROR","Keyboard layout %s loaded for codepage %i\n");
04468     MSG_Add("PROGRAM_KEYB_FILENOTFOUND","Keyboard file %s not found\n\n");
04469     MSG_Add("PROGRAM_KEYB_INVALIDFILE","Keyboard file %s invalid\n");
04470     MSG_Add("PROGRAM_KEYB_LAYOUTNOTFOUND","No layout in %s for codepage %i\n");
04471     MSG_Add("PROGRAM_KEYB_INVCPFILE","None or invalid codepage file for layout %s\n\n");
04472     MSG_Add("PROGRAM_MODE_USAGE",
04473             "\033[34;1mMODE\033[0m display-type       :display-type codes are "
04474             "\033[1mCO80\033[0m, \033[1mBW80\033[0m, \033[1mCO40\033[0m, \033[1mBW40\033[0m, or \033[1mMONO\033[0m\n"
04475             "\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");
04476     MSG_Add("PROGRAM_MODE_INVALID_PARAMETERS","Invalid parameter(s).\n");
04477     //MSG_Add("PROGRAM_MORE_USAGE","Usage: \033[34;1mMORE <\033[0m text-file\n");
04478     //MSG_Add("PROGRAM_MORE_MORE","-- More --");
04479 
04480     /*regular setup*/
04481     PROGRAMS_MakeFile("MOUNT.COM",MOUNT_ProgramStart);
04482     PROGRAMS_MakeFile("LOADFIX.COM",LOADFIX_ProgramStart);
04483     PROGRAMS_MakeFile("RESCAN.COM",RESCAN_ProgramStart);
04484     PROGRAMS_MakeFile("INTRO.COM",INTRO_ProgramStart);
04485     PROGRAMS_MakeFile("BOOT.COM",BOOT_ProgramStart);
04486 
04487     if (!IS_PC98_ARCH)
04488         PROGRAMS_MakeFile("LDGFXROM.COM", LDGFXROM_ProgramStart);
04489 
04490     PROGRAMS_MakeFile("IMGMAKE.COM", IMGMAKE_ProgramStart);
04491     PROGRAMS_MakeFile("IMGMOUNT.COM", IMGMOUNT_ProgramStart);
04492 
04493     if (!IS_PC98_ARCH)
04494         PROGRAMS_MakeFile("MODE.COM", MODE_ProgramStart);
04495 
04496     //PROGRAMS_MakeFile("MORE.COM", MORE_ProgramStart);
04497 
04498     if (!IS_PC98_ARCH)
04499         PROGRAMS_MakeFile("KEYB.COM", KEYB_ProgramStart);
04500 
04501     if (!IS_PC98_ARCH)
04502         PROGRAMS_MakeFile("MOUSE.COM", MOUSE_ProgramStart);
04503 
04504     PROGRAMS_MakeFile("A20GATE.COM",A20GATE_ProgramStart);
04505 #if !defined(C_SDL2)
04506     PROGRAMS_MakeFile("SHOWGUI.COM",SHOWGUI_ProgramStart);
04507 #endif
04508     PROGRAMS_MakeFile("NMITEST.COM",NMITEST_ProgramStart);
04509     PROGRAMS_MakeFile("RE-DOS.COM",REDOS_ProgramStart);
04510 
04511     if (IS_VGA_ARCH && svgaCard != SVGA_None)
04512         PROGRAMS_MakeFile("VESAMOED.COM",VESAMOED_ProgramStart);
04513 
04514     if (IS_PC98_ARCH)
04515         PROGRAMS_MakeFile("PC98UTIL.COM",PC98UTIL_ProgramStart);
04516 
04517         PROGRAMS_MakeFile("CAPMOUSE.COM", CAPMOUSE_ProgramStart);
04518 }