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