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