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;