mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1559 lines
44 KiB
1559 lines
44 KiB
#include "diskcopy.h"
|
|
#include "ids.h"
|
|
#include "..\..\inc\help.h"
|
|
|
|
#define WM_DONE_WITH_FORMAT (WM_USER + 100)
|
|
|
|
#ifndef WINNT
|
|
//
|
|
// NT has multiple disk formats handled by FMIFS.DLL
|
|
//
|
|
#ifdef DBCS
|
|
// changed from NEC_98 to DBCS.
|
|
// 3mode FD will use this on non NEC and BOOTSEC doesn't
|
|
// suffer from this anyway.
|
|
#define SEC_SIZE 1024
|
|
#else
|
|
#define SEC_SIZE 512
|
|
#endif
|
|
|
|
#define DEVPB_DEVATT_DMF 0x0800
|
|
//
|
|
// The low BYTE of Options is the lock sub-type
|
|
//
|
|
#define LFS_OPT_EXCLUSIVE 0 // Only owner of lock can read or write
|
|
#define LFS_OPT_ALLOWOPEN 1 // See below option definitions
|
|
#define LFS_OPT_ALLOWRDBLKWRT 2 // Requires LFS_OPT_ALLOWOPEN first
|
|
#define LFS_OPT_BLKALL 3 // Requires LFS_OPT_ALLOWRDBLKWRT first
|
|
//
|
|
// The HIGH WORD of Options is the lock type modifyer for LFS_OPT_ALLOWOPEN
|
|
//
|
|
// NOTE that this is actually a flag field as opposed to a value setting.
|
|
//
|
|
#define LFS_OPT_ALLOWRDFLWRT 0x0000 // Allow others to read, fail writes
|
|
#define LFS_OPT_ALLOWRDWRT 0x0001 // Allow others to read and write
|
|
#define LFS_OPT_ALLOWMMACT 0x0000 // Allow memory mapped file activity
|
|
#define LFS_OPT_NOMMACT 0x0002 // Fail memory mapped file activity
|
|
#define LFS_OPT_FORMAT 0x0004 // Mount default FSD
|
|
|
|
|
|
#define IOCTL_FORMAT 0x0842
|
|
#define IOCTL_GET_DPB 0x0860
|
|
#define IOCTL_SET_DPB 0x0840
|
|
#define IOCTL_READ 0x0861
|
|
#define IOCTL_WRITE 0x0841
|
|
#define IOCTL_LOCK 0x084A
|
|
#define IOCTL_UNLOCK 0x086A
|
|
|
|
/* Media descriptor values for different floppy drives */
|
|
// NOTE: these are not all unique!
|
|
#define MEDIA_160 0xFE /* 160KB */
|
|
#define MEDIA_320 0xFF /* 320KB */
|
|
#define MEDIA_180 0xFC /* 180KB */
|
|
#define MEDIA_360 0xFD /* 360KB */
|
|
#define MEDIA_1200 0xF9 /* 1.2MB */
|
|
#define MEDIA_720 0xF9 /* 720KB */
|
|
#ifdef NEC_98
|
|
#define MEDIA_1250 0xFE /* 1.25MB(1024bps) */
|
|
#endif
|
|
#define MEDIA_1440 0xF0 /* 1.44M */
|
|
#define MEDIA_2880 0xF0 /* 2.88M */
|
|
|
|
|
|
/* DriveIOCTL error codes */
|
|
#define SECNOTFOUND 0x1B
|
|
#define CRCERROR 0x17
|
|
#define GENERALERROR 0x1F
|
|
|
|
#pragma pack(1)
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* BIOS Parameter Block Structure - */
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct
|
|
{
|
|
WORD cbSec; /* Bytes per sector */
|
|
BYTE secPerClus; /* Sectors per cluster */
|
|
WORD cSecRes; /* Reserved sectors */
|
|
BYTE cFAT; /* FATS */
|
|
WORD cDir; /* Root Directory Entries */
|
|
WORD cSec; /* Total number of sectors in image */
|
|
BYTE bMedia; /* Media descriptor */
|
|
WORD secPerFAT; /* Sectors per FAT */
|
|
WORD secPerTrack; /* Sectors per track */
|
|
WORD cHead; /* Heads */
|
|
WORD cSecHidden; /* Hidden sectors */
|
|
} BPB, *PBPB;
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Drive Parameter Block Structure - */
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct
|
|
{
|
|
BYTE drive;
|
|
BYTE unit;
|
|
WORD sector_size;
|
|
BYTE cluster_mask;
|
|
BYTE cluster_shift;
|
|
WORD first_FAT;
|
|
BYTE FAT_count;
|
|
WORD root_entries;
|
|
WORD first_sector;
|
|
WORD max_cluster;
|
|
BYTE FAT_size;
|
|
WORD dir_sector;
|
|
LONG reserved1;
|
|
BYTE media;
|
|
BYTE first_access;
|
|
BYTE reserved2[4];
|
|
WORD next_free;
|
|
WORD free_cnt;
|
|
BYTE DOS4_Extra;
|
|
} DPB, *PDPB;
|
|
|
|
#define MAX_SEC_PER_TRACK 40
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Device Parameter Block Structure - */
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct
|
|
{
|
|
BYTE SplFunctions;
|
|
BYTE devType;
|
|
WORD devAtt; // see dskmaint for what these are
|
|
WORD NumCyls;
|
|
BYTE bMediaType; /* 0=>1.2MB and 1=>360KB */
|
|
BPB BPB;
|
|
BYTE reserved3[MAX_SEC_PER_TRACK * 4 + 2];
|
|
} DEVPB, *PDEVPB;
|
|
|
|
#define TRACKLAYOUT_OFFSET (7+31) /* Offset of tracklayout
|
|
* in a Device Parameter Block */
|
|
|
|
typedef struct
|
|
{
|
|
BYTE jump[3]; /* 3 byte jump */
|
|
BYTE label[8]; /* OEM name and version */
|
|
BPB BPB; /* BPB */
|
|
BYTE bootdrive; /* INT 13h indicator for boot device */
|
|
BYTE dontcare[SEC_SIZE-12-3-sizeof(BPB)];
|
|
BYTE phydrv;
|
|
WORD signature;
|
|
} BOOTSEC;
|
|
|
|
#pragma pack()
|
|
|
|
#endif // ndef WINNT
|
|
|
|
typedef struct
|
|
{
|
|
int nSrcDrive;
|
|
int nDestDrive;
|
|
UINT nCylinderSize;
|
|
UINT nCylinders;
|
|
UINT nHeads;
|
|
UINT nSectorsPerTrack;
|
|
UINT nSectorSize;
|
|
#ifndef WINNT
|
|
PDEVPB pTrackLayout; /* DEVPB with the track layout */
|
|
|
|
LPBYTE pCopyBuffer;
|
|
DWORD dwCopyBufferSize;
|
|
#else
|
|
BOOL bNotifiedWriting;
|
|
#endif
|
|
BOOL bFormatTried;
|
|
|
|
HWND hdlg;
|
|
HANDLE hThread;
|
|
BOOL bUserAbort;
|
|
DWORD dwError;
|
|
} DISKINFO, *PDISKINFO;
|
|
|
|
int ErrorMessageBox(DISKINFO* pdi, UINT uFlags);
|
|
void SetStatusText(DISKINFO* pdi, int id);
|
|
BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck);
|
|
|
|
#ifndef WINNT
|
|
|
|
extern BOOL DriveIOCTL(int iDrive, int cmd, void *pv);
|
|
BOOL LockDrive(int iDrive, BOOL fLock, WORD wPermissions);
|
|
|
|
const TCHAR c_szVWIN32[] = TEXT("\\\\.\\vwin32");
|
|
|
|
// in:
|
|
// iDrive 0 based drive number
|
|
//
|
|
// returns:
|
|
// TRUE success
|
|
// FALSE failure
|
|
|
|
BOOL DriveIOCTL(int iDrive, int cmd, void *pv)
|
|
{
|
|
DWORD reg[7];
|
|
DWORD cbBytes;
|
|
HANDLE h;
|
|
BOOL bRet;
|
|
|
|
reg[0] = iDrive + 1; // make 1 based drive number
|
|
reg[1] = (DWORD)pv; // out buffer
|
|
reg[2] = cmd; // device specific command code
|
|
reg[3] = 0x440D; // generic read ioctl
|
|
reg[6] = 0x0001; // flags, assume error (carry)
|
|
|
|
h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
DeviceIoControl(h, 1, ®, sizeof(reg), ®, sizeof(reg), &cbBytes, 0);
|
|
CloseHandle(h);
|
|
}
|
|
|
|
bRet = !(reg[6] & 0x0001);
|
|
|
|
#ifdef DEBUG
|
|
// all we ever get is access denied
|
|
if (!bRet) {
|
|
DebugMsg(DM_TRACE, TEXT("IOCtl error %x, %d"), reg[3], GetLastError());
|
|
//SetLastError(reg[3]);
|
|
}
|
|
#endif
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL ReadWriteSector(void *pBuf, UINT nFunc, UINT nDrive, UINT nCylinder, UINT wHead, UINT wCount)
|
|
{
|
|
#pragma pack(1)
|
|
typedef struct
|
|
{
|
|
BYTE bSplFn;
|
|
WORD wHead;
|
|
WORD nCylinder;
|
|
WORD wStSector;
|
|
WORD wCount;
|
|
LPBYTE pBuf;
|
|
WORD wSel;
|
|
} RW_PARMBLOCK;
|
|
#pragma pack()
|
|
RW_PARMBLOCK rwp;
|
|
|
|
rwp.bSplFn = 0;
|
|
rwp.wHead = wHead;
|
|
rwp.nCylinder = nCylinder;
|
|
rwp.wStSector = 0;
|
|
rwp.wCount = wCount;
|
|
rwp.pBuf = pBuf;
|
|
|
|
_asm mov rwp.wSel, ds
|
|
|
|
return DriveIOCTL(nDrive, nFunc, &rwp);
|
|
}
|
|
|
|
|
|
|
|
// This reads the boot sector of a floppy and returns a ptr to
|
|
// the BIOS PARAMETER BLOCK in the Boot sector.
|
|
// BUGBUG: boot sector sizes != 512 will puke
|
|
|
|
BOOL GetBootBPB(int nDrive, PBPB pBPB)
|
|
{
|
|
BOOTSEC Boot;
|
|
if (ReadWriteSector(&Boot, IOCTL_READ, nDrive, 0, 0, 1) &&
|
|
(Boot.jump[0] == 0xEB || Boot.jump[0] == 0xE9))
|
|
{
|
|
*pBPB = Boot.BPB;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Gets get the BPB of the Physical Drive.
|
|
|
|
BOOL GetBPB(int nDrive, PDEVPB pDevicePB, BYTE bDisk)
|
|
{
|
|
/* All fields in pDevicePB must be initialized to zero. */
|
|
memset(pDevicePB, 0, sizeof(DEVPB));
|
|
pDevicePB->SplFunctions = bDisk;
|
|
/* Spl Function field must be set to get parameters */
|
|
Assert(pDevicePB->SplFunctions == 0);
|
|
return DriveIOCTL(nDrive, IOCTL_GET_DPB, pDevicePB);
|
|
}
|
|
|
|
|
|
/* Checks whether the two BPB are compatible for the purpose of performing
|
|
* the diskcopy operation.
|
|
*/
|
|
|
|
BOOL CheckBPBCompatibility(PDEVPB pSrc, PDEVPB pDst)
|
|
{
|
|
#ifndef NEC_98
|
|
/* Let us compare the media byte */
|
|
if (pSrc->BPB.bMedia == 0xF9)
|
|
{
|
|
/* If the source and dest have the same number of sectors,
|
|
* or if srce is 720KB and Dest is 1.44MB floppy drive,
|
|
* thnigs are kosher.
|
|
*/
|
|
if ((pSrc->BPB.cSec == pDst->BPB.cSec) ||
|
|
((pSrc->BPB.secPerTrack == 9) && (pDst->BPB.bMedia == 0xF0)))
|
|
return TRUE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* If they have the same media byte */
|
|
if ((pSrc->BPB.bMedia == pDst->BPB.bMedia) &&
|
|
(pSrc->BPB.cbSec == pDst->BPB.cbSec) && // bytes per sector are the same
|
|
(pSrc->BPB.cSec == pDst->BPB.cSec)) // total sectors on drive are the same
|
|
return TRUE; /* They are compatible */
|
|
#ifndef NEC_98
|
|
else if
|
|
/* srce is 160KB and dest is 320KB drive */
|
|
(((pSrc->BPB.bMedia == MEDIA_160) && (pDst->BPB.bMedia == MEDIA_320)) ||
|
|
/* or if srce is 180KB and dest is 360KB drive */
|
|
((pSrc->BPB.bMedia == MEDIA_180) && (pDst->BPB.bMedia == MEDIA_360)) ||
|
|
/* or if srce is 1.44MB and dest is 2.88MB drive */
|
|
((pSrc->BPB.bMedia == MEDIA_1440) && (pDst->BPB.bMedia == MEDIA_2880)
|
|
&& ((pSrc->devType == 7) || (pSrc->devType == 9))
|
|
&& (pDst->devType == 9)) ||
|
|
/* or if srce is 360KB and dest is 1.2MB drive */
|
|
((pSrc->BPB.bMedia == MEDIA_360) && (pDst->BPB.secPerTrack == 15)))
|
|
return TRUE; /* They are compatible */
|
|
#endif
|
|
}
|
|
|
|
/* All other combinations are currently incompatible. */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PDEVPB BuildDEVPB(PDEVPB pDEVPB)
|
|
{
|
|
PDEVPB pNewDEVPB = LocalAlloc(LPTR, TRACKLAYOUT_OFFSET + 2 + pDEVPB->BPB.secPerTrack * 4);
|
|
if (pNewDEVPB)
|
|
{
|
|
WORD wTrackNumber, *pData;
|
|
|
|
memcpy(pNewDEVPB, pDEVPB, TRACKLAYOUT_OFFSET);
|
|
|
|
pData = (WORD *)((LPBYTE)pNewDEVPB + TRACKLAYOUT_OFFSET);
|
|
*pData++ = pDEVPB->BPB.secPerTrack;
|
|
|
|
for (wTrackNumber = 1; wTrackNumber <= pDEVPB->BPB.secPerTrack; wTrackNumber++)
|
|
{
|
|
*pData++ = wTrackNumber;
|
|
*pData++ = pDEVPB->BPB.cbSec;
|
|
}
|
|
}
|
|
return pNewDEVPB;
|
|
}
|
|
|
|
|
|
|
|
/* Saves a copy of the drive parameters block and
|
|
* Checks if the BPB of Drive and BPB of disk are different and if
|
|
* so, modifies the drive parameter block accordingly.
|
|
*/
|
|
|
|
BOOL ModifyDeviceParams(int nDrive, PDEVPB pdpbParams, PDEVPB *ppSaveDevPB,
|
|
PBPB pDriveBPB, PBPB pMediaBPB)
|
|
{
|
|
PDEVPB pNewDPB;
|
|
|
|
*ppSaveDevPB = BuildDEVPB(pdpbParams);
|
|
if (!*ppSaveDevPB)
|
|
return FALSE;
|
|
|
|
/* Check if the Disk and Drive have the same parameters */
|
|
//if (pMediaBPB->bMedia != pDriveBPB->bMedia)
|
|
//{
|
|
/* They are not equal; So, it must be a 360KB floppy in a 1.2MB drive
|
|
* or a 720KB floppy in a 1.44MB drive kind of situation!.
|
|
* So, modify the DriveParameterBlock's BPB.
|
|
*/
|
|
// copy these always because sometimes (liek 2.88) bMedia is the same on
|
|
// both when the media are different
|
|
pdpbParams->BPB = *pMediaBPB;
|
|
DebugMsg(DM_TRACE, TEXT("BPB = %x %x %x %x\n %x %x %x %x\n %x %x %x \n"),
|
|
(DWORD)pMediaBPB->cbSec, (DWORD)pMediaBPB->secPerClus, (DWORD)pMediaBPB->cSecRes,
|
|
(DWORD)pMediaBPB->cFAT,
|
|
(DWORD)pMediaBPB->cDir, (DWORD)pMediaBPB->cSec, (DWORD)pMediaBPB->bMedia,
|
|
(DWORD)pMediaBPB->secPerFAT,
|
|
(DWORD)pMediaBPB->secPerTrack, (DWORD)pMediaBPB->cHead, (DWORD)pMediaBPB->cSecHidden);
|
|
//}
|
|
|
|
// Build a DPB with TrackLayout
|
|
pNewDPB = BuildDEVPB(pdpbParams);
|
|
if (!pNewDPB)
|
|
{
|
|
LocalFree(*ppSaveDevPB);
|
|
*ppSaveDevPB = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
pNewDPB->SplFunctions = 4; /* To Set parameters */
|
|
|
|
// REVIEW: special case, may not be needed on win95
|
|
if (pMediaBPB->bMedia == MEDIA_360)
|
|
{
|
|
pNewDPB->NumCyls = 40;
|
|
pNewDPB->bMediaType = 1;
|
|
}
|
|
|
|
DriveIOCTL(nDrive, IOCTL_SET_DPB, pNewDPB);
|
|
|
|
LocalFree(pNewDPB);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* This calls IOCTL format if DOS ver >= 3.2; Else calls BIOS.
|
|
*
|
|
* Returns : 0 if no error
|
|
* > 0 if tolerable error (resuling in bad sectors);
|
|
* -1 if fatal error (Format has to be aborted);
|
|
*/
|
|
|
|
int FormatTrack(UINT nDisk, UINT nCylinder, UINT wHead)
|
|
{
|
|
#pragma pack(1)
|
|
typedef struct
|
|
{
|
|
BYTE bSpl;
|
|
WORD wHead;
|
|
WORD nCylinder;
|
|
} FORMATPARAMS;
|
|
#pragma pack()
|
|
|
|
FORMATPARAMS fp;
|
|
// int iErrCode;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("Format %d %d"), wHead, nCylinder);
|
|
|
|
fp.bSpl = 0;
|
|
fp.wHead = wHead;
|
|
fp.nCylinder = nCylinder;
|
|
|
|
if (DriveIOCTL(nDisk, IOCTL_FORMAT, &fp))
|
|
return 0; // success
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("FormatTrack failed %d"), GetLastError());
|
|
return -1; // fatial error
|
|
}
|
|
|
|
#if 0
|
|
// BUGBUG: need extended error
|
|
switch (iErrCode) {
|
|
case NOERROR:
|
|
case CRCERROR:
|
|
case SECNOTFOUND:
|
|
case GENERALERROR:
|
|
return iErrCode;
|
|
default:
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#define FORMAT_RETRY -1
|
|
#define FORMAT_ERROR 0
|
|
#define FORMAT_SUCCESS 1
|
|
|
|
|
|
|
|
int FormatAllTracks(PDISKINFO pdi, UINT nStartCylinder, UINT nStartHead)
|
|
{
|
|
int iErrCode;
|
|
BOOL bRetValue = FORMAT_SUCCESS;
|
|
|
|
LockDrive(pdi->nDestDrive, TRUE, LFS_OPT_FORMAT);
|
|
SetStatusText(pdi, IDS_FORMATTINGDEST);
|
|
|
|
pdi->pTrackLayout->SplFunctions = 5;
|
|
DriveIOCTL(pdi->nDestDrive, IOCTL_SET_DPB, pdi->pTrackLayout);
|
|
|
|
// Format tracks one by one, checking if the user has "Aborted"
|
|
// after each track is formatted; DlgProgreeProc() will set the global
|
|
// bUserAbort, if the user has aborted;
|
|
|
|
while (nStartCylinder < pdi->nCylinders)
|
|
{
|
|
/* Has the user aborted? */
|
|
if (pdi->bUserAbort)
|
|
{
|
|
bRetValue = FORMAT_ERROR;
|
|
break;
|
|
}
|
|
|
|
Retry:
|
|
/* If no message is pending, go ahead and format one track */
|
|
if ((iErrCode = FormatTrack(pdi->nDestDrive, nStartCylinder, nStartHead)))
|
|
{
|
|
/* Check if it is a fatal error */
|
|
if (iErrCode == -1)
|
|
{
|
|
pdi->dwError = IDS_ERROR_FORMAT;
|
|
LockDrive(pdi->nDestDrive, FALSE, LFS_OPT_FORMAT);
|
|
LockDrive(pdi->nDestDrive, FALSE, 0);
|
|
if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY) {
|
|
|
|
LockDrive(pdi->nDestDrive, TRUE, 0);
|
|
LockDrive(pdi->nDestDrive, TRUE, LFS_OPT_FORMAT);
|
|
pdi->dwError = 0;
|
|
goto Retry;
|
|
}
|
|
bRetValue = FORMAT_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
if (++nStartHead >= pdi->nHeads)
|
|
{
|
|
nStartHead = 0;
|
|
nStartCylinder++;
|
|
}
|
|
}
|
|
|
|
pdi->pTrackLayout->SplFunctions = 4;
|
|
DriveIOCTL(pdi->nDestDrive, IOCTL_SET_DPB, pdi->pTrackLayout);
|
|
|
|
LockDrive(pdi->nDestDrive, FALSE, 0);
|
|
return bRetValue;
|
|
}
|
|
|
|
|
|
|
|
BOOL AllocCopyDiskBuffers(PDISKINFO pdi)
|
|
{
|
|
// now, lets try to allocate a buffer for the whole disk, and
|
|
// if that fails try smaller
|
|
|
|
pdi->dwCopyBufferSize = pdi->nCylinderSize * pdi->nCylinders;
|
|
|
|
// we will try down to 8 cylinders worth, less than that means
|
|
// there will be too much disk swapping so don't bother
|
|
|
|
do {
|
|
pdi->pCopyBuffer = GlobalAlloc(GPTR, pdi->dwCopyBufferSize);
|
|
if (pdi->pCopyBuffer)
|
|
return TRUE;
|
|
|
|
// reduce request and try again
|
|
DebugMsg(DM_TRACE, TEXT("Failed alloc, trying smaller size"));
|
|
pdi->dwCopyBufferSize /= 2;
|
|
|
|
} while (pdi->dwCopyBufferSize > (8 * pdi->nCylinderSize));
|
|
|
|
DebugMsg(DM_ERROR, TEXT("Failed to alloc copy buffers"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
// BOOL bWrite; TRUE for Write, FALSE for Read
|
|
|
|
int ReadWriteCylinder(PDISKINFO pdi, LPBYTE pBuf, BOOL bWrite, UINT nCylinder)
|
|
{
|
|
UINT nHead;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("%s Cylinder %d"), bWrite ? TEXT("Write") : TEXT("Read"), nCylinder);
|
|
|
|
/* Perform the operation for all the heads for a given cylinder */
|
|
for (nHead = 0; nHead < pdi->nHeads; nHead++, pBuf += pdi->nSectorsPerTrack * pdi->nSectorSize)
|
|
{
|
|
|
|
Retry:
|
|
if (bWrite)
|
|
{
|
|
if (!ReadWriteSector(pBuf, IOCTL_WRITE,
|
|
pdi->nDestDrive, nCylinder, nHead, pdi->nSectorsPerTrack))
|
|
{
|
|
// that didn't work, try formatting
|
|
if (!pdi->bFormatTried)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("ReadWriteCylinder() write failed, trying format"));
|
|
|
|
pdi->bFormatTried = TRUE;
|
|
|
|
switch (FormatAllTracks(pdi, nCylinder, nHead)) {
|
|
case FORMAT_ERROR:
|
|
return -1; /* Failure or user cancel */
|
|
|
|
case FORMAT_SUCCESS:
|
|
break;
|
|
}
|
|
SetStatusText(pdi, IDS_WRITING);
|
|
|
|
if (!ReadWriteSector(pBuf, IOCTL_WRITE,
|
|
pdi->nDestDrive, nCylinder, nHead, pdi->nSectorsPerTrack)) {
|
|
pdi->dwError = IDS_ERROR_WRITE;
|
|
goto PromptRetry;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ReadWriteSector(pBuf, IOCTL_READ,
|
|
pdi->nSrcDrive, nCylinder, nHead, pdi->nSectorsPerTrack))
|
|
{
|
|
pdi->dwError = IDS_ERROR_READ;
|
|
|
|
PromptRetry:
|
|
if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY) {
|
|
pdi->dwError = 0;
|
|
goto Retry;
|
|
}
|
|
DebugMsg(DM_ERROR, TEXT("RWS Failed %d %d %d"), nCylinder, nHead, pdi->nSectorsPerTrack);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// BOOL bWrite TRUE for Write, FALSE for Read
|
|
//
|
|
// reads or writes as many cylinders as possible using whats in
|
|
// pCopyBuffer
|
|
//
|
|
// returns:
|
|
// the next cylinder to be read.
|
|
//
|
|
|
|
int ReadWriteMaxPossible(PDISKINFO pdi, BOOL bWrite, UINT nStartCylinder)
|
|
{
|
|
LPBYTE pBuf;
|
|
|
|
SetStatusText(pdi, bWrite ? IDS_WRITING : IDS_READING);
|
|
|
|
pdi->bFormatTried = FALSE;
|
|
|
|
// buffer needs to be multiple of cyl size
|
|
Assert((pdi->dwCopyBufferSize % pdi->nCylinderSize) == 0);
|
|
|
|
/* We will read a cylinder only if we can read the entire cylinder. */
|
|
|
|
for (pBuf = pdi->pCopyBuffer;
|
|
pBuf < (pdi->pCopyBuffer + pdi->dwCopyBufferSize);
|
|
pBuf += pdi->nCylinderSize)
|
|
{
|
|
if (pdi->bUserAbort)
|
|
return -1;
|
|
|
|
if (ReadWriteCylinder(pdi, pBuf, bWrite, nStartCylinder))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("ReadWriteCylinder failed"));
|
|
return -1;
|
|
}
|
|
|
|
nStartCylinder++;
|
|
|
|
SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_DELTAPOS, 1, 0);
|
|
|
|
/* Have we read/written all the cylinders? */
|
|
if (nStartCylinder >= pdi->nCylinders)
|
|
break;
|
|
}
|
|
return nStartCylinder;
|
|
}
|
|
|
|
|
|
void RestoreDPB(int nDisk, PDEVPB pDEVPB)
|
|
{
|
|
if (pDEVPB)
|
|
{
|
|
pDEVPB->SplFunctions = 4;
|
|
DriveIOCTL(nDisk, IOCTL_SET_DPB, pDEVPB);
|
|
LocalFree(pDEVPB);
|
|
}
|
|
}
|
|
|
|
BOOL LockDrive(int iDrive, BOOL fLock, WORD wPermissions)
|
|
{
|
|
int idCmd = fLock ? IOCTL_LOCK : IOCTL_UNLOCK;
|
|
DWORD reg[7];
|
|
BOOL bRet;
|
|
DWORD cbBytes;
|
|
HANDLE h;
|
|
|
|
reg[0] = MAKELONG(iDrive + 1, LFS_OPT_EXCLUSIVE); // make 1 based drive number and lock level
|
|
reg[1] = MAKELONG(wPermissions, wPermissions); // permissions
|
|
reg[2] = idCmd; // device specific command code
|
|
reg[3] = 0x440D; // generic read ioctl
|
|
reg[6] = 0x0001; // flags, assume error (carry)
|
|
|
|
h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
DeviceIoControl(h, 1, ®, sizeof(reg), ®, sizeof(reg), &cbBytes, 0);
|
|
CloseHandle(h);
|
|
}
|
|
|
|
bRet = !(reg[6] & 0x0001);
|
|
DebugMsg(DM_TRACE, TEXT("LockDrive bRet = %d"), bRet);
|
|
return bRet;
|
|
}
|
|
|
|
DWORD CALLBACK CopyDiskThreadProc(DISKINFO *pdi)
|
|
{
|
|
int rc = -1;
|
|
UINT nCylinder, nNextCylinder;
|
|
BPB BootBPB; /* Boot Drive's BPB (taken from Boot sector) */
|
|
#ifdef DBCS //NEC_98
|
|
// we need this for 3mode as well.
|
|
//
|
|
BPB DstBootBPB; /* Destination BPB */
|
|
#endif
|
|
DEVPB dpbSrcParams, dpbDstParams;
|
|
PDEVPB pSaveSrcParams, pSaveDstParams;
|
|
BOOL bSingleDrive = pdi->nSrcDrive == pdi->nDestDrive;
|
|
HWND hwndProgress;
|
|
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), FALSE);
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), FALSE);
|
|
|
|
DebugMsg(DM_TRACE, TEXT("CopyDisk %d -> %d"), pdi->nSrcDrive, pdi->nDestDrive);
|
|
|
|
hwndProgress = GetDlgItem(pdi->hdlg, IDD_PROBAR);
|
|
// src and dest the same, special case this.
|
|
if (!PromptInsertDisk(pdi, bSingleDrive ? MAKEINTRESOURCE(IDS_INSERTSRC) : MAKEINTRESOURCE(IDS_INSERTSRCDEST), TRUE)) {
|
|
goto Failure;
|
|
}
|
|
|
|
// lock the drive. do it here after the prompt so that the user
|
|
// has a chance to view the disk and make sure it's right
|
|
LockDrive(pdi->nSrcDrive, TRUE, 0);
|
|
if (pdi->nSrcDrive != pdi->nDestDrive)
|
|
LockDrive(pdi->nDestDrive, TRUE, 0);
|
|
|
|
|
|
/* Get the BiosParameterBlock of source drive */
|
|
if (!GetBPB(pdi->nSrcDrive, &dpbSrcParams, 1))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Bad source disk"));
|
|
goto BadSourceDisk;
|
|
}
|
|
|
|
/* Get the BiosParameterBlock of the Source Diskette */
|
|
if (!GetBootBPB(pdi->nSrcDrive, &BootBPB))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Bad source disk boot sector"));
|
|
BadSourceDisk:
|
|
pdi->dwError = IDS_SRCDISKBAD;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISKBAD), NULL, MB_ICONHAND | MB_OK);
|
|
goto Failure;
|
|
}
|
|
|
|
#if defined(DBCS) && !defined(NEC_98)
|
|
// Reject 1024 b/sec in case 3 mode FDD.
|
|
|
|
if (BootBPB.cbSec == 1024)
|
|
{
|
|
pdi->dwError = IDS_SRCDISK1024;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISK1024), NULL, MB_ICONERROR | MB_OK);
|
|
goto Failure;
|
|
}
|
|
#endif
|
|
|
|
if (dpbSrcParams.devAtt & DEVPB_DEVATT_DMF) {
|
|
pdi->dwError = IDS_SRCDISKDMF;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_SRCDISKDMF), NULL, MB_ICONERROR | MB_OK);
|
|
goto Failure;
|
|
}
|
|
|
|
/* Get the BPB and DPB for the Destination drive also; */
|
|
if (!bSingleDrive)
|
|
{
|
|
#ifdef NEC_98
|
|
/* Since NEC_98 3.5" FD drive can handle both 1.25MB/1.21MB media and 1.44
|
|
MB media, we should check actaul medias for both src and dest are
|
|
completely same. */
|
|
if (!GetBootBPB(pdi->nDestDrive, &DstBootBPB))
|
|
#else
|
|
if (!GetBPB(pdi->nDestDrive, &dpbDstParams, 0))
|
|
#endif
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Bad dest disk"));
|
|
pdi->dwError = IDS_DSTDISKBAD;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK);
|
|
goto Failure;
|
|
}
|
|
#ifdef NEC_98
|
|
/* Set ACTUAL BPB, not device default BPB due to above reason */
|
|
dpbSrcParams.BPB = BootBPB;
|
|
dpbDstParams.BPB = DstBootBPB;
|
|
#endif
|
|
|
|
/* Compare BPB of source and Dest to see if they are compatible */
|
|
if (!(CheckBPBCompatibility(&dpbSrcParams, &dpbDstParams)))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("disks don't match"));
|
|
pdi->dwError = IDS_COPYSRCDESTINCOMPAT;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK);
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
if (!ModifyDeviceParams(pdi->nSrcDrive, &dpbSrcParams, &pSaveSrcParams, &dpbSrcParams.BPB, &BootBPB))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("can't set device params for source"));
|
|
goto Failure;
|
|
}
|
|
|
|
if (!bSingleDrive)
|
|
{
|
|
if (!ModifyDeviceParams(pdi->nDestDrive, &dpbDstParams, &pSaveDstParams, &dpbDstParams.BPB, &BootBPB))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("can't set device params for dest"));
|
|
RestoreDPB(pdi->nSrcDrive, pSaveSrcParams);
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
pdi->nCylinderSize = BootBPB.secPerTrack * BootBPB.cbSec * BootBPB.cHead;
|
|
pdi->nCylinders = BootBPB.cSec / (BootBPB.secPerTrack * BootBPB.cHead);
|
|
pdi->nHeads = BootBPB.cHead;
|
|
pdi->nSectorsPerTrack = BootBPB.secPerTrack;
|
|
pdi->nSectorSize = BootBPB.cbSec;
|
|
|
|
if (!pdi->nCylinderSize || !pdi->nCylinders) {
|
|
pdi->dwError = IDS_ERROR_GENERAL;
|
|
ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
|
|
goto Failure;
|
|
}
|
|
|
|
PostMessage(hwndProgress, PBM_SETRANGE, 0, MAKELONG(0, (WORD)pdi->nCylinders * 2));
|
|
|
|
// In case we need to format the destination diskette, we need to know the
|
|
// track layout; So, build a DPB with the required track layout
|
|
|
|
pdi->pTrackLayout = BuildDEVPB(&dpbSrcParams);
|
|
if (!pdi->pTrackLayout)
|
|
goto Failure0;
|
|
|
|
/* The following is required to format a 360KB floppy in a 1.2MB
|
|
* drive of NCR PC916 machine; We do formatting, if the destination
|
|
* floppy is an unformatted one;
|
|
* Fix for Bug #6894 --01-10-90-- SANKAR --
|
|
*/
|
|
if (pdi->pTrackLayout->BPB.bMedia == MEDIA_360)
|
|
{
|
|
pdi->pTrackLayout->NumCyls = 40;
|
|
pdi->pTrackLayout->bMediaType = 1;
|
|
}
|
|
|
|
/* We wish we could do the following allocation at the begining of this
|
|
* function, but we can not do so, because we need di
|
|
* and we just got it;
|
|
*/
|
|
if (!AllocCopyDiskBuffers(pdi))
|
|
{
|
|
// ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_REASONS+DE_INSMEM), NULL, MB_ICONHAND | MB_OK);
|
|
goto Failure0;
|
|
}
|
|
|
|
for (nCylinder = 0; nCylinder < pdi->nCylinders; nCylinder = nNextCylinder)
|
|
{
|
|
// Do not prompt for the first time, because the Source diskette is
|
|
// already in the drive.
|
|
|
|
if (bSingleDrive && (nCylinder > 0))
|
|
{
|
|
LockDrive(pdi->nSrcDrive, FALSE, 0);
|
|
if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(IDS_INSERTSRC), FALSE)) {
|
|
pdi->bUserAbort = TRUE;
|
|
goto Failure0;
|
|
}
|
|
LockDrive(pdi->nSrcDrive, TRUE, 0);
|
|
}
|
|
|
|
// Read in the current cylinders
|
|
|
|
rc = ReadWriteMaxPossible(pdi, FALSE, nCylinder);
|
|
if (rc < 0)
|
|
break;
|
|
else
|
|
nNextCylinder = rc;
|
|
|
|
// If this is a single drive system, ask the user to insert
|
|
// the destination diskette.
|
|
|
|
if (bSingleDrive)
|
|
{
|
|
LockDrive(pdi->nSrcDrive, FALSE, 0);
|
|
if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(IDS_INSERTDEST), FALSE)) {
|
|
pdi->bUserAbort = TRUE;
|
|
goto Failure0;
|
|
}
|
|
LockDrive(pdi->nSrcDrive, TRUE, 0);
|
|
#ifdef DBCS // NEC_98
|
|
// we need this for 3mode as well.
|
|
//
|
|
/* Get destination media BPB */
|
|
if (!GetBootBPB(pdi->nSrcDrive, &DstBootBPB))
|
|
{
|
|
DebugMsg(DM_ERROR, "Bad dest disk");
|
|
pdi->dwError = IDS_DSTDISKBAD;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
dpbDstParams.BPB = DstBootBPB;
|
|
/* Compare BPB of source and Dest to see if they are compatible */
|
|
if (!(CheckBPBCompatibility(&dpbSrcParams, &dpbDstParams)))
|
|
{
|
|
DebugMsg(DM_ERROR, "disks don't match");
|
|
pdi->dwError = IDS_COPYSRCDESTINCOMPAT;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Write out the current cylinders
|
|
rc = ReadWriteMaxPossible(pdi, TRUE, nCylinder);
|
|
if (rc < 0)
|
|
break;
|
|
}
|
|
|
|
if (pdi->pCopyBuffer)
|
|
{
|
|
GlobalFree(pdi->pCopyBuffer);
|
|
pdi->pCopyBuffer = NULL;
|
|
}
|
|
|
|
Failure0:
|
|
|
|
// Reset the Source drive parameters to the same as old
|
|
RestoreDPB(pdi->nSrcDrive, pSaveSrcParams);
|
|
|
|
if (!bSingleDrive)
|
|
RestoreDPB(pdi->nDestDrive, pSaveDstParams);
|
|
|
|
if (pdi->pTrackLayout)
|
|
{
|
|
LocalFree(pdi->pTrackLayout);
|
|
pdi->pTrackLayout = NULL;
|
|
}
|
|
|
|
Failure:
|
|
|
|
PostMessage(pdi->hdlg, WM_DONE_WITH_FORMAT, 0, 0);
|
|
|
|
return rc;
|
|
}
|
|
#else
|
|
|
|
typedef struct _fmifs {
|
|
HANDLE hDll;
|
|
PFMIFS_DISKCOPY_ROUTINE DiskCopy;
|
|
} FMIFS;
|
|
typedef FMIFS *PFMIFS;
|
|
|
|
|
|
BOOL LoadFMIFS(PFMIFS pFMIFS)
|
|
{
|
|
//
|
|
// Load the FMIFS DLL and query for the entry points we need
|
|
//
|
|
|
|
pFMIFS->hDll = LoadLibrary(TEXT("FMIFS.DLL"));
|
|
|
|
if (NULL == pFMIFS->hDll)
|
|
return FALSE;
|
|
|
|
pFMIFS->DiskCopy = (PFMIFS_DISKCOPY_ROUTINE)GetProcAddress(pFMIFS->hDll,
|
|
"DiskCopy");
|
|
|
|
if (NULL == pFMIFS->DiskCopy)
|
|
{
|
|
FreeLibrary(pFMIFS->hDll);
|
|
pFMIFS->hDll = (HANDLE)0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void UnloadFMIFS(PFMIFS pFMIFS)
|
|
{
|
|
FreeLibrary(pFMIFS->hDll);
|
|
pFMIFS->hDll = NULL;
|
|
pFMIFS->DiskCopy = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Thread-Local Storage index for our DISKINFO structure pointer
|
|
//
|
|
|
|
static DWORD g_iTLSDiskInfo = 0;
|
|
static LONG g_cTLSDiskInfo = 0; // Usage count
|
|
|
|
__inline void UnstuffDiskInfoPtr()
|
|
{
|
|
if (InterlockedDecrement(&g_cTLSDiskInfo) == 0)
|
|
TlsFree(g_iTLSDiskInfo);
|
|
}
|
|
|
|
BOOL StuffDiskInfoPtr(PDISKINFO pDiskInfo)
|
|
{
|
|
//
|
|
// Allocate an index slot for our thread-local DISKINFO pointer, if one
|
|
// doesn't already exist, then stuff our DISKINFO ptr at that index.
|
|
//
|
|
|
|
if (0 == g_iTLSDiskInfo)
|
|
{
|
|
if (0xFFFFFFFF == (g_iTLSDiskInfo = TlsAlloc()))
|
|
{
|
|
return FALSE;
|
|
}
|
|
g_cTLSDiskInfo = 0;
|
|
}
|
|
|
|
InterlockedIncrement(&g_cTLSDiskInfo);
|
|
|
|
if (!TlsSetValue(g_iTLSDiskInfo, (LPVOID) pDiskInfo))
|
|
{
|
|
UnstuffDiskInfoPtr();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
__inline PDISKINFO GetDiskInfoPtr()
|
|
{
|
|
return TlsGetValue(g_iTLSDiskInfo);
|
|
}
|
|
|
|
|
|
BOOLEAN CopyDiskCallback( FMIFS_PACKET_TYPE PacketType, DWORD PacketLength, PVOID PacketData)
|
|
{
|
|
PDISKINFO pdi = GetDiskInfoPtr();
|
|
int iDisk;
|
|
|
|
// Quit if told to do so..
|
|
if (pdi->bUserAbort)
|
|
return FALSE;
|
|
|
|
switch (PacketType) {
|
|
case FmIfsPercentCompleted:
|
|
{
|
|
DWORD dwPercent = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)
|
|
PacketData)->PercentCompleted;
|
|
|
|
//
|
|
// Hokey method of determining "writing"
|
|
//
|
|
if (dwPercent > 50 && !pdi->bNotifiedWriting)
|
|
{
|
|
pdi->bNotifiedWriting = TRUE;
|
|
SetStatusText(pdi, IDS_WRITING);
|
|
}
|
|
|
|
SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, dwPercent,0);
|
|
break;
|
|
}
|
|
case FmIfsInsertDisk:
|
|
|
|
switch(((PFMIFS_INSERT_DISK_INFORMATION)PacketData)->DiskType) {
|
|
case DISK_TYPE_SOURCE:
|
|
case DISK_TYPE_GENERIC:
|
|
iDisk = IDS_INSERTSRC;
|
|
break;
|
|
|
|
case DISK_TYPE_TARGET:
|
|
iDisk = IDS_INSERTDEST;
|
|
break;
|
|
case DISK_TYPE_SOURCE_AND_TARGET:
|
|
iDisk = IDS_INSERTSRCDEST;
|
|
break;
|
|
}
|
|
if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(iDisk), FALSE)) {
|
|
pdi->bUserAbort = TRUE;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case FmIfsFormattingDestination:
|
|
pdi->bNotifiedWriting = FALSE; // Reset so we get Writing later
|
|
SetStatusText(pdi, IDS_FORMATTINGDEST);
|
|
break;
|
|
|
|
case FmIfsIncompatibleFileSystem:
|
|
case FmIfsIncompatibleMedia:
|
|
pdi->dwError = IDS_COPYSRCDESTINCOMPAT;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_COPYSRCDESTINCOMPAT), NULL, MB_ICONHAND | MB_OK);
|
|
return FALSE;
|
|
|
|
case FmIfsMediaWriteProtected:
|
|
pdi->dwError = IDS_DSTDISKBAD;
|
|
ShellMessageBox(g_hinst, pdi->hdlg, MAKEINTRESOURCE(IDS_DSTDISKBAD), NULL, MB_ICONHAND | MB_OK);
|
|
return FALSE;
|
|
|
|
case FmIfsCantLock:
|
|
// BUGBUG - BobDay - We should do something for this!
|
|
pdi->dwError = IDS_ERROR_GENERAL;
|
|
ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
|
|
case FmIfsAccessDenied:
|
|
case FmIfsBadLabel:
|
|
case FmIfsCantQuickFormat:
|
|
pdi->dwError = IDS_ERROR_GENERAL;
|
|
ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
|
|
return FALSE;
|
|
|
|
case FmIfsIoError:
|
|
switch(((PFMIFS_IO_ERROR_INFORMATION)PacketData)->DiskType) {
|
|
case DISK_TYPE_SOURCE:
|
|
pdi->dwError = IDS_SRCDISKBAD;
|
|
break;
|
|
case DISK_TYPE_TARGET:
|
|
pdi->dwError = IDS_DSTDISKBAD;
|
|
break;
|
|
default:
|
|
// BUGBUG - BobDay - We should never get this!!
|
|
pdi->dwError = IDS_ERROR_GENERAL;
|
|
break;
|
|
}
|
|
|
|
if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY)
|
|
{
|
|
pdi->dwError = 0;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case FmIfsFinished:
|
|
if (((PFMIFS_FINISHED_INFORMATION)PacketData)->Success)
|
|
{
|
|
pdi->dwError = 0;
|
|
}
|
|
else
|
|
{
|
|
pdi->dwError = IDS_ERROR_GENERAL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD CALLBACK CopyDiskThreadProc(DISKINFO *pdi)
|
|
{
|
|
FMIFS fmifs;
|
|
HWND hwndProgress = GetDlgItem(pdi->hdlg, IDD_PROBAR);
|
|
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), FALSE);
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), FALSE);
|
|
|
|
PostMessage(hwndProgress, PBM_SETRANGE, 0, MAKELONG(0, 100));
|
|
|
|
pdi->bFormatTried = FALSE;
|
|
pdi->bNotifiedWriting = FALSE;
|
|
pdi->dwError = 0;
|
|
|
|
if (StuffDiskInfoPtr(pdi) && LoadFMIFS(&fmifs))
|
|
{
|
|
TCHAR szSource[3];
|
|
TCHAR szDestination[3];
|
|
|
|
//
|
|
// Now copy the disk
|
|
//
|
|
szSource[0] = TEXT('A') + pdi->nSrcDrive;
|
|
szSource[1] = TEXT(':');
|
|
szSource[2] = 0;
|
|
|
|
szDestination[0] = TEXT('A') + pdi->nDestDrive;
|
|
szDestination[1] = TEXT(':');
|
|
szDestination[2] = 0;
|
|
|
|
SetStatusText(pdi, IDS_READING);
|
|
|
|
fmifs.DiskCopy(szSource, szDestination, FALSE, CopyDiskCallback);
|
|
|
|
UnstuffDiskInfoPtr();
|
|
UnloadFMIFS(&fmifs);
|
|
}
|
|
|
|
PostMessage(pdi->hdlg, WM_DONE_WITH_FORMAT, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int ErrorMessageBox(DISKINFO* pdi, UINT uFlags)
|
|
{
|
|
if (!pdi->bUserAbort && pdi->dwError) {
|
|
TCHAR szTemp[1024];
|
|
DWORD dwLastError = GetLastError();
|
|
|
|
DebugMsg(DM_TRACE, TEXT("ERROR %d %d"), pdi->dwError, dwLastError);
|
|
if (dwLastError) {
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, szTemp, sizeof(szTemp), NULL);
|
|
} else {
|
|
LoadString(g_hinst, (int)pdi->dwError, szTemp, sizeof(szTemp));
|
|
}
|
|
|
|
// if the user didn't abort and it didn't complete normally, post an error box
|
|
return ShellMessageBox(g_hinst, pdi->hdlg, szTemp, NULL, uFlags);
|
|
} else
|
|
return -1;
|
|
}
|
|
|
|
void SetStatusText(DISKINFO* pdi, int id)
|
|
{
|
|
TCHAR szMsg[128];
|
|
LoadString(g_hinst, id, szMsg, sizeof(szMsg));
|
|
SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, (LPARAM)szMsg);
|
|
}
|
|
|
|
BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck)
|
|
{
|
|
if (fAutoCheck)
|
|
goto AutoCheckBegin;
|
|
|
|
for (;;) {
|
|
DWORD dwLastErrorSrc = 0;
|
|
DWORD dwLastErrorDest = 0 ;
|
|
TCHAR szPath[4];
|
|
|
|
if (ShellMessageBox(g_hinst, pdi->hdlg, lpsz, NULL, MB_OKCANCEL | MB_ICONINFORMATION) != IDOK) {
|
|
pdi->bUserAbort = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
AutoCheckBegin:
|
|
szPath[0] = TEXT('A') + pdi->nSrcDrive;
|
|
szPath[1] = TEXT(':');
|
|
szPath[2] = TEXT('\\');
|
|
szPath[3] = 0;
|
|
|
|
// make sure both disks are in
|
|
if (GetFileAttributes(szPath) == (UINT)-1)
|
|
dwLastErrorDest = GetLastError();
|
|
|
|
if (pdi->nDestDrive != pdi->nSrcDrive) {
|
|
szPath[0] = TEXT('A') + pdi->nDestDrive;
|
|
if (GetFileAttributes(szPath) == (UINT)-1)
|
|
dwLastErrorDest = GetLastError();
|
|
}
|
|
|
|
if (dwLastErrorDest != ERROR_NOT_READY &&
|
|
dwLastErrorSrc != ERROR_NOT_READY)
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HICON GetDriveInfo(int nDrive, LPTSTR pszName)
|
|
{
|
|
SHFILEINFO shfi;
|
|
TCHAR szRoot[4];
|
|
|
|
PathBuildRoot(szRoot, nDrive);
|
|
|
|
if (SHGetFileInfo(szRoot, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi),
|
|
SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME)) // | SHGFI_USEFILEATTRIBUTES
|
|
{
|
|
lstrcpy(pszName, shfi.szDisplayName);
|
|
return shfi.hIcon;
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(pszName, szRoot);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int AddDriveToListView(HWND hwndLV, int nDrive, int nDefaultDrive)
|
|
{
|
|
TCHAR szDriveName[64];
|
|
LV_ITEM item;
|
|
HICON hicon = GetDriveInfo(nDrive, szDriveName);
|
|
HIMAGELIST himlSmall = ListView_GetImageList(hwndLV, LVSIL_SMALL);
|
|
|
|
Assert(himlSmall);
|
|
if (hicon)
|
|
{
|
|
item.iImage = ImageList_AddIcon(himlSmall, hicon);
|
|
DestroyIcon(hicon);
|
|
}
|
|
else
|
|
item.iImage = 0;
|
|
|
|
item.mask = nDrive == nDefaultDrive ?
|
|
LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE :
|
|
LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
|
|
|
|
item.stateMask = item.state = LVIS_SELECTED | LVIS_FOCUSED;
|
|
item.iItem = 26; // add at end
|
|
item.iSubItem = 0;
|
|
|
|
item.pszText = szDriveName;
|
|
item.lParam = (LPARAM)nDrive;
|
|
|
|
return ListView_InsertItem(hwndLV, &item);
|
|
}
|
|
|
|
int GetSelectedDrive(HWND hwndLV)
|
|
{
|
|
LV_ITEM item;
|
|
item.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
|
|
if (item.iItem >= 0)
|
|
{
|
|
item.mask = LVIF_PARAM;
|
|
item.iSubItem = 0;
|
|
ListView_GetItem(hwndLV, &item);
|
|
return (int)item.lParam;
|
|
}
|
|
|
|
// implicitly selected the 0th item
|
|
ListView_SetItemState(hwndLV, 0, LVIS_SELECTED, LVIS_SELECTED);
|
|
return 0;
|
|
}
|
|
|
|
void InitSingleColListView(HWND hwndLV)
|
|
{
|
|
LV_COLUMN col = {LVCF_FMT | LVCF_WIDTH, LVCFMT_LEFT};
|
|
RECT rc;
|
|
|
|
GetClientRect(hwndLV, &rc);
|
|
col.cx = rc.right;
|
|
// - GetSystemMetrics(SM_CXVSCROLL)
|
|
// - GetSystemMetrics(SM_CXSMICON)
|
|
// - 2 * GetSystemMetrics(SM_CXEDGE);
|
|
ListView_InsertColumn(hwndLV, 0, &col);
|
|
}
|
|
|
|
#define g_cxSmIcon GetSystemMetrics(SM_CXSMICON)
|
|
|
|
void CopyDiskInitDlg(HWND hDlg, DISKINFO *pdi)
|
|
{
|
|
int iDrive;
|
|
HWND hwndFrom = GetDlgItem(hDlg, IDD_FROM);
|
|
HWND hwndTo = GetDlgItem(hDlg, IDD_TO);
|
|
HIMAGELIST himl;
|
|
|
|
SetWindowLong(hDlg, DWL_USER, (UINT)pdi);
|
|
|
|
SendMessage(hDlg, WM_SETICON, 0, (LPARAM)LoadImage(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY), IMAGE_ICON, 16, 16, 0));
|
|
SendMessage(hDlg, WM_SETICON, 1, (LPARAM)LoadIcon(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY)));
|
|
|
|
pdi->hdlg = hDlg;
|
|
|
|
InitSingleColListView(hwndFrom);
|
|
InitSingleColListView(hwndTo);
|
|
|
|
himl = ImageList_Create(g_cxSmIcon, g_cxSmIcon, ILC_MASK, 1, 4);
|
|
if (himl)
|
|
{
|
|
// NOTE: only one of these is not marked LVS_SHAREIMAGELIST
|
|
// so it will only be destroyed once
|
|
|
|
ListView_SetImageList(hwndFrom, himl, LVSIL_SMALL);
|
|
ListView_SetImageList(hwndTo, himl, LVSIL_SMALL);
|
|
}
|
|
|
|
for (iDrive = 0; iDrive < 26; iDrive++)
|
|
{
|
|
if (IsRemovableDrive(iDrive) && !IsCDRomDrive(iDrive))
|
|
{
|
|
AddDriveToListView(hwndFrom, iDrive, pdi->nSrcDrive);
|
|
AddDriveToListView(hwndTo, iDrive, pdi->nDestDrive);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DWORD _inline WaitForThreadDeath(HWND hThread)
|
|
{
|
|
MSG msg;
|
|
if (hThread) {
|
|
while(TRUE) {
|
|
DWORD result = MsgWaitForMultipleObjects(1, &hThread, FALSE, 5000, QS_SENDMESSAGE);
|
|
switch (result) {
|
|
case WAIT_OBJECT_0:
|
|
case WAIT_FAILED:
|
|
return result;
|
|
|
|
case WAIT_TIMEOUT:
|
|
TerminateThread(hThread, (DWORD)-1);
|
|
return result;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetCancelButtonText(HWND hDlg, int id)
|
|
{
|
|
TCHAR szText[80];
|
|
LoadString(g_hinst, id, szText, sizeof(szText));
|
|
SetDlgItemText(hDlg, IDCANCEL, szText);
|
|
}
|
|
|
|
void DoneWithFormat(DISKINFO* pdi)
|
|
{
|
|
int id;
|
|
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), TRUE);
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), TRUE);
|
|
|
|
#ifndef WINNT
|
|
// unlock the drives
|
|
LockDrive(pdi->nSrcDrive, FALSE, 0 );
|
|
if (pdi->nSrcDrive != pdi->nDestDrive)
|
|
LockDrive(pdi->nDestDrive, FALSE, 0);
|
|
#endif
|
|
|
|
SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, 0, 0);
|
|
EnableWindow(GetDlgItem(pdi->hdlg, IDOK), TRUE);
|
|
|
|
CloseHandle(pdi->hThread);
|
|
SetCancelButtonText(pdi->hdlg, IDS_CLOSE);
|
|
pdi->hThread = NULL;
|
|
|
|
if (pdi->bUserAbort) {
|
|
id = IDS_COPYABORTED;
|
|
} else {
|
|
switch (pdi->dwError) {
|
|
case 0:
|
|
id = IDS_COPYCOMPLETED;
|
|
break;
|
|
|
|
default:
|
|
id = IDS_COPYFAILED;
|
|
break;
|
|
}
|
|
}
|
|
SetStatusText(pdi, id);
|
|
SetCancelButtonText(pdi->hdlg, IDS_CLOSE);
|
|
|
|
// reset variables
|
|
pdi->dwError = 0;
|
|
pdi->bUserAbort = 0;
|
|
}
|
|
|
|
|
|
#pragma data_seg(".text")
|
|
const static DWORD aCopyDiskHelpIDs[] = { // Context Help IDs
|
|
IDOK, IDH_DISKCOPY_START,
|
|
IDD_FROM, IDH_DISKCOPY_FROM,
|
|
IDD_TO, IDH_DISKCOPY_TO,
|
|
IDD_STATUS, NO_HELP,
|
|
IDD_PROBAR, NO_HELP,
|
|
|
|
0, 0
|
|
};
|
|
#pragma data_seg()
|
|
|
|
BOOL CALLBACK CopyDiskDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DISKINFO *pdi = (DISKINFO *)GetWindowLong(hDlg, DWL_USER);
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
CopyDiskInitDlg(hDlg, (DISKINFO *)lParam);
|
|
break;
|
|
|
|
case WM_DONE_WITH_FORMAT:
|
|
DoneWithFormat(pdi);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (DWORD)(LPTSTR) aCopyDiskHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(DWORD)(LPVOID) aCopyDiskHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
case IDCANCEL:
|
|
// if there's az hThread that means we're in copy mode, abort
|
|
// from that, otherwise, it means quit the dialog completely
|
|
if (pdi->hThread)
|
|
{
|
|
pdi->bUserAbort = TRUE;
|
|
|
|
// do a Msgwaitformultiple so that we don't
|
|
// get blocked with them sending us a message
|
|
if (WaitForThreadDeath(pdi->hThread) == WAIT_TIMEOUT)
|
|
DoneWithFormat(pdi);
|
|
CloseHandle(pdi->hThread);
|
|
pdi->hThread = NULL;
|
|
}
|
|
else
|
|
EndDialog(hDlg, IDCANCEL);
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
DWORD idThread;
|
|
|
|
SetLastError(0);
|
|
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
|
|
|
|
// set cancel button to "Cancel"
|
|
SetCancelButtonText(hDlg, IDS_CANCEL);
|
|
|
|
pdi->nSrcDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_FROM));
|
|
pdi->nDestDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_TO));
|
|
|
|
pdi->bUserAbort = FALSE;
|
|
|
|
SendDlgItemMessage(hDlg, IDD_PROBAR, PBM_SETPOS, 0, 0);
|
|
SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, 0);
|
|
|
|
Assert(pdi->hThread == NULL);
|
|
|
|
pdi->hThread = CreateThread(NULL, 0, CopyDiskThreadProc, pdi, 0, &idThread);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int SHCopyDisk(HWND hwnd, int nSrcDrive, int nDestDrive, DWORD dwFlags)
|
|
{
|
|
DISKINFO di;
|
|
|
|
memset(&di, 0, sizeof(di));
|
|
|
|
di.nSrcDrive = nSrcDrive;
|
|
di.nDestDrive = nDestDrive;
|
|
|
|
return DialogBoxParam(g_hinst, MAKEINTRESOURCE(DLG_DISKCOPYPROGRESS), hwnd, CopyDiskDlgProc, (LPARAM)&di);
|
|
}
|