diff --git a/CHANGELOG b/CHANGELOG index 79f76e9f7..43eeda134 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,16 @@ 0.84.2 - - + - Added support for auto-converting non-image drives + (such as mounted local drives) to disk images, so + that they will be visible when booting into guest + systems. A config option "convertdrivefat" (in + [dosbox] section) is added to toggle this setting, + or you can toggle via BOOT command's -convertfat + and -noconvertfat options. There is also a config + option "drive z convert image" (in [dos] section) + to control if to convert virtual drive Z:. Much of + the code is imported from DOSBox Pure. (Wengier) + - Fixed display of total file size in DIR command + when the file sizes are very large. (Wengier) 0.84.1 - Added dosbox(-x).conf option to enable workaround for errant DOS programs that set the TF (trap flag) diff --git a/contrib/linux/com.dosbox_x.DOSBox-X.metainfo.xml.in b/contrib/linux/com.dosbox_x.DOSBox-X.metainfo.xml.in index c04360b10..fa876b388 100644 --- a/contrib/linux/com.dosbox_x.DOSBox-X.metainfo.xml.in +++ b/contrib/linux/com.dosbox_x.DOSBox-X.metainfo.xml.in @@ -10,7 +10,7 @@ Emulation - + diff --git a/contrib/windows/installer/dosbox-x.reference.setup.conf b/contrib/windows/installer/dosbox-x.reference.setup.conf index 85df8a3eb..46d96d149 100644 --- a/contrib/windows/installer/dosbox-x.reference.setup.conf +++ b/contrib/windows/installer/dosbox-x.reference.setup.conf @@ -396,6 +396,7 @@ debuggerrun = debugger # If set to "relative", the value of MOUNT -freesize will change relative to the specified value. # If set to "fixed" (="false"), the value of MOUNT -freesize will be a fixed one to be reported all the time. # Possible values: true, false, fixed, relative, cap, 2, 1, 0. +# convertdrivefat: If set, DOSBox-X will auto-convert mounted non-FAT drives (such as local drives) to FAT format for use with guest systems. #DOSBOX-X-ADV:# leading colon write protect image: If set, BOOT and IMGMOUNT commands will put an image file name with a leading colon (:) in write-protect mode. #DOSBOX-X-ADV:# locking disk image mount: If set, BOOT and IMGMOUNT commands will try to lock the mounted disk image files. As a result, you cannot #DOSBOX-X-ADV:# mount the same disk image files in read/write mode at the same time as this can cause possible disk corruptions. @@ -495,6 +496,7 @@ memsize = 16 #DOSBOX-X-ADV:memalias = 0 nocachedir = false freesizecap = cap +convertdrivefat = true #DOSBOX-X-ADV:leading colon write protect image = true #DOSBOX-X-ADV:locking disk image mount = true #DOSBOX-X-ADV:unmask keyboard on int 16 read = true @@ -2069,6 +2071,7 @@ timeout = 0 #DOSBOX-X-ADV:# Set this option to true to prevent SCANDISK.EXE from attempting scan and repair drive Z: #DOSBOX-X-ADV:# which is impossible since Z: is a virtual drive not backed by a disk filesystem. #DOSBOX-X-ADV:# Possible values: true, false, 1, 0, auto. +#DOSBOX-X-ADV:# drive z convert fat: If set, DOSBox-X will automatically convert the Z drive into disk image as well when "convertdrivefat" is set. #DOSBOX-X-ADV:# drive z expand path: If set, DOSBox-X will automatically expand the %PATH% environment variable to include the subdirectories on the Z drive. #DOSBOX-X-ADV:# drive z hide files: The files or directories listed here (separated by space) will be either hidden or removed from the Z drive. #DOSBOX-X-ADV:# Files with leading forward slashes (e.g. "/DEBUG\BIOSTEST.COM") will become hidden files (DIR /A will list them). @@ -2251,7 +2254,7 @@ timeout = 0 # dos clipboard api: If set, DOS APIs for communications with the Windows clipboard will be enabled for shared clipboard communications. #DOSBOX-X-ADV-SEE:# #DOSBOX-X-ADV-SEE:# Advanced options (see full configuration reference file [dosbox-x.reference.full.conf] for more details): -#DOSBOX-X-ADV-SEE:# -> badcommandhandler; hma allow reservation; special operation file prefix; drive z is remote; drive z expand path; drive z hide files; hidenonrepresentable; hma minimum allocation; dos sda size; hma free space; cpm compatibility mode; minimum dos initial private segment; minimum mcb segment; enable dummy device mcb; maximum environment block size on exec; additional environment block size on exec; enable a20 on windows init; zero memory on xms memory allocation; vcpi; unmask timer on disk io; zero int 67h if no ems; zero unused int 68h; emm386 startup active; zero memory on ems memory allocation; ems system handle memory size; ems system handle on even megabyte; umb start; umb end; kernel allocation in umb; keep umb on boot; keep private area on boot; private area in umb; autoa20fix; autoloadfix; startincon; int33 hide host cursor if interrupt subroutine; int33 hide host cursor when polling; int33 disable cell granularity; int 13 disk change detect; int 13 extensions; biosps2; int15 wait force unmask irq; int15 mouse callback does not preserve registers; filenamechar; collating and uppercase; con device use int 16h to detect keyboard input; zero memory on int 21h memory allocation +#DOSBOX-X-ADV-SEE:# -> badcommandhandler; hma allow reservation; special operation file prefix; drive z is remote; drive z convert fat; drive z expand path; drive z hide files; hidenonrepresentable; hma minimum allocation; dos sda size; hma free space; cpm compatibility mode; minimum dos initial private segment; minimum mcb segment; enable dummy device mcb; maximum environment block size on exec; additional environment block size on exec; enable a20 on windows init; zero memory on xms memory allocation; vcpi; unmask timer on disk io; zero int 67h if no ems; zero unused int 68h; emm386 startup active; zero memory on ems memory allocation; ems system handle memory size; ems system handle on even megabyte; umb start; umb end; kernel allocation in umb; keep umb on boot; keep private area on boot; private area in umb; autoa20fix; autoloadfix; startincon; int33 hide host cursor if interrupt subroutine; int33 hide host cursor when polling; int33 disable cell granularity; int 13 disk change detect; int 13 extensions; biosps2; int15 wait force unmask irq; int15 mouse callback does not preserve registers; filenamechar; collating and uppercase; con device use int 16h to detect keyboard input; zero memory on int 21h memory allocation #DOSBOX-X-ADV-SEE:# xms = true xms handles = 0 @@ -2263,6 +2266,7 @@ hard drive data rate limit = -1 floppy drive data rate limit = -1 #DOSBOX-X-ADV:special operation file prefix = .DB #DOSBOX-X-ADV:drive z is remote = auto +#DOSBOX-X-ADV:drive z convert fat = false #DOSBOX-X-ADV:drive z expand path = true #DOSBOX-X-ADV:drive z hide files = /TEXTUTIL\25.COM /TEXTUTIL\28.COM /TEXTUTIL\50.COM #DOSBOX-X-ADV:hidenonrepresentable = true diff --git a/dosbox-x.reference.conf b/dosbox-x.reference.conf index 3eb88b19b..62af69537 100644 --- a/dosbox-x.reference.conf +++ b/dosbox-x.reference.conf @@ -192,6 +192,7 @@ debuggerrun = debugger # If set to "relative", the value of MOUNT -freesize will change relative to the specified value. # If set to "fixed" (="false"), the value of MOUNT -freesize will be a fixed one to be reported all the time. # Possible values: true, false, fixed, relative, cap, 2, 1, 0. +# convertdrivefat: If set, DOSBox-X will auto-convert mounted non-FAT drives (such as local drives) to FAT format for use with guest systems. # # Advanced options (see full configuration reference file [dosbox-x.reference.full.conf] for more details): # -> disable graphical splash; allow quit after warning; keyboard hook; weitek; bochs debug port e9; compresssaveparts; show recorded filename; skip encoding unchanged frames; capture chroma format; capture format; shell environment size; private area size; turn off a20 gate on boot; cbus bus clock; isa bus clock; pci bus clock; call binary on reset; unhandled irq handler; call binary on boot; ibm rom basic; rom bios allocation max; rom bios minimum size; irq delay ns; iodelay; iodelay16; iodelay32; acpi; acpi rsd ptr location; acpi sci irq; acpi iobase; acpi reserved size; memsizekb; dos mem limit; isa memory hole at 512kb; reboot delay; memalias; leading colon write protect image; locking disk image mount; unmask keyboard on int 16 read; int16 keyboard polling undocumented cf behavior; allow port 92 reset; enable port 92; enable 1st dma controller; enable 2nd dma controller; allow dma address decrement; enable 128k capable 16-bit dma; enable dma extra page registers; dma page registers write-only; cascade interrupt never in service; cascade interrupt ignore in service; enable slave pic; enable pc nmi mask; allow more than 640kb base memory; enable pci bus @@ -222,6 +223,7 @@ a20 = mask memsize = 16 nocachedir = false freesizecap = cap +convertdrivefat = true [render] # frameskip: How many frames DOSBox-X skips before drawing one. @@ -1086,7 +1088,7 @@ timeout = 0 # dos clipboard api: If set, DOS APIs for communications with the Windows clipboard will be enabled for shared clipboard communications. # # Advanced options (see full configuration reference file [dosbox-x.reference.full.conf] for more details): -# -> badcommandhandler; hma allow reservation; special operation file prefix; drive z is remote; drive z expand path; drive z hide files; hidenonrepresentable; hma minimum allocation; dos sda size; hma free space; cpm compatibility mode; minimum dos initial private segment; minimum mcb segment; enable dummy device mcb; maximum environment block size on exec; additional environment block size on exec; enable a20 on windows init; zero memory on xms memory allocation; vcpi; unmask timer on disk io; zero int 67h if no ems; zero unused int 68h; emm386 startup active; zero memory on ems memory allocation; ems system handle memory size; ems system handle on even megabyte; umb start; umb end; kernel allocation in umb; keep umb on boot; keep private area on boot; private area in umb; autoa20fix; autoloadfix; startincon; int33 hide host cursor if interrupt subroutine; int33 hide host cursor when polling; int33 disable cell granularity; int 13 disk change detect; int 13 extensions; biosps2; int15 wait force unmask irq; int15 mouse callback does not preserve registers; filenamechar; collating and uppercase; con device use int 16h to detect keyboard input; zero memory on int 21h memory allocation +# -> badcommandhandler; hma allow reservation; special operation file prefix; drive z is remote; drive z convert fat; drive z expand path; drive z hide files; hidenonrepresentable; hma minimum allocation; dos sda size; hma free space; cpm compatibility mode; minimum dos initial private segment; minimum mcb segment; enable dummy device mcb; maximum environment block size on exec; additional environment block size on exec; enable a20 on windows init; zero memory on xms memory allocation; vcpi; unmask timer on disk io; zero int 67h if no ems; zero unused int 68h; emm386 startup active; zero memory on ems memory allocation; ems system handle memory size; ems system handle on even megabyte; umb start; umb end; kernel allocation in umb; keep umb on boot; keep private area on boot; private area in umb; autoa20fix; autoloadfix; startincon; int33 hide host cursor if interrupt subroutine; int33 hide host cursor when polling; int33 disable cell granularity; int 13 disk change detect; int 13 extensions; biosps2; int15 wait force unmask irq; int15 mouse callback does not preserve registers; filenamechar; collating and uppercase; con device use int 16h to detect keyboard input; zero memory on int 21h memory allocation # xms = true xms handles = 0 diff --git a/dosbox-x.reference.full.conf b/dosbox-x.reference.full.conf index f55f7a27b..6c99844fe 100644 --- a/dosbox-x.reference.full.conf +++ b/dosbox-x.reference.full.conf @@ -388,6 +388,7 @@ debuggerrun = debugger # If set to "relative", the value of MOUNT -freesize will change relative to the specified value. # If set to "fixed" (="false"), the value of MOUNT -freesize will be a fixed one to be reported all the time. # Possible values: true, false, fixed, relative, cap, 2, 1, 0. +# convertdrivefat: If set, DOSBox-X will auto-convert mounted non-FAT drives (such as local drives) to FAT format for use with guest systems. # leading colon write protect image: If set, BOOT and IMGMOUNT commands will put an image file name with a leading colon (:) in write-protect mode. # locking disk image mount: If set, BOOT and IMGMOUNT commands will try to lock the mounted disk image files. As a result, you cannot # mount the same disk image files in read/write mode at the same time as this can cause possible disk corruptions. @@ -483,6 +484,7 @@ reboot delay = -1 memalias = 0 nocachedir = false freesizecap = cap +convertdrivefat = true leading colon write protect image = true locking disk image mount = true unmask keyboard on int 16 read = true @@ -2013,6 +2015,7 @@ timeout = 0 # Set this option to true to prevent SCANDISK.EXE from attempting scan and repair drive Z: # which is impossible since Z: is a virtual drive not backed by a disk filesystem. # Possible values: true, false, 1, 0, auto. +# drive z convert fat: If set, DOSBox-X will automatically convert the Z drive into disk image as well when "convertdrivefat" is set. # drive z expand path: If set, DOSBox-X will automatically expand the %PATH% environment variable to include the subdirectories on the Z drive. # drive z hide files: The files or directories listed here (separated by space) will be either hidden or removed from the Z drive. # Files with leading forward slashes (e.g. "/DEBUG\BIOSTEST.COM") will become hidden files (DIR /A will list them). @@ -2203,6 +2206,7 @@ hard drive data rate limit = -1 floppy drive data rate limit = -1 special operation file prefix = .DB drive z is remote = auto +drive z convert fat = false drive z expand path = true drive z hide files = /TEXTUTIL\25.COM /TEXTUTIL\28.COM /TEXTUTIL\50.COM hidenonrepresentable = true diff --git a/include/bios_disk.h b/include/bios_disk.h index 778fe713f..b4926dd56 100644 --- a/include/bios_disk.h +++ b/include/bios_disk.h @@ -65,9 +65,12 @@ class imageDisk { virtual void Get_Geometry(uint32_t * getHeads, uint32_t *getCyl, uint32_t *getSect, uint32_t *getSectSize); virtual uint8_t GetBiosType(void); virtual uint32_t getSectSize(void); + imageDisk(class DOS_Drive *useDrive); imageDisk(FILE *imgFile, const char *imgName, uint32_t imgSizeK, bool isHardDisk); imageDisk(FILE* diskimg, const char* diskName, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t sector_size, bool hardDrive); - virtual ~imageDisk() { if(diskimg != NULL) { fclose(diskimg); diskimg=NULL; } }; + virtual ~imageDisk(); + void Set_GeometryForHardDisk(); + struct fatFromDOSDrive* ffdd = NULL; IMAGE_TYPE class_id = ID_BASE; std::string diskname; @@ -91,6 +94,7 @@ class imageDisk { private: volatile int refcount = 0; std::vector partition_in_use; /* used by FAT driver to prevent mounting a partition twice */ + uint64_t current_fpos; public: int Addref() { diff --git a/include/build_timestamp.h b/include/build_timestamp.h index 1bf05e7d9..d981ae5b4 100644 --- a/include/build_timestamp.h +++ b/include/build_timestamp.h @@ -1,4 +1,4 @@ /*auto-generated*/ -#define UPDATED_STR "Jul 1, 2022 2:43:23pm" -#define GIT_COMMIT_HASH "644ab8a" +#define UPDATED_STR "Jul 3, 2022 11:03:27pm" +#define GIT_COMMIT_HASH "e73255c" #define COPYRIGHT_END_YEAR "2022" diff --git a/include/dos_system.h b/include/dos_system.h index b511201a3..c65933e3f 100644 --- a/include/dos_system.h +++ b/include/dos_system.h @@ -332,7 +332,6 @@ public: virtual void closedir(void *handle) { (void)handle; }; virtual bool read_directory_first(void *handle, char* entry_name, char* entry_sname, bool& is_directory) { (void)handle; (void)entry_name; (void)entry_sname; (void)is_directory; return false; }; virtual bool read_directory_next(void *handle, char* entry_name, char* entry_sname, bool& is_directory) { (void)handle; (void)entry_name; (void)entry_sname; (void)is_directory; return false; }; - virtual const char * GetInfo(void); char * GetBaseDir(void); diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index 20447657b..0f9e03b87 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -99,6 +99,7 @@ bool Mouse_Vertical = false; bool force_nocachedir = false; bool lockmount = true; bool wpcolon = true; +bool convertimg = true; bool startcmd = false; bool startwait = true; bool startquiet = false; @@ -107,7 +108,7 @@ bool mountwarning = true; bool qmount = false; bool nowarn = false; bool CodePageHostToGuestUTF8(char *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/), CodePageHostToGuestUTF16(char *d/*CROSS_LEN*/,const uint16_t *s/*CROSS_LEN*/); -extern bool addovl, addipx, addne2k, prepared, inshell, usecon, uao, morelen, mountfro[26], mountiro[26], resetcolor, staycolors; +extern bool addovl, addipx, addne2k, prepared, inshell, usecon, uao, morelen, mountfro[26], mountiro[26], resetcolor, staycolors, internal_program; extern bool clear_screen(), OpenGL_using(void), DOS_SetAnsiAttr(uint8_t attr); extern int lastcp, FileDirExistCP(const char *name), FileDirExistUTF8(std::string &localname, const char *name); extern uint8_t DOS_GetAnsiAttr(void); @@ -1846,6 +1847,7 @@ public: bool bios_boot = false; bool swaponedrive = false; bool force = false; + int convimg = -1; int quiet = 0; //Hack To allow long commandlines @@ -1874,6 +1876,12 @@ public: if (cmd->FindExist("-force",true)) force = true; + if (cmd->FindExist("-convertfat",true)) + convimg = 1; + + if (cmd->FindExist("-noconvertfat",true)) + convimg = 0; + if (cmd->FindString("-bios",bios,true)) bios_boot = true; @@ -2490,10 +2498,44 @@ public: return; } + char msg[512] = {0}; + const uint8_t page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + if ((convimg == 1 || (convertimg && convimg == -1 && !IS_PC98_ARCH))) { // PC-98 image not supported yet + unsigned int drv = 2, nextdrv = 2; + for (unsigned int d=2;dGetInfo(), "fatDrive ", 9) && strncmp(Drives[drv]->GetInfo(), "isoDrive ", 9)) { + if (drv==ZDRIVE_NUM && !static_cast(control->GetSection("dos"))->Get_bool("drive z convert fat")) continue; + while (imageDiskList[nextdrv]) nextdrv++; + if (nextdrv>=MAX_DISK_IMAGES) break; + if (quiet<2) { + size_t len = strlen(msg); + if (!len) strcat(msg, CURSOR_POS_COL(page)>0?"\r\n":""); + strcat(msg, "Converting drive "); + strcat(msg, std::string(1, 'A'+drv).c_str()); + strcat(msg, ": to FAT...\r\n"); + LOG_MSG("%s", msg+len); + if (!quiet) { + uint16_t s = (uint16_t)strlen(msg); + DOS_WriteFile(STDERR,(uint8_t*)msg,&s); + *msg = 0; + } + } + imageDiskList[nextdrv] = new imageDisk(Drives[drv]); + if (imageDiskList[nextdrv]) { + bool ide_slave = false; + signed char ide_index = -1; + IDE_Auto(ide_index,ide_slave); + IDE_Hard_Disk_Attach((signed char)ide_index, ide_slave, nextdrv); + } + } + } + } + if (quiet<2) { - char msg[30]; - const uint8_t page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - strcpy(msg, CURSOR_POS_COL(page)>0?"\r\n":""); + if (!strlen(msg)) strcat(msg, CURSOR_POS_COL(page)>0?"\r\n":""); strcat(msg, "Booting from drive "); strcat(msg, std::string(1, drive).c_str()); strcat(msg, "...\r\n"); @@ -8283,8 +8325,10 @@ void Add_VFiles(bool usecp) { PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart,"/SYSTEM/"); PROGRAMS_MakeFile("COUNTRY.COM",COUNTRY_ProgramStart,"/SYSTEM/"); PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart); + internal_program = true; if (usecp) VFILE_Register("AUTOEXEC.BAT",(uint8_t *)autoexec_data,(uint32_t)strlen(autoexec_data)); if (prepared) VFILE_Register("CONFIG.SYS",(uint8_t *)config_data,(uint32_t)strlen(config_data)); + internal_program = false; PROGRAMS_MakeFile("RE-DOS.COM",REDOS_ProgramStart,"/SYSTEM/"); PROGRAMS_MakeFile("RESCAN.COM",RESCAN_ProgramStart,"/SYSTEM/"); #if defined(WIN32) && !defined(HX_DOS) || defined(LINUX) || defined(MACOSX) diff --git a/src/dos/drive_iso.cpp b/src/dos/drive_iso.cpp index 5c5937d1d..6ae12bec2 100644 --- a/src/dos/drive_iso.cpp +++ b/src/dos/drive_iso.cpp @@ -633,4 +633,3 @@ void isoDrive::GetLongName(const char* ident, char* lfindName) { strcpy(lfindName,ident); } - diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp index b4fb053e0..89d0e2b9e 100644 --- a/src/dos/drive_local.cpp +++ b/src/dos/drive_local.cpp @@ -1402,6 +1402,7 @@ bool localDrive::FileOpen(DOS_File * * file,const char * name,uint32_t flags) { break; } } + if (!dos_kernel_disabled) for (i=0;iIsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { lfp=dynamic_cast(Files[i]); @@ -2283,7 +2284,6 @@ bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) { return true; } - uint8_t localDrive::GetMediaByte(void) { return allocation.mediaid; } diff --git a/src/dos/drive_overlay.cpp b/src/dos/drive_overlay.cpp index a48bb712f..2cfc63dc2 100644 --- a/src/dos/drive_overlay.cpp +++ b/src/dos/drive_overlay.cpp @@ -593,6 +593,7 @@ bool Overlay_Drive::FileOpen(DOS_File * * file,const char * name,uint32_t flags) break; } } + if (!dos_kernel_disabled) for (i=0;iIsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { lfp=dynamic_cast(Files[i]); diff --git a/src/dos/drive_virtual.cpp b/src/dos/drive_virtual.cpp index c4b00996b..797b9757b 100644 --- a/src/dos/drive_virtual.cpp +++ b/src/dos/drive_virtual.cpp @@ -41,13 +41,14 @@ struct VFILE_Block { unsigned int onpos; bool isdir; bool hidden; + bool intprog; VFILE_Block * next; }; #define MAX_VFILES 500 unsigned int vfpos=1, lfn_id[256]; -char ondirs[MAX_VFILES][CROSS_LEN],sfn[DOS_NAMELENGTH_ASCII]; -char vfnames[MAX_VFILES][CROSS_LEN],vfsnames[MAX_VFILES][DOS_NAMELENGTH_ASCII]; +bool internal_program = false, skipintprog = false; +char sfn[DOS_NAMELENGTH_ASCII],vfnames[MAX_VFILES][CROSS_LEN],vfsnames[MAX_VFILES][DOS_NAMELENGTH_ASCII]; static VFILE_Block * first_file, * lfn_search[256], * parent_dir = NULL; extern int lfn_filefind_handle; @@ -249,7 +250,8 @@ void VFILE_Register(const char * name,uint8_t * data,uint32_t size,const char *d VFILE_Block * new_file=new VFILE_Block; new_file->name=vfsnames[vfpos]; new_file->lname=vfnames[vfpos]; - vfpos++; + vfpos++; + new_file->intprog = internal_program; new_file->data=data; new_file->size=size; new_file->date=fztime||fzdate?fzdate:DOS_PackDate(2002,10,1); @@ -550,7 +552,7 @@ bool Virtual_Drive::FindNext(DOS_DTA & dta) { if (lfn_filefind_handle>=LFN_FILEFIND_MAX) while (search_file) { - if (pos==search_file->onpos&&((attr & DOS_ATTR_DIRECTORY)||!search_file->isdir)&&(WildFileCmp(search_file->name,pattern)||LWildFileCmp(search_file->lname,pattern))) { + if (!(skipintprog && search_file->intprog) && pos==search_file->onpos&&((attr & DOS_ATTR_DIRECTORY)||!search_file->isdir)&&(WildFileCmp(search_file->name,pattern)||LWildFileCmp(search_file->lname,pattern))) { dta.SetResult(search_file->name,search_file->lname,search_file->size,search_file->date,search_file->time,search_file->isdir?(search_file->hidden?DOS_ATTR_DIRECTORY|DOS_ATTR_HIDDEN:DOS_ATTR_DIRECTORY):(search_file->hidden?DOS_ATTR_ARCHIVE|DOS_ATTR_HIDDEN:DOS_ATTR_ARCHIVE)); search_file=search_file->next; return true; @@ -559,7 +561,7 @@ bool Virtual_Drive::FindNext(DOS_DTA & dta) { } else while (lfn_search[lfn_filefind_handle]) { - if (pos==lfn_search[lfn_filefind_handle]->onpos&&((attr & DOS_ATTR_DIRECTORY)||!lfn_search[lfn_filefind_handle]->isdir)&&(WildFileCmp(lfn_search[lfn_filefind_handle]->name,pattern)||LWildFileCmp(lfn_search[lfn_filefind_handle]->lname,pattern))) { + if (!(skipintprog && search_file->intprog) && pos==lfn_search[lfn_filefind_handle]->onpos&&((attr & DOS_ATTR_DIRECTORY)||!lfn_search[lfn_filefind_handle]->isdir)&&(WildFileCmp(lfn_search[lfn_filefind_handle]->name,pattern)||LWildFileCmp(lfn_search[lfn_filefind_handle]->lname,pattern))) { dta.SetResult(lfn_search[lfn_filefind_handle]->name,lfn_search[lfn_filefind_handle]->lname,lfn_search[lfn_filefind_handle]->size,lfn_search[lfn_filefind_handle]->date,lfn_search[lfn_filefind_handle]->time,lfn_search[lfn_filefind_handle]->isdir?(lfn_search[lfn_filefind_handle]->hidden?DOS_ATTR_DIRECTORY|DOS_ATTR_HIDDEN:DOS_ATTR_DIRECTORY):(lfn_search[lfn_filefind_handle]->hidden?DOS_ATTR_ARCHIVE|DOS_ATTR_HIDDEN:DOS_ATTR_ARCHIVE)); lfn_search[lfn_filefind_handle]=lfn_search[lfn_filefind_handle]->next; return true; diff --git a/src/dos/drives.h b/src/dos/drives.h index e5cd4857f..4e94e639b 100644 --- a/src/dos/drives.h +++ b/src/dos/drives.h @@ -99,7 +99,6 @@ public: virtual void remove_special_file_from_disk(const char* dosname, const char* operation); virtual std::string create_filename_of_special_operation(const char* dosname, const char* operation, bool expand); virtual bool add_special_file_to_disk(const char* dosname, const char* operation, uint16_t value, bool isdir); - virtual void EmptyCache(void) { dirCache.EmptyCache(); }; virtual void MediaChange() {}; const char* getBasedir() {return basedir;}; @@ -681,7 +680,7 @@ private: bool GetNextDirEntry(const int dirIteratorHandle, isoDirEntry* de); void FreeDirIterator(const int dirIterator); bool ReadCachedSector(uint8_t** buffer, const uint32_t sector); - void GetLongName(const char* ident, char* lfindName); + void GetLongName(const char* ident, char* lfindName); struct DirIterator { bool valid; diff --git a/src/dosbox.cpp b/src/dosbox.cpp index ff6d0c7f3..d272a760b 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -124,6 +124,7 @@ static void CheckX86ExtensionsSupport() extern void GFX_SetTitle(int32_t cycles, int frameskip, Bits timing, bool paused); extern void AddSaveStateMapper(), AddMessages(), JFONT_Init(), J3_SetType(std::string type, std::string back, std::string text); extern bool force_nocachedir; +extern bool convertimg; extern bool wpcolon; extern bool lockmount; extern bool clearline; @@ -793,9 +794,8 @@ void DOSBOX_InitTickLoop() { void Init_VGABIOS() { long rom_sz = 0; FILE *rom_fp = NULL; - Section_prop *section = static_cast(control->GetSection("dosbox")); Section_prop *video_section = static_cast(control->GetSection("video")); - assert(section != NULL && video_section != NULL); + assert(video_section != NULL); if (IS_PC98_ARCH) { // There IS no VGA BIOS, this is PC-98 mode! @@ -812,14 +812,6 @@ void Init_VGABIOS() { // We can remove this once the device callout system is in place. assert(MemBase != NULL); - force_nocachedir = section->Get_bool("nocachedir"); - std::string freesizestr = section->Get_string("freesizecap"); - if (freesizestr == "fixed" || freesizestr == "false" || freesizestr == "0") freesizecap = 0; - else if (freesizestr == "relative" || freesizestr == "2") freesizecap = 2; - else freesizecap = 1; - wpcolon = section->Get_bool("leading colon write protect image"); - lockmount = section->Get_bool("locking disk image mount"); - VGA_BIOS_use_rom = video_section->Get_bool("vga bios use rom image"); VGA_BIOS_rom = video_section->Get_string("vga bios rom image"); @@ -1036,6 +1028,15 @@ void DOSBOX_RealInit() { // TODO: should be parsed by motherboard emulation allow_port_92_reset = section->Get_bool("allow port 92 reset"); + force_nocachedir = section->Get_bool("nocachedir"); + std::string freesizestr = section->Get_string("freesizecap"); + if (freesizestr == "fixed" || freesizestr == "false" || freesizestr == "0") freesizecap = 0; + else if (freesizestr == "relative" || freesizestr == "2") freesizecap = 2; + else freesizecap = 1; + convertimg = section->Get_bool("convertdrivefat"); + wpcolon = section->Get_bool("leading colon write protect image"); + lockmount = section->Get_bool("locking disk image mount"); + // CGA/EGA/VGA-specific extern unsigned char vga_p3da_undefined_bits; vga_p3da_undefined_bits = (unsigned char)static_cast(control->GetSection("video"))->Get_hex("vga 3da undefined bits"); @@ -1791,6 +1792,10 @@ void DOSBOX_SetupConfigSections(void) { "If set to \"fixed\" (=\"false\"), the value of MOUNT -freesize will be a fixed one to be reported all the time."); Pstring->SetBasic(true); + Pbool = secprop->Add_bool("convertdrivefat",Property::Changeable::WhenIdle,true); + Pbool->Set_help("If set, DOSBox-X will auto-convert mounted non-FAT drives (such as local drives) to FAT format for use with guest systems."); + Pbool->SetBasic(true); + Pbool = secprop->Add_bool("leading colon write protect image",Property::Changeable::WhenIdle,true); Pbool->Set_help("If set, BOOT and IMGMOUNT commands will put an image file name with a leading colon (:) in write-protect mode."); @@ -4100,6 +4105,9 @@ void DOSBOX_SetupConfigSections(void) { "Set this option to true to prevent SCANDISK.EXE from attempting scan and repair drive Z:\n" "which is impossible since Z: is a virtual drive not backed by a disk filesystem."); + Pbool = secprop->Add_bool("drive z convert fat",Property::Changeable::WhenIdle,false); + Pbool->Set_help("If set, DOSBox-X will automatically convert the Z drive into disk image as well when \"convertdrivefat\" is set."); + Pbool = secprop->Add_bool("drive z expand path",Property::Changeable::WhenIdle,true); Pbool->Set_help("If set, DOSBox-X will automatically expand the %PATH% environment variable to include the subdirectories on the Z drive."); diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 8612ce170..febe4832b 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -73,7 +73,7 @@ bool tonoime = false, enableime = false; bool usesystemcursor = false, rtl = false, selmark = false; bool mountfro[26], mountiro[26]; bool OpenGL_using(void), Direct3D_using(void); -void DOSBox_SetSysMenu(void), GFX_OpenGLRedrawScreen(void), InitFontHandle(void), DOSV_FillScreen(void), SetWindowTransparency(int trans); +void DOSBox_SetSysMenu(void), GFX_OpenGLRedrawScreen(void), InitFontHandle(void), DOSV_FillScreen(void), Add_VFiles(bool usecp), SetWindowTransparency(int trans); void MenuBrowseProgramFile(void), OutputSettingMenuUpdate(void), aspect_ratio_menu(void), update_pc98_clock_pit_menu(void), AllocCallback1(void), AllocCallback2(void), ToggleMenu(bool pressed); int Reflect_Menu(void); @@ -9190,12 +9190,14 @@ fresh_boot: Voodoo_Output_Enable(false); grGlideShutdown(); + /* shutdown DOSBox-X's virtual drive Z */ VFILE_Shutdown(); - /* shutdown the programs */ PROGRAMS_Shutdown(); /* FIXME: Is this safe? Or will this cause use-after-free bug? */ + Add_VFiles(false); + /* remove environment variables for some components */ DOS_UninstallMisc(); SBLASTER_DOS_Shutdown(); diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index 6440034bb..0710b6f4a 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -34,9 +34,850 @@ #endif extern int bootdrive; -extern bool int13_disk_change_detect_enable; +extern unsigned long freec; +extern bool int13_disk_change_detect_enable, skipintprog, rsize; extern bool int13_extensions_enable, bootguest, bootvm, use_quick_reboot; +#define STATIC_ASSERTM(A,B) static_assertion_##A##_##B +#define STATIC_ASSERTN(A,B) STATIC_ASSERTM(A,B) +#define STATIC_ASSERT(cond) typedef char STATIC_ASSERTN(__LINE__,__COUNTER__)[(cond)?1:-1] + +uint32_t DriveCalculateCRC32(const uint8_t *ptr, size_t len, uint32_t crc) +{ + // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ + static const uint32_t s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + uint32_t crcu32 = (uint32_t)~crc; + while (len--) { uint8_t b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; +} + +void DriveFileIterator(DOS_Drive* drv, void(*func)(const char* path, bool is_dir, uint32_t size, uint16_t date, uint16_t time, uint8_t attr, Bitu data), Bitu data) +{ + if (!drv) return; + struct Iter + { + static void ParseDir(DOS_Drive* drv, const std::string& dir, std::vector& dirs, void(*func)(const char* path, bool is_dir, uint32_t size, uint16_t date, uint16_t time, uint8_t attr, Bitu data), Bitu data) + { + size_t dirlen = dir.length(); + if (dirlen + DOS_NAMELENGTH >= DOS_PATHLENGTH) return; + char full_path[DOS_PATHLENGTH+4]; + if (dirlen) + { + memcpy(full_path, &dir[0], dirlen); + full_path[dirlen++] = '\\'; + } + full_path[dirlen] = '\0'; + + RealPt save_dta = dos.dta(); + dos.dta(dos.tables.tempdta); + DOS_DTA dta(dos.dta()); + dta.SetupSearch(255, (uint8_t)(0xffff & ~DOS_ATTR_VOLUME), (char*)"*.*"); + for (bool more = drv->FindFirst((char*)dir.c_str(), dta); more; more = drv->FindNext(dta)) + { + char dta_name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1]; uint32_t dta_size; uint16_t dta_date, dta_time; uint8_t dta_attr; + dta.GetResult(dta_name, lname, dta_size, dta_date, dta_time, dta_attr); + strcpy(full_path + dirlen, dta_name); + bool is_dir = !!(dta_attr & DOS_ATTR_DIRECTORY); + //if (is_dir) printf("[%s] [%s] %s (size: %u - date: %u - time: %u - attr: %u)\n", (const char*)data, (dta_attr == 8 ? "V" : (is_dir ? "D" : "F")), full_path, dta_size, dta_date, dta_time, dta_attr); + if (dta_name[0] == '.' && dta_name[dta_name[1] == '.' ? 2 : 1] == '\0') continue; + if (is_dir) dirs.emplace_back(full_path); + func(full_path, is_dir, dta_size, dta_date, dta_time, dta_attr, data); + } + dos.dta(save_dta); + } + }; + std::vector dirs; + dirs.emplace_back(""); + std::string dir; + while (dirs.size()) + { + std::swap(dirs.back(), dir); + dirs.pop_back(); + Iter::ParseDir(drv, dir.c_str(), dirs, func, data); + } +} + +template struct StringToPointerHashMap +{ + StringToPointerHashMap() : len(0), maxlen(0), keys(NULL), vals(NULL) { } + ~StringToPointerHashMap() { free(keys); free(vals); } + + static uint32_t Hash(const char* str, uint32_t str_limit = 0xFFFF, uint32_t hash_init = (uint32_t)0x811c9dc5) + { + for (const char* e = str + str_limit; *str && str != e;) + hash_init = ((hash_init * (uint32_t)0x01000193) ^ (uint32_t)*(str++)); + return hash_init; + } + + TVal* Get(const char* str, uint32_t str_limit = 0xFFFF, uint32_t hash_init = (uint32_t)0x811c9dc5) const + { + if (len == 0) return NULL; + for (uint32_t key0 = Hash(str, str_limit, hash_init), key = (key0 ? key0 : 1), i = key;; i++) + { + if (keys[i &= maxlen] == key) return vals[i]; + if (!keys[i]) return NULL; + } + } + + void Put(const char* str, TVal* val, uint32_t str_limit = 0xFFFF, uint32_t hash_init = (uint32_t)0x811c9dc5) + { + if (len * 2 >= maxlen) Grow(); + for (uint32_t key0 = Hash(str, str_limit, hash_init), key = (key0 ? key0 : 1), i = key;; i++) + { + if (!keys[i &= maxlen]) { len++; keys[i] = key; vals[i] = val; return; } + if (keys[i] == key) { vals[i] = val; return; } + } + } + + bool Remove(const char* str, uint32_t str_limit = 0xFFFF, uint32_t hash_init = (uint32_t)0x811c9dc5) + { + if (len == 0) return false; + for (uint32_t key0 = Hash(str, str_limit, hash_init), key = (key0 ? key0 : 1), i = key;; i++) + { + if (keys[i &= maxlen] == key) + { + keys[i] = 0; + len--; + while ((key = keys[i = (i + 1) & maxlen]) != 0) + { + for (uint32_t j = key;; j++) + { + if (keys[j &= maxlen] == key) break; + if (!keys[j]) { keys[i] = 0; keys[j] = key; vals[j] = vals[i]; break; } + } + } + return true; + } + if (!keys[i]) return false; + } + } + + void Clear() { memset(keys, len = 0, (maxlen + 1) * sizeof(uint32_t)); } + + uint32_t Len() const { return len; } + uint32_t Capacity() const { return (maxlen ? maxlen + 1 : 0); } + TVal* GetAtIndex(uint32_t idx) const { return (keys[idx] ? vals[idx] : NULL); } + + struct Iterator + { + Iterator(StringToPointerHashMap& _map, uint32_t _index) : map(_map), index(_index - 1) { this->operator++(); } + StringToPointerHashMap& map; + uint32_t index; + TVal* operator *() const { return map.vals[index]; } + bool operator ==(const Iterator &other) const { return index == other.index; } + bool operator !=(const Iterator &other) const { return index != other.index; } + Iterator& operator ++() + { + if (!map.maxlen) { index = 0; return *this; } + if (++index > map.maxlen) index = map.maxlen + 1; + while (index <= map.maxlen && !map.keys[index]) index++; + return *this; + } + }; + + Iterator begin() { return Iterator(*this, 0); } + Iterator end() { return Iterator(*this, (maxlen ? maxlen + 1 : 0)); } + +private: + uint32_t len, maxlen, *keys; + TVal** vals; + + void Grow() + { + uint32_t oldMax = maxlen, oldCap = (maxlen ? oldMax + 1 : 0), *oldKeys = keys; + TVal **oldVals = vals; + maxlen = (maxlen ? maxlen * 2 + 1 : 15); + keys = (uint32_t*)calloc(maxlen + 1, sizeof(uint32_t)); + vals = (TVal**)malloc((maxlen + 1) * sizeof(TVal*)); + for (uint32_t i = 0; i != oldCap; i++) + { + if (!oldKeys[i]) continue; + for (uint32_t key = oldKeys[i], j = key;; j++) + { + if (!keys[j &= maxlen]) { keys[j] = key; vals[j] = oldVals[i]; break; } + } + } + free(oldKeys); + free(oldVals); + } + + // not copyable + StringToPointerHashMap(const StringToPointerHashMap&); + StringToPointerHashMap& operator=(const StringToPointerHashMap&); +}; + +#ifdef _MSC_VER +#pragma pack (1) +#endif +struct bootstrap { + uint8_t nearjmp[3]; + uint8_t oemname[8]; + uint8_t bytespersector[2]; + uint8_t sectorspercluster; + uint16_t reservedsectors; + uint8_t fatcopies; + uint16_t rootdirentries; + uint16_t totalsectorcount; + uint8_t mediadescriptor; + uint16_t sectorsperfat; + uint16_t sectorspertrack; + uint16_t headcount; + uint32_t hiddensectorcount; + uint32_t totalsecdword; + uint8_t bootcode[474]; + uint8_t magic1; /* 0x55 */ + uint8_t magic2; /* 0xaa */ +} GCC_ATTRIBUTE(packed); + +struct lfndirentry { + uint8_t ord; + uint8_t name1[10]; + uint8_t attrib; + uint8_t type; + uint8_t chksum; + uint8_t name2[12]; + uint16_t loFirstClust; + uint8_t name3[4]; + char* Name(int j) { return (char*)(j < 5 ? name1 + j*2 : j < 11 ? name2 + (j-5)*2 : name3 + (j-11)*2); } +} GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack () +#endif +STATIC_ASSERT(sizeof(direntry) == sizeof(lfndirentry)); +enum +{ + DOS_ATTR_LONG_NAME = (DOS_ATTR_READ_ONLY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM | DOS_ATTR_VOLUME), + DOS_ATTR_LONG_NAME_MASK = (DOS_ATTR_READ_ONLY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM | DOS_ATTR_VOLUME | DOS_ATTR_DIRECTORY | DOS_ATTR_ARCHIVE), + DOS_ATTR_PENDING_SHORT_NAME = 0x80, +}; + +struct fatFromDOSDrive +{ + DOS_Drive* drive; + + enum ffddDefs : uint32_t + { + BYTESPERSECTOR = 512, + HEADCOUNT = 240, // needs to be >128 to fit 4GB into CHS + SECTORSPERTRACK = 63, + SECT_MBR = 0, + SECT_BOOT = 32, + CACHECOUNT = 256, + KEEPOPENCOUNT = 8, + NULL_CURSOR = (uint32_t)-1, + }; + + partTable mbr; + bootstrap bootsec; + uint8_t fsinfosec[BYTESPERSECTOR]; + uint32_t sectorsPerCluster; + bool isFAT32, readOnly, tomany = false; + + struct ffddFile { char path[DOS_PATHLENGTH+1]; uint32_t firstSect; }; + std::vector root, dirs; + std::vector files; + std::vector fileAtSector; + std::vector fat; + uint32_t sect_disk_end, sect_files_end, sect_files_start, sect_dirs_start, sect_root_start, sect_fat2_start, sect_fat1_start; + + struct ffddBuf { uint8_t data[BYTESPERSECTOR]; }; + struct ffddSec { uint32_t cursor = NULL_CURSOR; }; + std::vector diffSectorBufs; + std::vector diffSectors; + std::vector diffFreeCursors; + std::string savePath; + FILE* saveFile = NULL; + uint32_t saveEndCursor = 0; + uint8_t cacheSectorData[CACHECOUNT][BYTESPERSECTOR]; + uint32_t cacheSectorNumber[CACHECOUNT]; + DOS_File* openFiles[KEEPOPENCOUNT]; + uint32_t openIndex[KEEPOPENCOUNT]; + uint32_t openCursor = 0; + + ~fatFromDOSDrive() + { + if (saveFile) + fclose(saveFile); + for (DOS_File* df : openFiles) + if (df) { df->Close(); delete df; } + } + + fatFromDOSDrive(DOS_Drive* drv) : drive(drv) + { + cacheSectorNumber[0] = 1; // must not state that sector 0 is already cached + memset(&cacheSectorNumber[1], 0, sizeof(cacheSectorNumber) - sizeof(cacheSectorNumber[0])); + memset(openFiles, 0, sizeof(openFiles)); + + struct Iter + { + static void SetFAT(fatFromDOSDrive& ffdd, size_t idx, uint32_t val) + { + while (idx >= ffdd.fat.size() / (ffdd.isFAT32 ? 4 : 2)) + { + ffdd.fat.resize(ffdd.fat.size() + BYTESPERSECTOR); + memset(&ffdd.fat[ffdd.fat.size() - BYTESPERSECTOR], 0, BYTESPERSECTOR); + } + if (ffdd.isFAT32) + var_write((uint32_t * const)&ffdd.fat[idx * 4], (const uint32_t)val); + else + var_write((uint16_t * const)&ffdd.fat[idx * 2], (const uint16_t)val); + } + + static direntry* AddDirEntry(fatFromDOSDrive& ffdd, bool useFAT16Root, size_t& diridx) + { + const uint32_t entriesPerCluster = ffdd.sectorsPerCluster * BYTESPERSECTOR / sizeof(direntry); + if (!useFAT16Root && (diridx % entriesPerCluster) == 0) + { + // link fat (was set to 0xFFFF before but now we knew the chain continues) + if (diridx) SetFAT(ffdd, 2 + (diridx - 1) / entriesPerCluster, (uint32_t)(2 + ffdd.dirs.size() / entriesPerCluster)); + diridx = ffdd.dirs.size(); + ffdd.dirs.resize(diridx + entriesPerCluster); + memset(&ffdd.dirs[diridx], 0, sizeof(direntry) * entriesPerCluster); + SetFAT(ffdd, 2 + diridx / entriesPerCluster, (uint32_t)0xFFFFFFFF); // set as last cluster in chain for now + } + else if (useFAT16Root && diridx && (diridx % 512) == 0) + { + // this actually should never be larger than 512 for some FAT16 drivers + ffdd.root.resize(diridx + 512); + memset(&ffdd.root[diridx], 0, sizeof(direntry) * 512); + } + return &(!useFAT16Root ? ffdd.dirs : ffdd.root)[diridx++]; + } + + static void ParseDir(fatFromDOSDrive& ffdd, char* dir, const StringToPointerHashMap* filter, int dirlen = 0, uint16_t parentFirstCluster = 0) + { + if (ffdd.tomany) return; + const bool useFAT16Root = (!dirlen && !ffdd.isFAT32), readOnly = ffdd.readOnly; + const size_t firstidx = (!useFAT16Root ? ffdd.dirs.size() : 0); + const uint32_t sectorsPerCluster = ffdd.sectorsPerCluster, bytesPerCluster = sectorsPerCluster * BYTESPERSECTOR, entriesPerCluster = bytesPerCluster / sizeof(direntry); + const uint16_t myFirstCluster = (dirlen ? (uint16_t)(2 + firstidx / entriesPerCluster) : (uint16_t)0) ; + + char finddir[DOS_PATHLENGTH+4]; + memcpy(finddir, dir, dirlen); // because FindFirst can modify this... + finddir[dirlen] = '\0'; + if (dirlen) dir[dirlen++] = '\\'; + + size_t diridx = 0; + RealPt save_dta = dos.dta(); + dos.dta(dos.tables.tempdta); + DOS_DTA dta(dos.dta()); + dta.SetupSearch(255, 0xFF, (char*)"*.*"); + skipintprog = true; + for (bool more = ffdd.drive->FindFirst(finddir, dta); more; more = ffdd.drive->FindNext(dta)) + { + char dta_name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1]; uint32_t dta_size; uint16_t dta_date, dta_time; uint8_t dta_attr; + dta.GetResult(dta_name, lname, dta_size, dta_date, dta_time, dta_attr); + //LOG_MSG("dta_name %s lname %s\n", dta_name, lname); + const char *fend = dta_name + strlen(dta_name); + const bool dot = (dta_name[0] == '.' && dta_name[1] == '\0'), dotdot = (dta_name[0] == '.' && dta_name[1] == '.' && dta_name[2] == '\0'); + if (!dirlen && (dot || dotdot)) continue; // root shouldn't have dot entries (yet localDrive does...) + + ffddFile f; + memcpy(f.path, dir, dirlen); + memcpy(f.path + dirlen, dta_name, fend - dta_name + 1); + if (filter && filter->Get(f.path)) continue; + + const bool isLongFileName = (!dot && !dotdot && !(dta_attr & DOS_ATTR_VOLUME)); + if (isLongFileName) + { + size_t lfnlen = strlen(lname); + const char *lfn_end = lname + lfnlen; + for (size_t i = 0, lfnblocks = (lfnlen + 12) / 13; i != lfnblocks; i++) + { + lfndirentry* le = (lfndirentry*)AddDirEntry(ffdd, useFAT16Root, diridx); + le->ord = (uint8_t)((lfnblocks - i)|(i == 0 ? 0x40 : 0x0)); + le->attrib = DOS_ATTR_LONG_NAME; + le->type = 0; + le->loFirstClust = 0; + const char* plfn = lname + (lfnblocks - i - 1) * 13; + for (int j = 0; j != 13; j++, plfn++) + { + char* p = le->Name(j); + if (plfn > lfn_end) { p[0] = p[1] = (char)0xFF; } + else if (plfn == lfn_end) { p[0] = p[1] = 0; } + else { p[0] = *plfn; p[1] = 0; } + } + } + } + + const char *fext = (dot || dotdot ? NULL : strrchr(dta_name, '.')); + direntry* e = AddDirEntry(ffdd, useFAT16Root, diridx); + memset(e->entryname, ' ', sizeof(e->entryname)); + memcpy(e->entryname, dta_name, (fext ? fext : fend) - dta_name); + if (fext++) memcpy(e->entryname + 8, fext, fend - fext); + + e->attrib = dta_attr | (readOnly ? DOS_ATTR_READ_ONLY : 0) | (isLongFileName ? DOS_ATTR_PENDING_SHORT_NAME : 0); + if (dos.version.major >= 7) { + var_write(&e->crtTime, dta_time); // create date/time is DOS 7.0 and up only + var_write(&e->crtDate, dta_date); // create date/time is DOS 7.0 and up only + } + var_write(&e->accessDate, dta_date); + var_write(&e->modTime, dta_time); + var_write(&e->modDate, dta_date); + + if (dot) + { + e->attrib |= DOS_ATTR_DIRECTORY; // make sure + var_write(&e->loFirstClust, myFirstCluster); + } + else if (dotdot) + { + e->attrib |= DOS_ATTR_DIRECTORY; // make sure + var_write(&e->loFirstClust, parentFirstCluster); + } + else if (dta_attr & DOS_ATTR_VOLUME) + { + if ((dirlen || (e->attrib & DOS_ATTR_DIRECTORY) || dta_size)) { + if (!strcmp(trim((char *)e->entryname), "DOSBOX-X")) continue; + LOG_MSG("Invalid volume entry - %s\n", e->entryname); + } + } + else if (!(dta_attr & DOS_ATTR_DIRECTORY)) + { + var_write(&e->entrysize, dta_size); + + uint32_t fileIdx = (uint32_t)ffdd.files.size(); + ffdd.files.push_back(f); + + uint32_t numSects = (dta_size + bytesPerCluster - 1) / bytesPerCluster * sectorsPerCluster; + try { + ffdd.fileAtSector.resize(ffdd.fileAtSector.size() + numSects, fileIdx); + } catch (...) { + LOG_MSG("Too many sectors needed, will discard remaining files (from %s)", lname); + ffdd.tomany = ffdd.readOnly = true; + var_write((uint32_t *const)&ffdd.fsinfosec[488], (const uint32_t)0x0); + break; + } + } + } + skipintprog = false; + dos.dta(save_dta); + if (dirlen && diridx < firstidx + 2) { + LOG_MSG("Directory need at least . and .. entries - %s\n", finddir); + return; + } + + // Now fill out the subdirectories (can't be done above because only one dos.dta can run simultaneously + std::vector& entries = (!useFAT16Root ? ffdd.dirs : ffdd.root); + for (size_t ei = firstidx; ei != diridx; ei++) + { + direntry& e = entries[ei]; + uint8_t* entryname = e.entryname; + int totlen = dirlen; + if (e.attrib & DOS_ATTR_DIRECTORY) // copy name before modifying SFN + { + if (entryname[0] == '.' && entryname[entryname[1] == '.' ? 2 : 1] == ' ') continue; + for (int i = 0; i != 8 && entryname[i] != ' '; i++) dir[totlen++] = entryname[i]; + if (entryname[8] != ' ') dir[totlen++] = '.'; + for (int i = 8; i != 11 && entryname[i] != ' '; i++) dir[totlen++] = entryname[i]; + } + if (e.attrib & DOS_ATTR_PENDING_SHORT_NAME) // convert LFN to SFN + { + memset(entryname, ' ', sizeof(e.entryname)); + int ni = 0, niext = 0, lossy = 0; + for (lfndirentry* le = (lfndirentry*)&e; le-- == (lfndirentry*)&e || !(le[1].ord & 0x40);) + { + for (int j = 0; j != 13; j++) + { + char c = *le->Name(j); + if (c == '\0') { lossy |= (niext && ni - niext > 3); break; } + if (c == '.') { if (ni > 8) { memset(entryname+8, ' ', 3); ni = 8; } if (!ni || niext) { lossy = 1; } niext = ni; continue; } + if (c == ' ' || ni == 11 || (ni == 8 && !niext)) { lossy = 1; continue; } + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { } + else if (c >= 'a' && c <= 'z') { c ^= 0x20; } + else if (strchr("$%'-_@~`!(){}^#&", c)) { } + else { lossy = 1; c = '_'; } + entryname[ni++] = (uint8_t)c; + } + } + + if (niext && niext != 8) + for (int i = 2; i >= 0; i--) + entryname[8+i] = entryname[niext+i], entryname[niext+i] = ' '; + if (niext && niext <= 4 && ni - niext > 3) + for (int i = niext + 3; i != 8; i++) + entryname[i] = ' '; + + if (lossy) + { + if (!niext) niext = ni; + for (int i = 1; i <= 999999; i++) + { + int taillen = (i<=9?2:i<=99?3:i<=999?4:i<=9999?5:i<=99999?6:7); + char* ptr = (char*)&entryname[niext + taillen > 8 ? 8 : niext + taillen]; + for (int j = i; j; j /= 10) *--ptr = '0'+(j%10); + *--ptr = '~'; + + bool conflict = false; + for (size_t e2 = firstidx; e2 != diridx; e2++) + if (!(entries[e2].attrib & (DOS_ATTR_VOLUME|DOS_ATTR_PENDING_SHORT_NAME)) && !memcmp(entryname, entries[e2].entryname, sizeof(e.entryname))) + { conflict = true; break; } + if (!conflict) break; + } + } + + uint8_t chksum = 0; + for (int i = 0; i != 11;) chksum = (chksum >> 1) + (chksum << 7) + entryname[i++]; + for (lfndirentry* le = (lfndirentry*)&e; le-- == (lfndirentry*)&e || !(le[1].ord & 0x40);) le->chksum = chksum; + e.attrib &= ~DOS_ATTR_PENDING_SHORT_NAME; + } + if (e.attrib & DOS_ATTR_DIRECTORY) // this reallocates ffdd.dirs so do this last + { + var_write(&e.loFirstClust, (const uint16_t)(2 + ffdd.dirs.size() / entriesPerCluster)); + ParseDir(ffdd, dir, filter, totlen, myFirstCluster); + } + } + } + + struct SumInfo { uint64_t used_bytes; const StringToPointerHashMap* filter; }; + static void SumFileSize(const char* path, bool is_dir, uint32_t size, uint16_t, uint16_t, uint8_t, Bitu data) + { + if (!((SumInfo*)data)->filter || !((SumInfo*)data)->filter->Get(path)) + ((SumInfo*)data)->used_bytes += (size + (32*1024-1)) / (32*1024) * (32*1024); // count as 32 kb clusters + } + }; + + Iter::SumInfo sum = { 0, NULL }; + Bitu freeSpace = 0, freeSpaceMB = 0; + uint32_t free_clusters = 0; + uint16_t drv_bytes_sector; uint8_t drv_sectors_cluster; uint16_t drv_total_clusters, drv_free_clusters; + rsize=true; + freec=0; + drv->AllocationInfo(&drv_bytes_sector, &drv_sectors_cluster, &drv_total_clusters, &drv_free_clusters); + free_clusters = freec?freec:drv_free_clusters; + freeSpace = (Bitu)drv_bytes_sector * (Bitu)drv_sectors_cluster * (Bitu)(freec?freec:free_clusters); + freeSpaceMB = freeSpace / (1024*1024); + rsize=false; + DriveFileIterator(drv, Iter::SumFileSize, (Bitu)&sum); + readOnly = (free_clusters == 0); + tomany = false; + + const uint32_t addFreeMB = (readOnly ? 0 : freeSpaceMB), totalMB = (uint32_t)(sum.used_bytes / (1024*1024)) + addFreeMB + 1; + if (totalMB >= 3072) { isFAT32 = true; sectorsPerCluster = 64; } // 32 kb clusters ( 98304 ~ FAT entries) + else if (totalMB >= 2048) { isFAT32 = true; sectorsPerCluster = 32; } // 16 kb clusters (131072 ~ 196608 FAT entries) + else if (totalMB >= 384) { isFAT32 = false; sectorsPerCluster = 64; } // 32 kb clusters ( 12288 ~ 65504 FAT entries) + else if (totalMB >= 192) { isFAT32 = false; sectorsPerCluster = 32; } // 16 kb clusters ( 12288 ~ 24576 FAT entries) + else if (totalMB >= 96) { isFAT32 = false; sectorsPerCluster = 16; } // 8 kb clusters ( 12288 ~ 24576 FAT entries) + else if (totalMB >= 48) { isFAT32 = false; sectorsPerCluster = 8; } // 4 kb clusters ( 12288 ~ 24576 FAT entries) + else if (totalMB >= 8) { isFAT32 = false; sectorsPerCluster = 4; } // 4 kb clusters ( 4096 ~ 24576 FAT entries) + else { isFAT32 = false; sectorsPerCluster = 1; } // 2 kb clusters ( ~ 16383 FAT entries) + + // mediadescriptor in very first byte of FAT table + Iter::SetFAT(*this, 0, (uint32_t)0xFFFFFF8); + Iter::SetFAT(*this, 1, (uint32_t)0xFFFFFFF); + + if (!isFAT32) + { + // this actually should never be anything but 512 for some FAT16 drivers + root.resize(512); + memset(&root[0], 0, sizeof(direntry) * 512); + } + + char dirbuf[DOS_PATHLENGTH+4]; + Iter::ParseDir(*this, dirbuf, NULL); + + const uint32_t bytesPerCluster = sectorsPerCluster * BYTESPERSECTOR; + const uint32_t entriesPerCluster = bytesPerCluster / sizeof(direntry); + uint32_t fileCluster = (uint32_t)(2 + dirs.size() / entriesPerCluster); + for (uint32_t fileSect = 0, rootOrDir = 0; rootOrDir != 2; rootOrDir++) + { + for (direntry& e : (rootOrDir ? dirs : root)) + { + if (!e.entrysize || (e.attrib & DOS_ATTR_LONG_NAME_MASK) == DOS_ATTR_LONG_NAME) continue; + var_write(&e.hiFirstClust, (const uint16_t)(fileCluster >> 16)); + var_write(&e.loFirstClust, (const uint16_t)(fileCluster)); + + // Write FAT link chain + uint32_t numClusters = (var_read(&e.entrysize) + bytesPerCluster - 1) / bytesPerCluster; + for (uint32_t i = fileCluster, iEnd = i + numClusters - 1; i != iEnd; i++) Iter::SetFAT(*this, i, i + 1); + Iter::SetFAT(*this, fileCluster + numClusters - 1, (uint32_t)0xFFFFFFF); + + files[fileAtSector[fileSect]].firstSect = fileSect; + + fileCluster += numClusters; + fileSect += numClusters * sectorsPerCluster; + } + } + + // Add at least one page after the last file or FAT spec minimume to make ScanDisk happy (even on read-only disks) + const uint32_t FATWidth = (isFAT32 ? 4 : 2), FATPageClusters = BYTESPERSECTOR / FATWidth, FATMinCluster = (isFAT32 ? 65525 : 4085) + FATPageClusters; + const uint32_t addFreeClusters = ((addFreeMB * (1024*1024/BYTESPERSECTOR)) + sectorsPerCluster - 1) / sectorsPerCluster; + const uint32_t targetClusters = fileCluster + (addFreeClusters < FATPageClusters ? FATPageClusters : addFreeClusters); + Iter::SetFAT(*this, (targetClusters < FATMinCluster ? FATMinCluster : targetClusters) - 1, 0); + const uint32_t totalClusters = (uint32_t)(fat.size() / FATWidth); // as set by Iter::SetFAT + + // on read-only disks, fill up the end of the FAT table with "Bad sector in cluster or reserved cluster" markers + if (readOnly) + for (uint32_t cluster = fileCluster; cluster != totalClusters; cluster++) + Iter::SetFAT(*this, cluster, 0xFFFFFF7); + + const uint32_t sectorsPerFat = (uint32_t)(fat.size() / BYTESPERSECTOR); + const uint16_t reservedSectors = (isFAT32 ? 32 : 1); + const uint32_t partSize = totalClusters * sectorsPerCluster + reservedSectors; + sect_fat1_start = SECT_BOOT + reservedSectors; + sect_fat2_start = sect_fat1_start + sectorsPerFat; + sect_root_start = sect_fat2_start + sectorsPerFat; + sect_dirs_start = sect_root_start + ((root.size() * sizeof(direntry) + BYTESPERSECTOR - 1) / BYTESPERSECTOR); + sect_files_start = sect_dirs_start + ((dirs.size() * sizeof(direntry) + BYTESPERSECTOR - 1) / BYTESPERSECTOR); + sect_files_end = sect_files_start + fileAtSector.size(); + sect_disk_end = SECT_BOOT + partSize; + assert(sect_disk_end >= sect_files_end); + + for (ffddFile& f : files) + f.firstSect += sect_files_start; + + uint32_t serial = 0; + if (!serial) + { + serial = DriveCalculateCRC32(&fat[0], fat.size(), 0); + if (root.size()) serial = DriveCalculateCRC32((uint8_t*)&root[0], root.size() * sizeof(direntry), serial); + if (dirs.size()) serial = DriveCalculateCRC32((uint8_t*)&dirs[0], dirs.size() * sizeof(direntry), serial); + } + + memset(&mbr, 0, sizeof(mbr)); + var_write((uint32_t *)&mbr.booter[440], serial); //4 byte disk serial number + var_write(&mbr.pentry[0].bootflag, 0x80); //Active bootable + if ((sect_disk_end - 1) / (HEADCOUNT * SECTORSPERTRACK) > 0x3FF) + { + mbr.pentry[0].beginchs[0] = mbr.pentry[0].beginchs[1] = mbr.pentry[0].beginchs[2] = 0; + mbr.pentry[0].endchs[0] = mbr.pentry[0].endchs[1] = mbr.pentry[0].endchs[2] = 0; + } + else + { + chs_write(mbr.pentry[0].beginchs, SECT_BOOT); + chs_write(mbr.pentry[0].endchs, sect_disk_end - 1); + } + var_write(&mbr.pentry[0].absSectStart, SECT_BOOT); + var_write(&mbr.pentry[0].partSize, partSize); + mbr.magic1 = 0x55; mbr.magic2 = 0xaa; + + memset(&bootsec, 0, sizeof(bootsec)); + memcpy(bootsec.nearjmp, "\xEB\x3C\x90", sizeof(bootsec.nearjmp)); + memcpy(bootsec.oemname, "MSWIN4.1", sizeof(bootsec.oemname)); + var_write((uint16_t *const)&bootsec.bytespersector, (const uint16_t)BYTESPERSECTOR); + var_write(&bootsec.sectorspercluster, sectorsPerCluster); + var_write(&bootsec.reservedsectors, reservedSectors); + var_write(&bootsec.fatcopies, 2); + var_write(&bootsec.totalsectorcount, 0); // 16 bit field is 0, actual value is in totalsecdword + var_write(&bootsec.mediadescriptor, 0xF8); //also in FAT[0] + var_write(&bootsec.sectorspertrack, SECTORSPERTRACK); + var_write(&bootsec.headcount, HEADCOUNT); + var_write(&bootsec.hiddensectorcount, SECT_BOOT); + var_write(&bootsec.totalsecdword, partSize); + bootsec.magic1 = 0x55; bootsec.magic2 = 0xaa; + if (!isFAT32) // FAT16 + { + var_write(&mbr.pentry[0].parttype, 0x04); //FAT16 + var_write((uint16_t *const)&bootsec.rootdirentries, (const uint16_t)root.size()); + var_write((uint16_t *const)&bootsec.sectorsperfat, (const uint16_t)sectorsPerFat); + bootsec.bootcode[0] = 0x80; //Physical drive (harddisk) flag + bootsec.bootcode[2] = 0x29; //Extended boot signature + var_write((uint32_t *const)&bootsec.bootcode[3], (const uint32_t)(serial + 1)); //4 byte partition serial number + memcpy(&bootsec.bootcode[7], "NO NAME ", 11); // volume label + memcpy(&bootsec.bootcode[18], "FAT16 ", 8); // file system string name + } + else // FAT32 + { + var_write(&mbr.pentry[0].parttype, 0x0C); //FAT32 + var_write((uint32_t *const)&bootsec.bootcode[0], sectorsPerFat); + var_write((uint32_t *const)&bootsec.bootcode[8], (const uint32_t)2); // First cluster number of the root directory + var_write((uint16_t *const)&bootsec.bootcode[12], (const uint16_t)1); // Sector of FSInfo structure in offset from top of the FAT32 volume + var_write((uint16_t *const)&bootsec.bootcode[14], (const uint16_t)6); // Sector of backup boot sector in offset from top of the FAT32 volume + bootsec.bootcode[28] = 0x80; //Physical drive (harddisk) flag + bootsec.bootcode[30] = 0x29; //Extended boot signature + var_write((uint32_t *const)&bootsec.bootcode[31], (const uint32_t)(serial + 1)); //4 byte partition serial number + memcpy(&bootsec.bootcode[35], "NO NAME ", 11); // volume label + memcpy(&bootsec.bootcode[46], "FAT32 ", 8); // file system string name + + memset(fsinfosec, 0, sizeof(fsinfosec)); + var_write((uint32_t *const)&fsinfosec[0], (const uint32_t)0x41615252); //lead signature + var_write((uint32_t *const)&fsinfosec[484], (const uint32_t)0x61417272); //Another signature + Bitu freeclusters = (Bitu)freeSpace / (BYTESPERSECTOR * sectorsPerCluster); + var_write((uint32_t *const)&fsinfosec[488], (const uint32_t)(readOnly ? 0x0 : (freeclusters < 0xFFFFFFFF ? freeclusters : 0xFFFFFFFF))); //last known free cluster count (all FF is unknown) + var_write((uint32_t *const)&fsinfosec[492], (const uint32_t)0xFFFFFFFF); //the cluster number at which the driver should start looking for free clusters (all FF is unknown) + var_write((uint32_t *const)&fsinfosec[508], (const uint32_t)0xAA550000); //ending signature + } + } + + static void chs_write(uint8_t* chs, uint32_t lba) + { + uint32_t cylinder = lba / (HEADCOUNT * SECTORSPERTRACK); + uint32_t head = (lba / SECTORSPERTRACK) % HEADCOUNT; + uint32_t sector = (lba % SECTORSPERTRACK) + 1; + if (head > 0xFF || sector > 0x3F || cylinder > 0x3FF) + LOG_MSG("Warning: Invalid CHS data - %X, %X, %X\n", head, sector, cylinder); + chs[0] = (uint8_t)(head & 0xFF); + chs[1] = (uint8_t)((sector & 0x3F) | ((cylinder >> 8) & 0x3)); + chs[2] = (uint8_t)(cylinder & 0xFF); + } + + uint8_t WriteSector(uint32_t sectnum, const void* data) + { + if (sectnum >= sect_disk_end) return 1; + if (sectnum == SECT_MBR) + { + // Windows 9x writes the disk timestamp into the booter area on startup. + // Just copy that part over so it doesn't get treated as a difference that needs to be stored. + memcpy(mbr.booter, data, sizeof(mbr.booter)); + } + + if (readOnly) return 0; // just return without error to avoid bluescreens in Windows 9x + + if (sectnum >= diffSectors.size()) diffSectors.resize(sectnum + 128); + uint32_t *cursor_ptr = &diffSectors[sectnum].cursor, cursor_val = *cursor_ptr; + + int is_different; + uint8_t filebuf[BYTESPERSECTOR]; + void* unmodified = GetUnmodifiedSector(sectnum, filebuf); + if (!unmodified) + { + is_different = false; // to be equal it must be filled with zeroes + for (uint64_t* p = (uint64_t*)data, *pEnd = p + (BYTESPERSECTOR / sizeof(uint64_t)); p != pEnd; p++) + if (*p) { is_different = true; break; } + } + else is_different = memcmp(unmodified, data, BYTESPERSECTOR); + + if (is_different) + { + if (!saveFile && !savePath.empty()) + { + saveFile = fopen(savePath.c_str(), "wb+"); + if (saveFile) { fwrite("FFDD\x1", 5, 1, saveFile); saveEndCursor = 5; }; + savePath.clear(); + } + if (cursor_val == NULL_CURSOR && diffFreeCursors.size()) + { + *cursor_ptr = cursor_val = diffFreeCursors.back(); + diffFreeCursors.pop_back(); + } + if (saveFile) + { + if (cursor_val == NULL_CURSOR) + { + uint32_t sectnumval; + var_write(§numval, sectnum); + *cursor_ptr = cursor_val = saveEndCursor; + saveEndCursor += sizeof(sectnumval) + BYTESPERSECTOR; + fseeko64(saveFile, cursor_val, SEEK_SET); + fwrite(§numval, sizeof(sectnumval), 1, saveFile); + } + else + fseeko64(saveFile, cursor_val + sizeof(sectnum), SEEK_SET); + fwrite(data, BYTESPERSECTOR, 1, saveFile); + } + else + { + if (cursor_val == NULL_CURSOR) + { + *cursor_ptr = cursor_val = (uint32_t)diffSectorBufs.size(); + diffSectorBufs.resize(cursor_val + 1); + } + memcpy(diffSectorBufs[cursor_val].data, data, BYTESPERSECTOR); + } + cacheSectorNumber[sectnum % CACHECOUNT] = (uint32_t)-1; // invalidate cache + } + else if (cursor_val != NULL_CURSOR) + { + if (saveFile) + { + // mark sector in diff file as free + uint32_t sectnumval = 0xFFFFFFFF; + fseeko64(saveFile, cursor_val, SEEK_SET); + fwrite(§numval, sizeof(sectnumval), 1, saveFile); + } + diffFreeCursors.push_back(cursor_val); + *cursor_ptr = NULL_CURSOR; + cacheSectorNumber[sectnum % CACHECOUNT] = (uint32_t)-1; // invalidate cache + } + return 0; + } + + void* GetUnmodifiedSector(uint32_t sectnum, void* filebuf) + { + if (sectnum >= sect_files_end) {} + else if (sectnum >= sect_files_start) + { + uint32_t idx = fileAtSector[sectnum - sect_files_start]; + ffddFile& f = files[idx]; + DOS_File* df = NULL; + for (uint32_t i = 0; i != KEEPOPENCOUNT; i++) + if (openIndex[i] == idx && openFiles[i]) + { df = openFiles[i]; break; } + if (!df) + { + openCursor = (openCursor + 1) % KEEPOPENCOUNT; + DOS_File*& cachedf = openFiles[openCursor]; + if (cachedf) + { + cachedf->Close(); + delete cachedf; + cachedf = NULL; + } + bool res = drive->FileOpen(&df, f.path, OPEN_READ) ; + if (res) + { + df->AddRef(); + cachedf = df; + openIndex[openCursor] = idx; + } + else return NULL; + } + if (df) + { + uint32_t pos = (sectnum - f.firstSect) * BYTESPERSECTOR; + uint16_t read = (uint16_t)BYTESPERSECTOR; + df->Seek(&pos, DOS_SEEK_SET); + if (!df->Read((uint8_t*)filebuf, &read)) { read = 0; assert(0); } + if (read != BYTESPERSECTOR) + memset((uint8_t*)filebuf + read, 0, BYTESPERSECTOR - read); + return filebuf; + } + } + else if (sectnum >= sect_dirs_start) return &dirs[(sectnum - sect_dirs_start) * (BYTESPERSECTOR / sizeof(direntry))]; + else if (sectnum >= sect_root_start) return &root[(sectnum - sect_root_start) * (BYTESPERSECTOR / sizeof(direntry))]; + else if (sectnum >= sect_fat2_start) return &fat[(sectnum - sect_fat2_start) * BYTESPERSECTOR]; + else if (sectnum >= sect_fat1_start) return &fat[(sectnum - sect_fat1_start) * BYTESPERSECTOR]; + else if (sectnum == SECT_BOOT) return &bootsec; + else if (sectnum == SECT_MBR) return &mbr; + else if (sectnum == SECT_BOOT+1) return fsinfosec; + else if (sectnum == SECT_BOOT+2) return fsinfosec; // additional boot loader code (anything is ok for us but needs 0x55AA footer signature) + else if (sectnum == SECT_BOOT+6) return &bootsec; // boot sector copy + else if (sectnum == SECT_BOOT+7) return fsinfosec; // boot sector copy + else if (sectnum == SECT_BOOT+8) return fsinfosec; // boot sector copy + return NULL; + } + + uint8_t ReadSector(uint32_t sectnum, void* data) + { + uint32_t sectorHash = sectnum % CACHECOUNT; + void *cachedata = cacheSectorData[sectorHash]; + if (cacheSectorNumber[sectorHash] == sectnum) + { + memcpy(data, cachedata, BYTESPERSECTOR); + return 0; + } + cacheSectorNumber[sectorHash] = sectnum; + + void *src; + uint32_t cursor = (sectnum >= diffSectors.size() ? NULL_CURSOR : diffSectors[sectnum].cursor); + if (cursor != NULL_CURSOR) + { + if (saveFile) + { + fseeko64(saveFile, cursor + sizeof(sectnum), SEEK_SET); + src = (fread(cachedata, BYTESPERSECTOR, 1, saveFile) ? cachedata : NULL); + } + else src = diffSectorBufs[cursor].data; + } + else src = GetUnmodifiedSector(sectnum, cachedata); + + if (src) memcpy(data, src, BYTESPERSECTOR); + else memset(data, 0, BYTESPERSECTOR); + if (src != cachedata) memcpy(cachedata, data, BYTESPERSECTOR); + return 0; + } +}; + diskGeo DiskGeometryList[] = { { 160, 8, 1, 40, 0, 512, 64, 1, 0xFE}, // IBM PC double density 5.25" single-sided 160KB { 180, 9, 1, 40, 0, 512, 64, 2, 0xFC}, // IBM PC double density 5.25" single-sided 180KB @@ -91,7 +932,7 @@ void FreeBIOSDiskList() { for (int i=0;i < MAX_DISK_IMAGES;i++) { if (imageDiskList[i] != NULL) { if (i >= 2) IDE_Hard_Disk_Detach(i); - imageDiskList[i]->Release(); + if (!imageDiskList[i]->ffdd) imageDiskList[i]->Release(); imageDiskList[i] = NULL; } } @@ -269,6 +1110,8 @@ uint8_t imageDisk::Read_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,v } uint8_t imageDisk::Read_AbsoluteSector(uint32_t sectnum, void * data) { + if (ffdd) return ffdd->ReadSector(sectnum, data); + uint64_t bytenum,res; int got; @@ -314,6 +1157,8 @@ uint8_t imageDisk::Write_Sector(uint32_t head,uint32_t cylinder,uint32_t sector, uint8_t imageDisk::Write_AbsoluteSector(uint32_t sectnum, const void *data) { + if (ffdd) return ffdd->WriteSector(sectnum, data); + uint64_t bytenum; bytenum = (uint64_t)sectnum * sector_size; @@ -346,7 +1191,8 @@ uint32_t imageDisk::Get_Reserved_Cylinders() { imageDisk::imageDisk(IMAGE_TYPE class_id) : class_id(class_id) { } -imageDisk::imageDisk(FILE* diskimg, const char* diskName, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t sector_size, bool hardDrive) { +imageDisk::imageDisk(FILE* diskimg, const char* diskName, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t sector_size, bool hardDrive) : ffdd(NULL) +{ if (diskName) this->diskname = diskName; this->cylinders = cylinders; this->heads = heads; @@ -642,6 +1488,50 @@ imageDisk::imageDisk(FILE* imgFile, const char* imgName, uint32_t imgSizeK, bool } } +imageDisk::imageDisk(class DOS_Drive *useDrive) +{ + ffdd = new fatFromDOSDrive(useDrive); + diskimg = NULL; + diskname[0] = '\0'; + hardDrive = true; + Set_GeometryForHardDisk(); +} + +imageDisk::~imageDisk() +{ + if(diskimg != NULL) { + fclose(diskimg); + diskimg=NULL; + } + if (ffdd) + delete ffdd; +} + +void imageDisk::Set_GeometryForHardDisk() +{ + sector_size = 512; + partTable mbrData; + for (int m = (Read_AbsoluteSector(0, &mbrData) ? 0 : 4); m--;) + { + if(!mbrData.pentry[m].partSize) continue; + bootstrap bootbuffer; + if (Read_AbsoluteSector(mbrData.pentry[m].absSectStart, &bootbuffer)) continue; + bootbuffer.sectorspertrack = var_read(&bootbuffer.sectorspertrack); + bootbuffer.headcount = var_read(&bootbuffer.headcount); + uint32_t setSect = bootbuffer.sectorspertrack; + uint32_t setHeads = bootbuffer.headcount; + uint32_t setCyl = (mbrData.pentry[m].absSectStart + mbrData.pentry[m].partSize) / (setSect * setHeads); + Set_Geometry(setHeads, setCyl, setSect, 512); + return; + } + if (!diskimg) return; + uint32_t diskimgsize; + fseek(diskimg,0,SEEK_END); + diskimgsize = (uint32_t)ftell(diskimg); + fseek(diskimg,current_fpos,SEEK_SET); + Set_Geometry(16, (uint32_t)(diskimgsize / (512 * 63 * 16)), 63, 512); +} + void imageDisk::Set_Geometry(uint32_t setHeads, uint32_t setCyl, uint32_t setSect, uint32_t setSectSize) { Bitu bigdisk_shift = 0; diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp index 7fc473d26..369399e5a 100644 --- a/src/misc/programs.cpp +++ b/src/misc/programs.cpp @@ -57,7 +57,7 @@ extern const char *modifier; extern unsigned int sendkeymap; extern std::string langname, configfile, dosbox_title; extern int autofixwarn, enablelfn, fat32setver, paste_speed, wheel_key, freesizecap, wpType, wpVersion, wpBG, wpFG, lastset, blinkCursor; -extern bool dos_kernel_disabled, force_nocachedir, wpcolon, lockmount, enable_config_as_shell_commands, lesssize, load, winrun, winautorun, startcmd, startwait, startquiet, starttranspath, mountwarning, wheel_guest, clipboard_dosapi, noremark_save_state, force_load_state, sync_time, manualtime, ttfswitch, loadlang, showbold, showital, showline, showsout, char512, printfont, rtl, gbk, chinasea, uao, showdbcs, dbcs_sbcs, autoboxdraw, halfwidthkana, ticksLocked, outcon, enable_dbcs_tables, show_recorded_filename; +extern bool dos_kernel_disabled, force_nocachedir, wpcolon, convertimg, lockmount, enable_config_as_shell_commands, lesssize, load, winrun, winautorun, startcmd, startwait, startquiet, starttranspath, mountwarning, wheel_guest, clipboard_dosapi, noremark_save_state, force_load_state, sync_time, manualtime, ttfswitch, loadlang, showbold, showital, showline, showsout, char512, printfont, rtl, gbk, chinasea, uao, showdbcs, dbcs_sbcs, autoboxdraw, halfwidthkana, ticksLocked, outcon, enable_dbcs_tables, show_recorded_filename, internal_program; /* This registers a file on the virtual drive and creates the correct structure for it*/ @@ -136,7 +136,9 @@ void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main,const char * ipe->comsize = size; ipe->comdata = comdata; internal_progs.push_back(ipe); + internal_program = true; VFILE_Register(name,ipe->comdata,ipe->comsize,dir); + internal_program = false; } static Bitu PROGRAMS_Handler(void) { @@ -641,6 +643,7 @@ void ApplySetting(std::string pvar, std::string inputline, bool quiet) { if (freesizestr == "fixed" || freesizestr == "false" || freesizestr == "0") freesizecap = 0; else if (freesizestr == "relative" || freesizestr == "2") freesizecap = 2; else freesizecap = 1; + convertimg = section->Get_bool("convertdrivefat"); wpcolon = section->Get_bool("leading colon write protect image"); lockmount = section->Get_bool("locking disk image mount"); if (!strcasecmp(inputline.substr(0, 9).c_str(), "saveslot=")) SetGameState_Run(section->Get_int("saveslot")-1); diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp index da68fe8df..76e8ffd8c 100644 --- a/src/shell/shell.cpp +++ b/src/shell/shell.cpp @@ -50,9 +50,9 @@ #endif #include "build_timestamp.h" -extern bool startcmd, startwait, startquiet, winautorun; +extern bool dos_shell_running_program, mountwarning, winautorun; +extern bool startcmd, startwait, startquiet, internal_program; extern bool halfwidthkana, force_conversion, showdbcs; -extern bool dos_shell_running_program, mountwarning; extern bool addovl, addipx, addne2k, enableime, gbk; extern const char* RunningProgram; extern int enablelfn, msgcodepage; @@ -212,7 +212,11 @@ void AutoexecObject::CreateAutoexec(void) { } sprintf((autoexec_data + auto_len),"%s\r\n",linecopy.c_str()); } - if (first_shell) VFILE_Register("AUTOEXEC.BAT",(uint8_t *)autoexec_data,(uint32_t)strlen(autoexec_data)); + if (first_shell) { + internal_program = true; + VFILE_Register("AUTOEXEC.BAT",(uint8_t *)autoexec_data,(uint32_t)strlen(autoexec_data)); + internal_program = false; + } } void AutoexecObject::Uninstall() { @@ -854,7 +858,9 @@ void DOS_Shell::Prepare(void) { strcat(config_data, section->Get_string("rem")); strcat(config_data, "\r\n"); } + internal_program = true; VFILE_Register("CONFIG.SYS",(uint8_t *)config_data,(uint32_t)strlen(config_data)); + internal_program = false; #if defined(WIN32) if (!control->opt_securemode&&!control->SecureMode()) { diff --git a/src/shell/shell_cmds.cpp b/src/shell/shell_cmds.cpp index bc8ec92bf..2dd293264 100644 --- a/src/shell/shell_cmds.cpp +++ b/src/shell/shell_cmds.cpp @@ -1534,8 +1534,8 @@ char *FormatTime(Bitu hour, Bitu min, Bitu sec, Bitu msec) { return retBuf; } - -uint32_t byte_count,file_count,dir_count; +uint64_t byte_count; +uint32_t file_count,dir_count; Bitu p_count; std::vector dirs, adirs; static bool dirPaused(DOS_Shell * shell, Bitu w_size, bool optP, bool optW, bool show=true) { @@ -1581,7 +1581,8 @@ static bool doDir(DOS_Shell * shell, char * args, DOS_DTA dta, char * numformat, } if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\"); - uint32_t cbyte_count=0,cfile_count=0,w_count=0; + uint64_t cbyte_count=0; + uint32_t cfile_count=0,w_count=0; int fbak=lfn_filefind_handle; lfn_filefind_handle=uselfn&&!optZ?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE; bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME), found=true, first=true; @@ -2064,7 +2065,7 @@ void DOS_Shell::CMD_DIR(char * args) { if ((dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) && Drives[drive]->AllocationInfo32(&bytes_sector32,§ors_cluster32,&total_clusters32,&free_clusters32)) { /* FAT32 aware extended API */ freec=0; - free_space=(Bitu)bytes_sector32 * (Bitu)sectors_cluster32 * (Bitu)(freec?freec:free_clusters32); + free_space=(Bitu)bytes_sector32 * (Bitu)sectors_cluster32 * (Bitu)free_clusters32; } else { uint16_t bytes_sector;uint8_t sectors_cluster;uint16_t total_clusters;uint16_t free_clusters; rsize=true;