Improve filename handling when adding files
Most of this change is a conversion of the old FileDetails struct into a new LocalFileDetails class. The new class keeps the members private, and keeps the Unicode and MOR representations of the string separate. The NuFX and DiskImg libraries don't support UTF-16 filenames, so we stil can't add files with non-CP-1252 filenames, but we're a step closer. Also, update NufxLib with a couple of fixes from the main project. Also, fix handling of "%00" when adding files. Also, mark most of the A2FileDOS fields private. Not sure why they weren't.
This commit is contained in:
parent
d94b707489
commit
b79498da50
|
@ -375,7 +375,7 @@ bail:
|
||||||
|
|
||||||
/*static*/ CString AcuArchive::AppInit(void)
|
/*static*/ CString AcuArchive::AppInit(void)
|
||||||
{
|
{
|
||||||
return "";
|
return L"";
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename,
|
GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename,
|
||||||
|
@ -526,11 +526,11 @@ int AcuArchive::ReadMasterHeader(int* pNumEntries)
|
||||||
header.unknown1 != 1 ||
|
header.unknown1 != 1 ||
|
||||||
strcmp((char*) header.fZink, "fZink") != 0)
|
strcmp((char*) header.fZink, "fZink") != 0)
|
||||||
{
|
{
|
||||||
LOGI("Not an ACU archive");
|
LOGW("Not an ACU archive");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("Looks like an ACU archive with %d entries", header.fileCount);
|
LOGD("Looks like an ACU archive with %d entries", header.fileCount);
|
||||||
|
|
||||||
*pNumEntries = header.fileCount;
|
*pNumEntries = header.fileCount;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -162,7 +162,7 @@ private:
|
||||||
}
|
}
|
||||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
||||||
{ ASSERT(false); }
|
{ ASSERT(false); }
|
||||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
||||||
{ ASSERT(false); return "!"; }
|
{ ASSERT(false); return "!"; }
|
||||||
virtual void XferAbort(CWnd* pMsgWnd) override
|
virtual void XferAbort(CWnd* pMsgWnd) override
|
||||||
|
@ -172,7 +172,7 @@ private:
|
||||||
|
|
||||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; }
|
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; }
|
||||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails) override
|
LocalFileDetails* pDetails) override
|
||||||
{ ASSERT(false); return kNuErrGeneric; }
|
{ ASSERT(false); return kNuErrGeneric; }
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -2138,21 +2138,21 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||||
pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly());
|
pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ bool MainWindow::SaveToArchive(GenericArchive::FileDetails* pDetails,
|
/*static*/ bool MainWindow::SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
|
||||||
const unsigned char* dataBufIn, long dataLen,
|
const uint8_t* dataBufIn, long dataLen,
|
||||||
const unsigned char* rsrcBufIn, long rsrcLen,
|
const uint8_t* rsrcBufIn, long rsrcLen,
|
||||||
CString& errMsg, CWnd* pDialog)
|
CString* pErrMsg, CWnd* pDialog)
|
||||||
{
|
{
|
||||||
MainWindow* pMain = GET_MAIN_WINDOW();
|
MainWindow* pMain = GET_MAIN_WINDOW();
|
||||||
GenericArchive* pArchive = pMain->GetOpenArchive();
|
GenericArchive* pArchive = pMain->GetOpenArchive();
|
||||||
DiskImgLib::A2File* pTargetSubdir = NULL;
|
DiskImgLib::A2File* pTargetSubdir = NULL;
|
||||||
XferFileOptions xferOpts;
|
XferFileOptions xferOpts;
|
||||||
CString storagePrefix;
|
CString storagePrefix;
|
||||||
unsigned char* dataBuf = NULL;
|
uint8_t* dataBuf = NULL;
|
||||||
unsigned char* rsrcBuf = NULL;
|
uint8_t* rsrcBuf = NULL;
|
||||||
|
|
||||||
ASSERT(pArchive != NULL);
|
ASSERT(pArchive != NULL);
|
||||||
ASSERT(errMsg.IsEmpty());
|
ASSERT(pErrMsg->IsEmpty());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a copy of the data for XferFile.
|
* Make a copy of the data for XferFile.
|
||||||
|
@ -2163,7 +2163,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||||
else
|
else
|
||||||
dataBuf = new unsigned char[dataLen];
|
dataBuf = new unsigned char[dataLen];
|
||||||
if (dataBuf == NULL) {
|
if (dataBuf == NULL) {
|
||||||
errMsg.Format(L"Unable to allocate %ld bytes", dataLen);
|
pErrMsg->Format(L"Unable to allocate %ld bytes", dataLen);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
memcpy(dataBuf, dataBufIn, dataLen);
|
memcpy(dataBuf, dataBufIn, dataLen);
|
||||||
|
@ -2187,7 +2187,7 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||||
// Always use ':' separator for SHK; this is a matter of
|
// Always use ':' separator for SHK; this is a matter of
|
||||||
// convenience, so they can specify a full path.
|
// convenience, so they can specify a full path.
|
||||||
//details.storageName.Replace(':', '_');
|
//details.storageName.Replace(':', '_');
|
||||||
pDetails->fileSysInfo = ':';
|
pDetails->SetFssep(':');
|
||||||
}
|
}
|
||||||
if (pTargetSubdir != NULL) {
|
if (pTargetSubdir != NULL) {
|
||||||
storagePrefix = pTargetSubdir->GetPathName();
|
storagePrefix = pTargetSubdir->GetPathName();
|
||||||
|
@ -2195,13 +2195,13 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||||
}
|
}
|
||||||
if (!storagePrefix.IsEmpty()) {
|
if (!storagePrefix.IsEmpty()) {
|
||||||
CString tmpStr, tmpFileName;
|
CString tmpStr, tmpFileName;
|
||||||
tmpFileName = pDetails->storageName;
|
tmpFileName = pDetails->GetStrippedLocalPathName();
|
||||||
tmpFileName.Replace(':', '_'); // strip any ':'s in the name
|
tmpFileName.Replace(':', '_'); // strip any ':'s in the name
|
||||||
pDetails->fileSysInfo = ':';
|
pDetails->SetFssep(':');
|
||||||
tmpStr = storagePrefix;
|
tmpStr = storagePrefix;
|
||||||
tmpStr += ':';
|
tmpStr += ':';
|
||||||
tmpStr += tmpFileName;
|
tmpStr += tmpFileName;
|
||||||
pDetails->storageName = tmpStr;
|
pDetails->SetStrippedLocalPathName(tmpStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2211,18 +2211,18 @@ void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI)
|
||||||
*/
|
*/
|
||||||
pArchive->XferPrepare(&xferOpts);
|
pArchive->XferPrepare(&xferOpts);
|
||||||
|
|
||||||
errMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen,
|
*pErrMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen,
|
||||||
&rsrcBuf, rsrcLen);
|
&rsrcBuf, rsrcLen);
|
||||||
delete[] dataBuf;
|
delete[] dataBuf;
|
||||||
delete[] rsrcBuf;
|
delete[] rsrcBuf;
|
||||||
|
|
||||||
if (errMsg.IsEmpty())
|
if (pErrMsg->IsEmpty())
|
||||||
pArchive->XferFinish(pDialog);
|
pArchive->XferFinish(pDialog);
|
||||||
else
|
else
|
||||||
pArchive->XferAbort(pDialog);
|
pArchive->XferAbort(pDialog);
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
return (errMsg.IsEmpty() != 0);
|
return (pErrMsg->IsEmpty() != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ private:
|
||||||
}
|
}
|
||||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
|
||||||
{ ASSERT(false); }
|
{ ASSERT(false); }
|
||||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
|
||||||
{ ASSERT(false); return "!"; }
|
{ ASSERT(false); return "!"; }
|
||||||
virtual void XferAbort(CWnd* pMsgWnd) override
|
virtual void XferAbort(CWnd* pMsgWnd) override
|
||||||
|
@ -159,7 +159,7 @@ private:
|
||||||
|
|
||||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; }
|
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; }
|
||||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails) override
|
LocalFileDetails* pDetails) override
|
||||||
{ ASSERT(false); return kNuErrGeneric; }
|
{ ASSERT(false); return kNuErrGeneric; }
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -616,25 +616,27 @@ void ImportBASDialog::OnOK(void)
|
||||||
/*
|
/*
|
||||||
* Write the file to the currently-open archive.
|
* Write the file to the currently-open archive.
|
||||||
*/
|
*/
|
||||||
GenericArchive::FileDetails details;
|
GenericArchive::LocalFileDetails details;
|
||||||
|
|
||||||
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
|
||||||
details.origName = L"Imported BASIC";
|
details.SetLocalPathName(L"Imported BASIC");
|
||||||
details.storageName = fileName;
|
details.SetStrippedLocalPathName(fileName);
|
||||||
details.access = 0xe3; // unlocked, backup bit set
|
details.SetAccess(0xe3); // unlocked, backup bit set
|
||||||
details.fileType = kFileTypeBAS;
|
details.SetFileType(kFileTypeBAS);
|
||||||
details.extraType = 0x0801;
|
details.SetExtraType(0x0801);
|
||||||
details.storageType = DiskFS::kStorageSeedling;
|
details.SetStorageType(DiskFS::kStorageSeedling);
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen);
|
NuDateTime ndt;
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.modWhen);
|
details.SetCreateWhen(ndt);
|
||||||
|
details.SetArchiveWhen(ndt);
|
||||||
|
details.SetModWhen(ndt);
|
||||||
|
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
|
|
||||||
fDirty = true;
|
fDirty = true;
|
||||||
if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput,
|
if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput,
|
||||||
fOutputLen, NULL, -1, /*ref*/errMsg, this))
|
fOutputLen, NULL, -1, &errMsg, this))
|
||||||
{
|
{
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,29 +418,33 @@ void CassetteDialog::OnImport(void)
|
||||||
/*
|
/*
|
||||||
* Write the file to the currently-open archive.
|
* Write the file to the currently-open archive.
|
||||||
*/
|
*/
|
||||||
GenericArchive::FileDetails details;
|
GenericArchive::LocalFileDetails details;
|
||||||
|
|
||||||
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork);
|
||||||
details.origName = "Cassette WAV";
|
details.SetLocalPathName(L"Cassette WAV");
|
||||||
details.storageName = impDialog.fFileName;
|
details.SetStrippedLocalPathName(impDialog.fFileName);
|
||||||
details.access = 0xe3; // unlocked, backup bit set
|
details.SetAccess(0xe3); // unlocked, backup bit set
|
||||||
details.fileType = impDialog.GetFileType();
|
details.SetFileType(impDialog.GetFileType());
|
||||||
if (details.fileType == kFileTypeBIN)
|
if (details.GetFileType() == kFileTypeBIN) {
|
||||||
details.extraType = impDialog.fStartAddr;
|
details.SetExtraType(impDialog.fStartAddr);
|
||||||
else if (details.fileType == kFileTypeBAS)
|
} else if (details.GetFileType() == kFileTypeBAS) {
|
||||||
details.extraType = 0x0801;
|
details.SetExtraType(0x0801);
|
||||||
else
|
} else {
|
||||||
details.extraType = 0x0000;
|
details.SetExtraType(0x0000);
|
||||||
details.storageType = DiskFS::kStorageSeedling;
|
}
|
||||||
|
details.SetStorageType(DiskFS::kStorageSeedling);
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen);
|
NuDateTime ndt;
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||||
|
details.SetCreateWhen(ndt);
|
||||||
|
details.SetArchiveWhen(ndt);
|
||||||
|
details.SetModWhen(ndt);
|
||||||
|
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
|
|
||||||
fDirty = true;
|
fDirty = true;
|
||||||
if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(),
|
if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(),
|
||||||
fDataArray[idx].GetDataLen(), NULL, -1, /*ref*/errMsg, this))
|
fDataArray[idx].GetDataLen(), NULL, -1, &errMsg, this))
|
||||||
{
|
{
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,11 +80,11 @@ typedef struct FileCollectionEntry {
|
||||||
uint32_t cmmtLen; // len of comments
|
uint32_t cmmtLen; // len of comments
|
||||||
uint32_t fileType;
|
uint32_t fileType;
|
||||||
uint32_t auxType;
|
uint32_t auxType;
|
||||||
int64_t createWhen; // time_t
|
int64_t createWhen; // holds time_t
|
||||||
int64_t modWhen; // time_t
|
int64_t modWhen; // holds time_t
|
||||||
uint8_t access; // ProDOS access flags
|
uint8_t access; // ProDOS access flags
|
||||||
uint8_t entryKind; // GenericArchive::FileDetails::FileKind
|
uint8_t entryKind; // GenericArchive::FileDetails::FileKind
|
||||||
uint8_t sourceFS; // DiskImgLib::DiskImg::FSFormat
|
uint8_t sourceFS; // holds DiskImgLib::DiskImg::FSFormat
|
||||||
uint8_t fssep; // filesystem separator char, e.g. ':'
|
uint8_t fssep; // filesystem separator char, e.g. ':'
|
||||||
|
|
||||||
/* data comes next: null-terminated WCHAR filename, then data fork, then
|
/* data comes next: null-terminated WCHAR filename, then data fork, then
|
||||||
|
@ -440,17 +440,17 @@ CString MainWindow::CopyToCollection(GenericEntry* pEntry, void** pBuf,
|
||||||
return errStr;
|
return errStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericArchive::FileDetails::FileKind entryKind;
|
GenericArchive::LocalFileDetails::FileKind entryKind;
|
||||||
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory)
|
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory)
|
||||||
entryKind = GenericArchive::FileDetails::kFileKindDirectory;
|
entryKind = GenericArchive::LocalFileDetails::kFileKindDirectory;
|
||||||
else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
|
else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
|
||||||
entryKind = GenericArchive::FileDetails::kFileKindBothForks;
|
entryKind = GenericArchive::LocalFileDetails::kFileKindBothForks;
|
||||||
else if (pEntry->GetHasDataFork())
|
else if (pEntry->GetHasDataFork())
|
||||||
entryKind = GenericArchive::FileDetails::kFileKindDataFork;
|
entryKind = GenericArchive::LocalFileDetails::kFileKindDataFork;
|
||||||
else if (pEntry->GetHasRsrcFork())
|
else if (pEntry->GetHasRsrcFork())
|
||||||
entryKind = GenericArchive::FileDetails::kFileKindRsrcFork;
|
entryKind = GenericArchive::LocalFileDetails::kFileKindRsrcFork;
|
||||||
else if (pEntry->GetHasDiskImage())
|
else if (pEntry->GetHasDiskImage())
|
||||||
entryKind = GenericArchive::FileDetails::kFileKindDiskImage;
|
entryKind = GenericArchive::LocalFileDetails::kFileKindDiskImage;
|
||||||
else {
|
else {
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
return errStr;
|
return errStr;
|
||||||
|
@ -912,30 +912,32 @@ bail:
|
||||||
CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||||
const WCHAR* pathName, const uint8_t* buf, long remLen)
|
const WCHAR* pathName, const uint8_t* buf, long remLen)
|
||||||
{
|
{
|
||||||
GenericArchive::FileDetails::FileKind entryKind;
|
GenericArchive::LocalFileDetails::FileKind entryKind;
|
||||||
GenericArchive::FileDetails details;
|
GenericArchive::LocalFileDetails details;
|
||||||
uint8_t* dataBuf = NULL;
|
uint8_t* dataBuf = NULL;
|
||||||
uint8_t* rsrcBuf = NULL;
|
uint8_t* rsrcBuf = NULL;
|
||||||
long dataLen, rsrcLen, cmmtLen;
|
long dataLen, rsrcLen, cmmtLen;
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
|
|
||||||
entryKind = (GenericArchive::FileDetails::FileKind) pCollEnt->entryKind;
|
entryKind = (GenericArchive::LocalFileDetails::FileKind) pCollEnt->entryKind;
|
||||||
LOGI(" Processing '%ls' (%d)", pathName, entryKind);
|
LOGD(" Processing '%ls' (%d)", pathName, entryKind);
|
||||||
|
|
||||||
details.entryKind = entryKind;
|
details.SetEntryKind(entryKind);
|
||||||
details.origName = L"Clipboard";
|
details.SetLocalPathName(L"Clipboard");
|
||||||
details.storageName = pathName; // TODO MacRoman convert
|
details.SetStrippedLocalPathName(pathName);
|
||||||
details.fileSysFmt = (DiskImg::FSFormat) pCollEnt->sourceFS;
|
details.SetFileSysFmt((DiskImg::FSFormat) pCollEnt->sourceFS);
|
||||||
details.fileSysInfo = pCollEnt->fssep;
|
details.SetFssep(pCollEnt->fssep);
|
||||||
details.access = pCollEnt->access;
|
details.SetAccess(pCollEnt->access);
|
||||||
details.fileType = pCollEnt->fileType;
|
details.SetFileType(pCollEnt->fileType);
|
||||||
details.extraType = pCollEnt->auxType;
|
details.SetExtraType(pCollEnt->auxType);
|
||||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen,
|
NuDateTime ndt;
|
||||||
&details.createWhen);
|
GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen, &ndt);
|
||||||
GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen,
|
details.SetCreateWhen(ndt);
|
||||||
&details.modWhen);
|
GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen, &ndt);
|
||||||
|
details.SetModWhen(ndt);
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
|
GenericArchive::UNIXTimeToDateTime(&now, &ndt);
|
||||||
|
details.SetArchiveWhen(ndt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Because of the way XferFile works, we need to make a copy of
|
* Because of the way XferFile works, we need to make a copy of
|
||||||
|
@ -958,23 +960,23 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||||
*/
|
*/
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
bool hasRsrc = false;
|
bool hasRsrc = false;
|
||||||
if (details.entryKind == GenericArchive::FileDetails::kFileKindDataFork) {
|
if (entryKind == GenericArchive::LocalFileDetails::kFileKindDataFork) {
|
||||||
hasData = true;
|
hasData = true;
|
||||||
details.storageType = kNuStorageSeedling;
|
details.SetStorageType(kNuStorageSeedling);
|
||||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindRsrcFork) {
|
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindRsrcFork) {
|
||||||
hasRsrc = true;
|
hasRsrc = true;
|
||||||
details.storageType = kNuStorageExtended;
|
details.SetStorageType(kNuStorageExtended);
|
||||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindBothForks) {
|
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindBothForks) {
|
||||||
hasData = hasRsrc = true;
|
hasData = hasRsrc = true;
|
||||||
details.storageType = kNuStorageExtended;
|
details.SetStorageType(kNuStorageExtended);
|
||||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDiskImage) {
|
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDiskImage) {
|
||||||
hasData = true;
|
hasData = true;
|
||||||
details.storageType = kNuStorageSeedling;
|
details.SetStorageType(kNuStorageSeedling);
|
||||||
} else if (details.entryKind == GenericArchive::FileDetails::kFileKindDirectory) {
|
} else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDirectory) {
|
||||||
details.storageType = kNuStorageDirectory;
|
details.SetStorageType(kNuStorageDirectory);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
return "Internal error.";
|
return L"Internal error.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasData) {
|
if (hasData) {
|
||||||
|
@ -985,7 +987,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||||
dataLen = pCollEnt->dataLen;
|
dataLen = pCollEnt->dataLen;
|
||||||
dataBuf = new uint8_t[dataLen];
|
dataBuf = new uint8_t[dataLen];
|
||||||
if (dataBuf == NULL)
|
if (dataBuf == NULL)
|
||||||
return "memory allocation failed.";
|
return L"memory allocation failed.";
|
||||||
memcpy(dataBuf, buf, dataLen);
|
memcpy(dataBuf, buf, dataLen);
|
||||||
buf += dataLen;
|
buf += dataLen;
|
||||||
remLen -= dataLen;
|
remLen -= dataLen;
|
||||||
|
@ -1003,7 +1005,7 @@ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
|
||||||
rsrcLen = pCollEnt->rsrcLen;
|
rsrcLen = pCollEnt->rsrcLen;
|
||||||
rsrcBuf = new uint8_t[rsrcLen];
|
rsrcBuf = new uint8_t[rsrcLen];
|
||||||
if (rsrcBuf == NULL)
|
if (rsrcBuf == NULL)
|
||||||
return "Memory allocation failed.";
|
return L"Memory allocation failed.";
|
||||||
memcpy(rsrcBuf, buf, rsrcLen);
|
memcpy(rsrcBuf, buf, rsrcLen);
|
||||||
buf += rsrcLen;
|
buf += rsrcLen;
|
||||||
remLen -= rsrcLen;
|
remLen -= rsrcLen;
|
||||||
|
|
|
@ -466,21 +466,26 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
|
||||||
{
|
{
|
||||||
CWaitCursor waitc;
|
CWaitCursor waitc;
|
||||||
|
|
||||||
|
// TODO(Unicode): modify DiskImg lib to accept wide paths
|
||||||
CStringA fileNameA(filename);
|
CStringA fileNameA(filename);
|
||||||
|
if (!PathName::TestNarrowConversion(filename, fileNameA, &errMsg)) {
|
||||||
|
result = kResultFailure;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
||||||
readOnly);
|
readOnly);
|
||||||
if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) {
|
if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) {
|
||||||
// retry file open with read-only set
|
// retry file open with read-only set
|
||||||
// don't do that for volumes -- assume they know what they want
|
// don't do that for volumes -- assume they know what they want
|
||||||
LOGI(" Retrying open with read-only set");
|
LOGD(" Retrying open with read-only set");
|
||||||
fIsReadOnly = readOnly = true;
|
fIsReadOnly = readOnly = true;
|
||||||
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep,
|
||||||
readOnly);
|
readOnly);
|
||||||
}
|
}
|
||||||
if (dierr != kDIErrNone) {
|
if (dierr != kDIErrNone) {
|
||||||
if (dierr == kDIErrFileArchive)
|
if (dierr == kDIErrFileArchive) {
|
||||||
result = kResultFileArchive;
|
result = kResultFileArchive;
|
||||||
else {
|
} else {
|
||||||
result = kResultFailure;
|
result = kResultFailure;
|
||||||
errMsg.Format(L"Unable to open '%ls': %hs.", filename,
|
errMsg.Format(L"Unable to open '%ls': %hs.", filename,
|
||||||
DiskImgLib::DIStrError(dierr));
|
DiskImgLib::DIStrError(dierr));
|
||||||
|
@ -507,7 +512,7 @@ GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename,
|
||||||
imf.SetAllowGenericFormats(false);
|
imf.SetAllowGenericFormats(false);
|
||||||
|
|
||||||
if (imf.DoModal() != IDOK) {
|
if (imf.DoModal() != IDOK) {
|
||||||
LOGI("User bailed on IMF dialog");
|
LOGD("User bailed on IMF dialog");
|
||||||
result = kResultCancel;
|
result = kResultCancel;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
@ -1246,7 +1251,7 @@ bail:
|
||||||
}
|
}
|
||||||
|
|
||||||
NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails)
|
LocalFileDetails* pDetails)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Add a file to a disk image Unfortunately we can't just add the files
|
* Add a file to a disk image Unfortunately we can't just add the files
|
||||||
|
@ -1286,8 +1291,9 @@ NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
int neededLen = 64; // reasonable guess
|
int neededLen = 64; // reasonable guess
|
||||||
char* fsNormalBuf = NULL; // name as it will appear on disk image
|
char* fsNormalBuf = NULL; // name as it will appear on disk image
|
||||||
|
|
||||||
LOGI(" +++ ADD file: orig='%ls' stor='%ls'",
|
LOGI(" +++ ADD file: orig='%ls' strip='%ls'",
|
||||||
(LPCWSTR) pDetails->origName, (LPCWSTR) pDetails->storageName);
|
(LPCWSTR) pDetails->GetLocalPathName(),
|
||||||
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
/*
|
/*
|
||||||
|
@ -1295,14 +1301,13 @@ retry:
|
||||||
*/
|
*/
|
||||||
delete[] fsNormalBuf;
|
delete[] fsNormalBuf;
|
||||||
fsNormalBuf = new char[neededLen];
|
fsNormalBuf = new char[neededLen];
|
||||||
CStringA storageNameA(pDetails->storageName);
|
dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
|
||||||
dierr = pDiskFS->NormalizePath(storageNameA,
|
|
||||||
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
||||||
if (dierr == kDIErrDataOverrun) {
|
if (dierr == kDIErrDataOverrun) {
|
||||||
/* not long enough, try again *once* */
|
/* not long enough, try again *once* */
|
||||||
delete[] fsNormalBuf;
|
delete[] fsNormalBuf;
|
||||||
fsNormalBuf = new char[neededLen];
|
fsNormalBuf = new char[neededLen];
|
||||||
dierr = pDiskFS->NormalizePath(storageNameA,
|
dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(),
|
||||||
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen);
|
||||||
}
|
}
|
||||||
if (dierr != kDIErrNone) {
|
if (dierr != kDIErrNone) {
|
||||||
|
@ -1337,16 +1342,16 @@ retry:
|
||||||
goto retry;
|
goto retry;
|
||||||
} else if (result == kNuOverwrite) {
|
} else if (result == kNuOverwrite) {
|
||||||
/* delete the existing file immediately */
|
/* delete the existing file immediately */
|
||||||
LOGI(" Deleting existing file '%hs'", fsNormalBuf);
|
LOGD(" Deleting existing file '%hs'", fsNormalBuf);
|
||||||
dierr = pDiskFS->DeleteFile(pExisting);
|
dierr = pDiskFS->DeleteFile(pExisting);
|
||||||
if (dierr != kDIErrNone) {
|
if (dierr != kDIErrNone) {
|
||||||
// Would be nice to show a dialog and explain *why*, but
|
// Would be nice to show a dialog and explain *why*, but
|
||||||
// I'm not sure we have a window here.
|
// I'm not sure we have a window here.
|
||||||
LOGI(" Deletion failed (err=%d)", dierr);
|
LOGE(" Deletion failed (err=%d)", dierr);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGI("GLITCH: bad return %d from HandleReplaceExisting",result);
|
LOGE("GLITCH: bad return %d from HandleReplaceExisting",result);
|
||||||
assert(false);
|
assert(false);
|
||||||
nuerr = kNuErrInternal;
|
nuerr = kNuErrInternal;
|
||||||
goto bail;
|
goto bail;
|
||||||
|
@ -1364,7 +1369,7 @@ retry:
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("FSNormalized is '%hs'", pAddData->GetFSNormalPath());
|
LOGD("FSNormalized is '%hs'", pAddData->GetFSNormalPath());
|
||||||
|
|
||||||
AddToAddDataList(pAddData);
|
AddToAddDataList(pAddData);
|
||||||
|
|
||||||
|
@ -1374,7 +1379,7 @@ bail:
|
||||||
}
|
}
|
||||||
|
|
||||||
NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||||
FileDetails* pDetails)
|
LocalFileDetails* pDetails)
|
||||||
{
|
{
|
||||||
NuResult result;
|
NuResult result;
|
||||||
|
|
||||||
|
@ -1390,8 +1395,8 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||||
confOvwr.fExistingFile = pExisting->GetPathName();
|
confOvwr.fExistingFile = pExisting->GetPathName();
|
||||||
confOvwr.fExistingFileModWhen = pExisting->GetModWhen();
|
confOvwr.fExistingFileModWhen = pExisting->GetModWhen();
|
||||||
|
|
||||||
PathName srcPath(pDetails->origName);
|
PathName srcPath(pDetails->GetLocalPathName());
|
||||||
confOvwr.fNewFileSource = pDetails->origName; // or storageName?
|
confOvwr.fNewFileSource = pDetails->GetLocalPathName();
|
||||||
confOvwr.fNewFileModWhen = srcPath.GetModWhen();
|
confOvwr.fNewFileModWhen = srcPath.GetModWhen();
|
||||||
|
|
||||||
if (confOvwr.DoModal() == IDCANCEL) {
|
if (confOvwr.DoModal() == IDCANCEL) {
|
||||||
|
@ -1399,6 +1404,9 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||||
return kNuAbort;
|
return kNuAbort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: if they rename one fork, we need to track that fact and
|
||||||
|
// carry it over to the other fork -- otherwise they can rename
|
||||||
|
// the data and resource forks into separate files.
|
||||||
if (confOvwr.fResultRename) {
|
if (confOvwr.fResultRename) {
|
||||||
/*
|
/*
|
||||||
* Replace the name in FileDetails. They were asked to modify
|
* Replace the name in FileDetails. They were asked to modify
|
||||||
|
@ -1413,8 +1421,9 @@ NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting,
|
||||||
* full path and reject "OK" if it's not valid. Instead, we just
|
* full path and reject "OK" if it's not valid. Instead, we just
|
||||||
* allow the FS normalizer to force the filename to be valid.
|
* allow the FS normalizer to force the filename to be valid.
|
||||||
*/
|
*/
|
||||||
pDetails->storageName = confOvwr.fExistingFile;
|
pDetails->SetStrippedLocalPathName(confOvwr.fExistingFile);
|
||||||
LOGI("Trying rename to '%ls'", (LPCWSTR) pDetails->storageName);
|
LOGI("Trying rename to '%ls'",
|
||||||
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
return kNuRename;
|
return kNuRename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1470,26 +1479,26 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
|
|
||||||
pData = fpAddDataHead;
|
pData = fpAddDataHead;
|
||||||
while (pData != NULL) {
|
while (pData != NULL) {
|
||||||
const FileDetails* pDataDetails = NULL;
|
const LocalFileDetails* pDataDetails = NULL;
|
||||||
const FileDetails* pRsrcDetails = NULL;
|
const LocalFileDetails* pRsrcDetails = NULL;
|
||||||
const FileDetails* pDetails = pData->GetDetails();
|
const LocalFileDetails* pDetails = pData->GetDetails();
|
||||||
const char* typeStr = "????"; // for debug msg only
|
const char* typeStr = "????"; // for debug msg only
|
||||||
|
|
||||||
switch (pDetails->entryKind) {
|
switch (pDetails->GetEntryKind()) {
|
||||||
case FileDetails::kFileKindDataFork:
|
case LocalFileDetails::kFileKindDataFork:
|
||||||
pDataDetails = pDetails;
|
pDataDetails = pDetails;
|
||||||
typeStr = "data";
|
typeStr = "data";
|
||||||
break;
|
break;
|
||||||
case FileDetails::kFileKindRsrcFork:
|
case LocalFileDetails::kFileKindRsrcFork:
|
||||||
pRsrcDetails = pDetails;
|
pRsrcDetails = pDetails;
|
||||||
typeStr = "rsrc";
|
typeStr = "rsrc";
|
||||||
break;
|
break;
|
||||||
case FileDetails::kFileKindDiskImage:
|
case LocalFileDetails::kFileKindDiskImage:
|
||||||
pDataDetails = pDetails;
|
pDataDetails = pDetails;
|
||||||
typeStr = "disk";
|
typeStr = "disk";
|
||||||
break;
|
break;
|
||||||
case FileDetails::kFileKindBothForks:
|
case LocalFileDetails::kFileKindBothForks:
|
||||||
case FileDetails::kFileKindDirectory:
|
case LocalFileDetails::kFileKindDirectory:
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return L"internal error";
|
return L"internal error";
|
||||||
|
@ -1499,20 +1508,20 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
pDetails = pData->GetOtherFork()->GetDetails();
|
pDetails = pData->GetOtherFork()->GetDetails();
|
||||||
typeStr = "both";
|
typeStr = "both";
|
||||||
|
|
||||||
switch (pDetails->entryKind) {
|
switch (pDetails->GetEntryKind()) {
|
||||||
case FileDetails::kFileKindDataFork:
|
case LocalFileDetails::kFileKindDataFork:
|
||||||
assert(pDataDetails == NULL);
|
assert(pDataDetails == NULL);
|
||||||
pDataDetails = pDetails;
|
pDataDetails = pDetails;
|
||||||
break;
|
break;
|
||||||
case FileDetails::kFileKindRsrcFork:
|
case LocalFileDetails::kFileKindRsrcFork:
|
||||||
assert(pRsrcDetails == NULL);
|
assert(pRsrcDetails == NULL);
|
||||||
pRsrcDetails = pDetails;
|
pRsrcDetails = pDetails;
|
||||||
break;
|
break;
|
||||||
case FileDetails::kFileKindDiskImage:
|
case LocalFileDetails::kFileKindDiskImage:
|
||||||
assert(false);
|
assert(false);
|
||||||
return L"(internal) add other disk error";
|
return L"(internal) add other disk error";
|
||||||
case FileDetails::kFileKindBothForks:
|
case LocalFileDetails::kFileKindBothForks:
|
||||||
case FileDetails::kFileKindDirectory:
|
case LocalFileDetails::kFileKindDirectory:
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return L"internal error";
|
return L"internal error";
|
||||||
|
@ -1520,7 +1529,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("Adding file '%ls' (%hs)",
|
LOGI("Adding file '%ls' (%hs)",
|
||||||
(LPCWSTR) pDetails->storageName, typeStr);
|
(LPCWSTR) pDetails->GetStrippedLocalPathName(), typeStr);
|
||||||
ASSERT(pDataDetails != NULL || pRsrcDetails != NULL);
|
ASSERT(pDataDetails != NULL || pRsrcDetails != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1530,14 +1539,20 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
* but it could be awkward for HFS (not to mention HFS Plus!).
|
* but it could be awkward for HFS (not to mention HFS Plus!).
|
||||||
*/
|
*/
|
||||||
DiskFS::CreateParms parms;
|
DiskFS::CreateParms parms;
|
||||||
ConvertFDToCP(pData->GetDetails(), &parms);
|
/* use the FS-normalized path here */
|
||||||
|
/* (do we have to? do we want to?) */
|
||||||
|
parms.pathName = pData->GetFSNormalPath();
|
||||||
if (pRsrcDetails != NULL)
|
if (pRsrcDetails != NULL)
|
||||||
parms.storageType = kNuStorageExtended;
|
parms.storageType = kNuStorageExtended;
|
||||||
else
|
else
|
||||||
parms.storageType = kNuStorageSeedling;
|
parms.storageType = kNuStorageSeedling;
|
||||||
/* use the FS-normalized path here */
|
/* copy the rest out of the LocalFileDetails */
|
||||||
/* (do we have to? do we want to?) */
|
parms.fssep = pDetails->GetFssep();
|
||||||
parms.pathName = pData->GetFSNormalPath();
|
parms.fileType = pDetails->GetFileType();
|
||||||
|
parms.auxType = pDetails->GetExtraType();
|
||||||
|
parms.access = pDetails->GetAccess();
|
||||||
|
parms.createWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetCreateWhen());
|
||||||
|
parms.modWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetModWhen());
|
||||||
|
|
||||||
dataLen = rsrcLen = -1;
|
dataLen = rsrcLen = -1;
|
||||||
if (pDataDetails != NULL) {
|
if (pDataDetails != NULL) {
|
||||||
|
@ -1545,8 +1560,8 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
/* (HA conversion only happens if text conversion happens) */
|
/* (HA conversion only happens if text conversion happens) */
|
||||||
GenericEntry::ConvertHighASCII convHA;
|
GenericEntry::ConvertHighASCII convHA;
|
||||||
if (addOptsConvEOL == AddFilesDialog::kConvEOLType) {
|
if (addOptsConvEOL == AddFilesDialog::kConvEOLType) {
|
||||||
if (pDataDetails->fileType == kFileTypeTXT ||
|
if (pDataDetails->GetFileType() == kFileTypeTXT ||
|
||||||
pDataDetails->fileType == kFileTypeSRC)
|
pDataDetails->GetFileType() == kFileTypeSRC)
|
||||||
{
|
{
|
||||||
LOGI("Enabling text conversion by type");
|
LOGI("Enabling text conversion by type");
|
||||||
convEOL = GenericEntry::kConvertEOLOn;
|
convEOL = GenericEntry::kConvertEOLOn;
|
||||||
|
@ -1559,14 +1574,14 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
else
|
else
|
||||||
convHA = GenericEntry::kConvertHAOff;
|
convHA = GenericEntry::kConvertHAOff;
|
||||||
|
|
||||||
errMsg = LoadFile(pDataDetails->origName, &dataBuf, &dataLen,
|
errMsg = LoadFile(pDataDetails->GetLocalPathName(), &dataBuf, &dataLen,
|
||||||
convEOL, convHA);
|
convEOL, convHA);
|
||||||
if (!errMsg.IsEmpty())
|
if (!errMsg.IsEmpty())
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
if (pRsrcDetails != NULL) {
|
if (pRsrcDetails != NULL) {
|
||||||
/* no text conversion on resource forks */
|
/* no text conversion on resource forks */
|
||||||
errMsg = LoadFile(pRsrcDetails->origName, &rsrcBuf, &rsrcLen,
|
errMsg = LoadFile(pRsrcDetails->GetLocalPathName(), &rsrcBuf, &rsrcLen,
|
||||||
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff);
|
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff);
|
||||||
if (!errMsg.IsEmpty())
|
if (!errMsg.IsEmpty())
|
||||||
goto bail;
|
goto bail;
|
||||||
|
@ -1575,7 +1590,7 @@ CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL)
|
||||||
/* really ought to do this separately for each thread */
|
/* really ought to do this separately for each thread */
|
||||||
SET_PROGRESS_BEGIN();
|
SET_PROGRESS_BEGIN();
|
||||||
CString pathNameW(parms.pathName);
|
CString pathNameW(parms.pathName);
|
||||||
SET_PROGRESS_UPDATE2(0, pDetails->origName, pathNameW);
|
SET_PROGRESS_UPDATE2(0, pDetails->GetLocalPathName(), pathNameW);
|
||||||
|
|
||||||
DIError dierr;
|
DIError dierr;
|
||||||
dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen,
|
dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen,
|
||||||
|
@ -1954,25 +1969,6 @@ bail:
|
||||||
return dierr;
|
return dierr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this a member of FileDetails, and return a struct owned by
|
|
||||||
// FileDetails. This is necessary because we put const strings into
|
|
||||||
// pCreateParms that are owned by FileDetails, and need to coordinate the
|
|
||||||
// object lifetime.
|
|
||||||
void DiskArchive::ConvertFDToCP(const FileDetails* pDetails,
|
|
||||||
DiskFS::CreateParms* pCreateParms)
|
|
||||||
{
|
|
||||||
// ugly hack to get storage for narrow string
|
|
||||||
pDetails->fStorageNameA = pDetails->storageName;
|
|
||||||
pCreateParms->pathName = pDetails->fStorageNameA;
|
|
||||||
pCreateParms->fssep = (char) pDetails->fileSysInfo;
|
|
||||||
pCreateParms->storageType = pDetails->storageType;
|
|
||||||
pCreateParms->fileType = pDetails->fileType;
|
|
||||||
pCreateParms->auxType = pDetails->extraType;
|
|
||||||
pCreateParms->access = pDetails->access;
|
|
||||||
pCreateParms->createWhen = NufxArchive::DateTimeToSeconds(&pDetails->createWhen);
|
|
||||||
pCreateParms->modWhen = NufxArchive::DateTimeToSeconds(&pDetails->modWhen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskArchive::AddToAddDataList(FileAddData* pData)
|
void DiskArchive::AddToAddDataList(FileAddData* pData)
|
||||||
{
|
{
|
||||||
ASSERT(pData != NULL);
|
ASSERT(pData != NULL);
|
||||||
|
@ -1983,31 +1979,31 @@ void DiskArchive::AddToAddDataList(FileAddData* pData)
|
||||||
* O(n^2) behavior, but I'm expecting N to be relatively small (under
|
* O(n^2) behavior, but I'm expecting N to be relatively small (under
|
||||||
* 1000 in almost all cases).
|
* 1000 in almost all cases).
|
||||||
*/
|
*/
|
||||||
//if (strcasecmp(pData->GetDetails()->storageName, "system\\finder") == 0)
|
|
||||||
// LOGI("whee");
|
|
||||||
FileAddData* pSearch = fpAddDataHead;
|
FileAddData* pSearch = fpAddDataHead;
|
||||||
FileDetails::FileKind dataKind, listKind;
|
LocalFileDetails::FileKind dataKind, listKind;
|
||||||
|
|
||||||
dataKind = pData->GetDetails()->entryKind;
|
dataKind = pData->GetDetails()->GetEntryKind();
|
||||||
while (pSearch != NULL) {
|
while (pSearch != NULL) {
|
||||||
if (pSearch->GetOtherFork() == NULL &&
|
if (pSearch->GetOtherFork() == NULL &&
|
||||||
wcscmp(pSearch->GetDetails()->storageName,
|
wcscmp(pSearch->GetDetails()->GetStrippedLocalPathName(),
|
||||||
pData->GetDetails()->storageName) == 0)
|
pData->GetDetails()->GetStrippedLocalPathName()) == 0)
|
||||||
{
|
{
|
||||||
//NuThreadID dataID = pData->GetDetails()->threadID;
|
//NuThreadID dataID = pData->GetDetails()->threadID;
|
||||||
//NuThreadID listID = pSearch->GetDetails()->threadID;
|
//NuThreadID listID = pSearch->GetDetails()->threadID;
|
||||||
|
|
||||||
listKind = pSearch->GetDetails()->entryKind;
|
listKind = pSearch->GetDetails()->GetEntryKind();
|
||||||
|
|
||||||
/* got a name match */
|
/* got a name match */
|
||||||
if (dataKind != listKind &&
|
if (dataKind != listKind &&
|
||||||
(dataKind == FileDetails::kFileKindDataFork || dataKind == FileDetails::kFileKindRsrcFork) &&
|
(dataKind == LocalFileDetails::kFileKindDataFork ||
|
||||||
(listKind == FileDetails::kFileKindDataFork || listKind == FileDetails::kFileKindRsrcFork))
|
dataKind == LocalFileDetails::kFileKindRsrcFork) &&
|
||||||
|
(listKind == LocalFileDetails::kFileKindDataFork ||
|
||||||
|
listKind == LocalFileDetails::kFileKindRsrcFork))
|
||||||
{
|
{
|
||||||
/* looks good, hook it in here instead of the list */
|
/* looks good, hook it in here instead of the list */
|
||||||
LOGD("--- connecting forks of '%ls' and '%ls'",
|
LOGD("--- connecting forks of '%ls' and '%ls'",
|
||||||
(LPCWSTR) pData->GetDetails()->origName,
|
(LPCWSTR) pData->GetDetails()->GetLocalPathName(),
|
||||||
(LPCWSTR) pSearch->GetDetails()->origName);
|
(LPCWSTR) pSearch->GetDetails()->GetLocalPathName());
|
||||||
pSearch->SetOtherFork(pData);
|
pSearch->SetOtherFork(pData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2299,7 +2295,7 @@ bool DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
|
||||||
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
|
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
LOGI("Rename of '%ls' to '%ls' succeeded",
|
LOGD("Rename of '%ls' to '%ls' succeeded",
|
||||||
pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName);
|
pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName);
|
||||||
} else if (result == IDCANCEL) {
|
} else if (result == IDCANCEL) {
|
||||||
LOGI("Canceling out of remaining renames");
|
LOGI("Canceling out of remaining renames");
|
||||||
|
@ -2528,7 +2524,7 @@ bool DiskArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pGenericEntry,
|
||||||
DiskImg::FSFormat fsFormat;
|
DiskImg::FSFormat fsFormat;
|
||||||
fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat();
|
fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat();
|
||||||
if (fsFormat == DiskImg::kFormatDOS32 || fsFormat == DiskImg::kFormatDOS33) {
|
if (fsFormat == DiskImg::kFormatDOS32 || fsFormat == DiskImg::kFormatDOS33) {
|
||||||
LOGI(" (reloading additional fields after DOS SFI)");
|
LOGD(" (reloading additional fields after DOS SFI)");
|
||||||
pEntry->SetDataForkLen(pFile->GetDataLength());
|
pEntry->SetDataForkLen(pFile->GetDataLength());
|
||||||
pEntry->SetCompressedLen(pFile->GetDataSparseLength());
|
pEntry->SetCompressedLen(pFile->GetDataSparseLength());
|
||||||
pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious);
|
pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious);
|
||||||
|
@ -2559,9 +2555,9 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
* forked or not.
|
* forked or not.
|
||||||
*/
|
*/
|
||||||
LOGI("DiskArchive XferSelection!");
|
LOGI("DiskArchive XferSelection!");
|
||||||
unsigned char* dataBuf = NULL;
|
uint8_t* dataBuf = NULL;
|
||||||
unsigned char* rsrcBuf = NULL;
|
uint8_t* rsrcBuf = NULL;
|
||||||
FileDetails fileDetails;
|
LocalFileDetails fileDetails;
|
||||||
CString errMsg, extractErrMsg, cmpStr;
|
CString errMsg, extractErrMsg, cmpStr;
|
||||||
CString fixedPathName;
|
CString fixedPathName;
|
||||||
XferStatus retval = kXferFailed;
|
XferStatus retval = kXferFailed;
|
||||||
|
@ -2590,7 +2586,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
*/
|
*/
|
||||||
fixedPathName = pEntry->GetPathName();
|
fixedPathName = pEntry->GetPathName();
|
||||||
if (fixedPathName.IsEmpty())
|
if (fixedPathName.IsEmpty())
|
||||||
fixedPathName = _T("(no filename)");
|
fixedPathName = L"(no filename)";
|
||||||
if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS)
|
if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS)
|
||||||
fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.');
|
fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.');
|
||||||
if (pEntry->GetSubVolName() != NULL) {
|
if (pEntry->GetSubVolName() != NULL) {
|
||||||
|
@ -2603,7 +2599,7 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
|
|
||||||
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) {
|
if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) {
|
||||||
/* this is the volume dir */
|
/* this is the volume dir */
|
||||||
LOGI(" XFER not transferring volume dir '%ls'",
|
LOGD(" XFER not transferring volume dir '%ls'",
|
||||||
(LPCWSTR) fixedPathName);
|
(LPCWSTR) fixedPathName);
|
||||||
continue;
|
continue;
|
||||||
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) {
|
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) {
|
||||||
|
@ -2613,22 +2609,22 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
cmpStr += (char)PathProposal::kDefaultStoredFssep;
|
cmpStr += (char)PathProposal::kDefaultStoredFssep;
|
||||||
|
|
||||||
if (pSelSet->CountMatchingPrefix(cmpStr) == 0) {
|
if (pSelSet->CountMatchingPrefix(cmpStr) == 0) {
|
||||||
LOGI("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName);
|
LOGD("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||||
cmpStr += kEmptyFolderMarker;
|
cmpStr += kEmptyFolderMarker;
|
||||||
dataBuf = new unsigned char[1];
|
dataBuf = new unsigned char[1];
|
||||||
dataLen = 0;
|
dataLen = 0;
|
||||||
fileDetails.entryKind = FileDetails::kFileKindDataFork;
|
fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
|
||||||
fileDetails.storageName = cmpStr;
|
fileDetails.SetStrippedLocalPathName(cmpStr);
|
||||||
fileDetails.fileType = 0; // NON
|
fileDetails.SetFileType(0); // NON
|
||||||
fileDetails.access =
|
fileDetails.SetAccess(
|
||||||
pEntry->GetAccess() | GenericEntry::kAccessInvisible;
|
pEntry->GetAccess() | GenericEntry::kAccessInvisible);
|
||||||
goto have_stuff2;
|
goto have_stuff2;
|
||||||
} else {
|
} else {
|
||||||
LOGI("NOT empty dir '%ls'", (LPCWSTR) fixedPathName);
|
LOGD("NOT empty dir '%ls'", (LPCWSTR) fixedPathName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI(" XFER not transferring directory '%ls'",
|
LOGD(" XFER not transferring directory '%ls'",
|
||||||
(LPCWSTR) fixedPathName);
|
(LPCWSTR) fixedPathName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2695,38 +2691,42 @@ GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
ASSERT(rsrcBuf == NULL);
|
ASSERT(rsrcBuf == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork())
|
if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) {
|
||||||
fileDetails.entryKind = FileDetails::kFileKindBothForks;
|
fileDetails.SetEntryKind(LocalFileDetails::kFileKindBothForks);
|
||||||
else if (pEntry->GetHasDataFork())
|
} else if (pEntry->GetHasDataFork()) {
|
||||||
fileDetails.entryKind = FileDetails::kFileKindDataFork;
|
fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork);
|
||||||
else if (pEntry->GetHasRsrcFork())
|
} else if (pEntry->GetHasRsrcFork()) {
|
||||||
fileDetails.entryKind = FileDetails::kFileKindRsrcFork;
|
fileDetails.SetEntryKind(LocalFileDetails::kFileKindRsrcFork);
|
||||||
else {
|
} else {
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
fileDetails.entryKind = FileDetails::kFileKindUnknown;
|
fileDetails.SetEntryKind(LocalFileDetails::kFileKindUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the FileDetails.
|
* Set up the rest of the LocalFileDetails fields.
|
||||||
*/
|
*/
|
||||||
fileDetails.storageName = fixedPathName;
|
fileDetails.SetStrippedLocalPathName(fixedPathName);
|
||||||
fileDetails.fileType = pEntry->GetFileType();
|
fileDetails.SetFileType(pEntry->GetFileType());
|
||||||
fileDetails.access = pEntry->GetAccess();
|
fileDetails.SetAccess(pEntry->GetAccess());
|
||||||
have_stuff2:
|
have_stuff2:
|
||||||
fileDetails.fileSysFmt = pEntry->GetSourceFS();
|
fileDetails.SetFileSysFmt(pEntry->GetSourceFS());
|
||||||
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep;
|
fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
|
||||||
fileDetails.extraType = pEntry->GetAuxType();
|
fileDetails.SetExtraType(pEntry->GetAuxType());
|
||||||
fileDetails.storageType = kNuStorageUnknown; /* let NufxLib deal */
|
fileDetails.SetStorageType(kNuStorageUnknown); // let NufxLib deal
|
||||||
|
|
||||||
|
NuDateTime ndt;
|
||||||
time_t when;
|
time_t when;
|
||||||
when = time(NULL);
|
when = time(NULL);
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen);
|
UNIXTimeToDateTime(&when, &ndt);
|
||||||
|
fileDetails.SetArchiveWhen(ndt);
|
||||||
when = pEntry->GetModWhen();
|
when = pEntry->GetModWhen();
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.modWhen);
|
UNIXTimeToDateTime(&when, &ndt);
|
||||||
|
fileDetails.SetModWhen(ndt);
|
||||||
when = pEntry->GetCreateWhen();
|
when = pEntry->GetCreateWhen();
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.createWhen);
|
UNIXTimeToDateTime(&when, &ndt);
|
||||||
|
fileDetails.SetCreateWhen(ndt);
|
||||||
|
|
||||||
pActionProgress->SetArcName(fileDetails.storageName);
|
pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName());
|
||||||
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
||||||
retval = kXferCancelled;
|
retval = kXferCancelled;
|
||||||
goto bail;
|
goto bail;
|
||||||
|
@ -2780,23 +2780,20 @@ void DiskArchive::XferPrepare(const XferFileOptions* pXferOpts)
|
||||||
fpXferTargetFS = pXferOpts->fpTargetFS;
|
fpXferTargetFS = pXferOpts->fpTargetFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
CString DiskArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
|
||||||
{
|
{
|
||||||
//const int kFileTypeTXT = 0x04;
|
|
||||||
DiskFS::CreateParms createParms;
|
|
||||||
DiskFS* pDiskFS;
|
DiskFS* pDiskFS;
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
DIError dierr = kDIErrNone;
|
DIError dierr = kDIErrNone;
|
||||||
|
|
||||||
LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)",
|
LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)",
|
||||||
(LPCWSTR) pDetails->storageName, dataLen, rsrcLen);
|
(LPCWSTR) pDetails->GetStrippedLocalPathName(), dataLen, rsrcLen);
|
||||||
|
|
||||||
ASSERT(pDataBuf != NULL);
|
ASSERT(pDataBuf != NULL);
|
||||||
ASSERT(pRsrcBuf != NULL);
|
ASSERT(pRsrcBuf != NULL);
|
||||||
|
|
||||||
/* fill out CreateParms from FileDetails */
|
const DiskFS::CreateParms& createParms = pDetails->GetCreateParms();
|
||||||
ConvertFDToCP(pDetails, &createParms);
|
|
||||||
|
|
||||||
if (fpXferTargetFS == NULL)
|
if (fpXferTargetFS == NULL)
|
||||||
pDiskFS = fpPrimaryDiskFS;
|
pDiskFS = fpPrimaryDiskFS;
|
||||||
|
@ -2813,22 +2810,24 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
* not worth adding a new interface just for that.
|
* not worth adding a new interface just for that.
|
||||||
*/
|
*/
|
||||||
bool srcIsDOS, dstIsDOS;
|
bool srcIsDOS, dstIsDOS;
|
||||||
srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt);
|
srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt());
|
||||||
dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat());
|
dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat());
|
||||||
if (dataLen > 0 &&
|
if (dataLen > 0 &&
|
||||||
(pDetails->fileType == kFileTypeTXT || pDetails->fileType == kFileTypeSRC))
|
(pDetails->GetFileType() == kFileTypeTXT ||
|
||||||
|
pDetails->GetFileType() == kFileTypeSRC))
|
||||||
{
|
{
|
||||||
unsigned char* ucp = *pDataBuf;
|
unsigned char* ucp = *pDataBuf;
|
||||||
long len = dataLen;
|
long len = dataLen;
|
||||||
|
|
||||||
if (srcIsDOS && !dstIsDOS) {
|
if (srcIsDOS && !dstIsDOS) {
|
||||||
LOGD(" Stripping high ASCII from '%ls'",
|
LOGD(" Stripping high ASCII from '%ls'",
|
||||||
(LPCWSTR) pDetails->storageName);
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
|
|
||||||
while (len--)
|
while (len--)
|
||||||
*ucp++ &= 0x7f;
|
*ucp++ &= 0x7f;
|
||||||
} else if (!srcIsDOS && dstIsDOS) {
|
} else if (!srcIsDOS && dstIsDOS) {
|
||||||
LOGD(" Adding high ASCII to '%ls'", (LPCWSTR) pDetails->storageName);
|
LOGD(" Adding high ASCII to '%ls'",
|
||||||
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
if (*ucp != '\0')
|
if (*ucp != '\0')
|
||||||
|
@ -2837,9 +2836,10 @@ CString DiskArchive::XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
}
|
}
|
||||||
} else if (srcIsDOS && dstIsDOS) {
|
} else if (srcIsDOS && dstIsDOS) {
|
||||||
LOGD(" --- not altering DOS-to-DOS text '%ls'",
|
LOGD(" --- not altering DOS-to-DOS text '%ls'",
|
||||||
(LPCWSTR) pDetails->storageName);
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
} else {
|
} else {
|
||||||
LOGD(" --- non-DOS transfer '%ls'", (LPCWSTR) pDetails->storageName);
|
LOGD(" --- non-DOS transfer '%ls'",
|
||||||
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ private:
|
||||||
virtual CString Close(void);
|
virtual CString Close(void);
|
||||||
|
|
||||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
||||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
||||||
virtual void XferAbort(CWnd* pMsgWnd) override;
|
virtual void XferAbort(CWnd* pMsgWnd) override;
|
||||||
virtual void XferFinish(CWnd* pMsgWnd) override;
|
virtual void XferFinish(CWnd* pMsgWnd) override;
|
||||||
|
@ -253,10 +253,10 @@ private:
|
||||||
*/
|
*/
|
||||||
class FileAddData {
|
class FileAddData {
|
||||||
public:
|
public:
|
||||||
FileAddData(const FileDetails* pDetails, char* fsNormalPath) {
|
FileAddData(const LocalFileDetails* pDetails, char* fsNormalPathMOR) {
|
||||||
fDetails = *pDetails;
|
fDetails = *pDetails;
|
||||||
|
|
||||||
fFSNormalPath = fsNormalPath;
|
fFSNormalPathMOR = fsNormalPathMOR;
|
||||||
fpOtherFork = NULL;
|
fpOtherFork = NULL;
|
||||||
fpNext = NULL;
|
fpNext = NULL;
|
||||||
}
|
}
|
||||||
|
@ -267,25 +267,21 @@ private:
|
||||||
FileAddData* GetOtherFork(void) const { return fpOtherFork; }
|
FileAddData* GetOtherFork(void) const { return fpOtherFork; }
|
||||||
void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; }
|
void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; }
|
||||||
|
|
||||||
const FileDetails* GetDetails(void) const { return &fDetails; }
|
const LocalFileDetails* GetDetails(void) const { return &fDetails; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the "FS-normal" path, i.e. exactly what we want to appear
|
* Get the "FS-normal" path, i.e. exactly what we want to appear
|
||||||
* on the disk image. This has the result of any conversions, so
|
* on the disk image. This has the result of any conversions, so
|
||||||
* we need to store it as a narrow string.
|
* we need to store it as a narrow Mac OS Roman string.
|
||||||
*/
|
*/
|
||||||
const char* GetFSNormalPath(void) const { return fFSNormalPath; }
|
const char* GetFSNormalPath(void) const { return fFSNormalPathMOR; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Three filenames stored inside FileDetails:
|
LocalFileDetails fDetails;
|
||||||
// fDetails.origName -- the name of the Windows file
|
|
||||||
// fDetails.storageName -- origName with type-preservation goodies
|
|
||||||
// stripped out
|
|
||||||
// fFSNormalPath -- the FS-normalized version of "storageName", i.e.
|
|
||||||
// the name as it will appear on the Apple II disk image
|
|
||||||
|
|
||||||
FileDetails fDetails;
|
// The DiskFS-normalized version of the storage name. This is the
|
||||||
CStringA fFSNormalPath;
|
// name as it will appear on the Apple II disk image.
|
||||||
|
CStringA fFSNormalPathMOR;
|
||||||
|
|
||||||
FileAddData* fpOtherFork;
|
FileAddData* fpOtherFork;
|
||||||
FileAddData* fpNext;
|
FileAddData* fpNext;
|
||||||
|
@ -293,7 +289,7 @@ private:
|
||||||
|
|
||||||
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; }
|
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; }
|
||||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails) override;
|
LocalFileDetails* pDetails) override;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reload the contents of the archive, showing an error message if the
|
* Reload the contents of the archive, showing an error message if the
|
||||||
|
@ -344,7 +340,7 @@ private:
|
||||||
* Replaces pDetails->storageName if the user elects to rename
|
* Replaces pDetails->storageName if the user elects to rename
|
||||||
*/
|
*/
|
||||||
NuResult HandleReplaceExisting(const A2File* pExisting,
|
NuResult HandleReplaceExisting(const A2File* pExisting,
|
||||||
FileDetails* pDetails);
|
LocalFileDetails* pDetails);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the list of pending file adds.
|
* Process the list of pending file adds.
|
||||||
|
@ -394,15 +390,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void FreeAddDataList(void);
|
void FreeAddDataList(void);
|
||||||
|
|
||||||
/*
|
|
||||||
* Fill out a CreateParms structure from a FileDetails structure.
|
|
||||||
*
|
|
||||||
* The NuStorageType values correspond exactly to ProDOS storage types, so
|
|
||||||
* there's no need to convert them.
|
|
||||||
*/
|
|
||||||
void ConvertFDToCP(const FileDetails* pDetails,
|
|
||||||
DiskFS::CreateParms* pCreateParms);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up a RenameEntryDialog for the entry in "*pEntry".
|
* Set up a RenameEntryDialog for the entry in "*pEntry".
|
||||||
*
|
*
|
||||||
|
|
|
@ -1287,9 +1287,11 @@ void PathProposal::DenormalizePath(WCHAR* pathBuf)
|
||||||
ch = HexDigit(*srcp) << 4;
|
ch = HexDigit(*srcp) << 4;
|
||||||
srcp++;
|
srcp++;
|
||||||
if (isxdigit((int)*srcp)) {
|
if (isxdigit((int)*srcp)) {
|
||||||
/* valid, output char */
|
/* valid, output char (unless it's a %00 place-holder) */
|
||||||
ch += HexDigit(*srcp);
|
ch += HexDigit(*srcp);
|
||||||
*dstp++ = ch;
|
if (ch != '\0') {
|
||||||
|
*dstp++ = ch;
|
||||||
|
}
|
||||||
srcp++;
|
srcp++;
|
||||||
} else {
|
} else {
|
||||||
/* bogus '%' with trailing hex digit found! */
|
/* bogus '%' with trailing hex digit found! */
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "GenericArchive.h"
|
#include "GenericArchive.h"
|
||||||
|
#include "NufxArchive.h"
|
||||||
#include "FileNameConv.h"
|
#include "FileNameConv.h"
|
||||||
#include "ContentList.h"
|
#include "ContentList.h"
|
||||||
#include "Main.h"
|
#include "Main.h"
|
||||||
|
@ -46,36 +47,34 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GenericEntry::GenericEntry(void)
|
GenericEntry::GenericEntry(void)
|
||||||
|
: fPathName(NULL),
|
||||||
|
fFileName(NULL),
|
||||||
|
fFileNameExtension(NULL),
|
||||||
|
fFssep('\0'),
|
||||||
|
fSubVolName(NULL),
|
||||||
|
fDisplayName(NULL),
|
||||||
|
fFileType(0),
|
||||||
|
fAuxType(0),
|
||||||
|
fAccess(0),
|
||||||
|
fCreateWhen(kDateNone),
|
||||||
|
fModWhen(kDateNone),
|
||||||
|
fRecordKind(kRecordKindUnknown),
|
||||||
|
fFormatStr(L"Unknown"),
|
||||||
|
fDataForkLen(0),
|
||||||
|
fRsrcForkLen(0),
|
||||||
|
fCompressedLen(0),
|
||||||
|
fSourceFS(DiskImg::kFormatUnknown),
|
||||||
|
fHasDataFork(false),
|
||||||
|
fHasRsrcFork(false),
|
||||||
|
fHasDiskImage(false),
|
||||||
|
fHasComment(false),
|
||||||
|
fHasNonEmptyComment(false),
|
||||||
|
fDamaged(false),
|
||||||
|
fSuspicious(false),
|
||||||
|
fIndex(-1),
|
||||||
|
fpPrev(NULL),
|
||||||
|
fpNext(NULL)
|
||||||
{
|
{
|
||||||
fPathName = NULL;
|
|
||||||
fFileName = NULL;
|
|
||||||
fFssep = '\0';
|
|
||||||
fSubVolName = NULL;
|
|
||||||
fDisplayName = NULL;
|
|
||||||
fFileType = 0;
|
|
||||||
fAuxType = 0;
|
|
||||||
fAccess = 0;
|
|
||||||
fModWhen = kDateNone;
|
|
||||||
fCreateWhen = kDateNone;
|
|
||||||
fRecordKind = kRecordKindUnknown;
|
|
||||||
fFormatStr = L"Unknown";
|
|
||||||
fCompressedLen = 0;
|
|
||||||
//fUncompressedLen = 0;
|
|
||||||
fDataForkLen = fRsrcForkLen = 0;
|
|
||||||
|
|
||||||
fSourceFS = DiskImg::kFormatUnknown;
|
|
||||||
|
|
||||||
fHasDataFork = false;
|
|
||||||
fHasRsrcFork = false;
|
|
||||||
fHasDiskImage = false;
|
|
||||||
fHasComment = false;
|
|
||||||
fHasNonEmptyComment = false;
|
|
||||||
|
|
||||||
fIndex = -1;
|
|
||||||
fpPrev = NULL;
|
|
||||||
fpNext = NULL;
|
|
||||||
|
|
||||||
fDamaged = fSuspicious = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericEntry::~GenericEntry(void)
|
GenericEntry::~GenericEntry(void)
|
||||||
|
@ -177,7 +176,7 @@ const WCHAR* GenericEntry::GetFileTypeString(void) const
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer,
|
/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer,
|
||||||
unsigned long count)
|
size_t count)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* (Pulled from NufxLib Funnel.c.)
|
* (Pulled from NufxLib Funnel.c.)
|
||||||
|
@ -254,7 +253,7 @@ static const char kCharLF = '\n';
|
||||||
static const char kCharCR = '\r';
|
static const char kCharCR = '\r';
|
||||||
|
|
||||||
/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion(
|
/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion(
|
||||||
const uint8_t* buffer, long count,
|
const uint8_t* buffer, size_t count,
|
||||||
EOLType* pSourceType, ConvertHighASCII* pConvHA)
|
EOLType* pSourceType, ConvertHighASCII* pConvHA)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -277,7 +276,7 @@ static const char kCharCR = '\r';
|
||||||
* it will be stripped *before* the EOL determination is made.
|
* it will be stripped *before* the EOL determination is made.
|
||||||
*/
|
*/
|
||||||
ConvertHighASCII wantConvHA = *pConvHA;
|
ConvertHighASCII wantConvHA = *pConvHA;
|
||||||
long bufCount, numBinary, numLF, numCR;
|
size_t bufCount, numBinary, numLF, numCR;
|
||||||
bool isHighASCII;
|
bool isHighASCII;
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
|
|
||||||
|
@ -367,7 +366,7 @@ static inline void PutEOL(FILE* fp)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
LOGI("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
|
LOGD("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA);
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
LOGI("WriteConvert asked to write 0 bytes; returning");
|
LOGI("WriteConvert asked to write 0 bytes; returning");
|
||||||
|
@ -396,7 +395,7 @@ static inline void PutEOL(FILE* fp)
|
||||||
*pConvHA = kConvertHAOff;
|
*pConvHA = kConvertHAOff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOGI("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
|
LOGD("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA);
|
||||||
ASSERT(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff);
|
ASSERT(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff);
|
||||||
ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff);
|
ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff);
|
||||||
|
|
||||||
|
@ -404,7 +403,7 @@ static inline void PutEOL(FILE* fp)
|
||||||
if (*pConv == kConvertEOLOff) {
|
if (*pConv == kConvertEOLOff) {
|
||||||
if (fwrite(buf, len, 1, fp) != 1) {
|
if (fwrite(buf, len, 1, fp) != 1) {
|
||||||
err = errno;
|
err = errno;
|
||||||
LOGI("WriteConvert failed, err=%d", errno);
|
LOGE("WriteConvert failed, err=%d", errno);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ASSERT(*pConv == kConvertEOLOn);
|
ASSERT(*pConv == kConvertEOLOn);
|
||||||
|
@ -546,7 +545,7 @@ GenericArchive::CreateIndex(void)
|
||||||
mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */
|
mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */
|
||||||
mangle += kTmpTemplate;
|
mangle += kTmpTemplate;
|
||||||
}
|
}
|
||||||
LOGI("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
|
LOGD("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle);
|
||||||
|
|
||||||
return mangle;
|
return mangle;
|
||||||
}
|
}
|
||||||
|
@ -594,13 +593,9 @@ GenericArchive::CreateIndex(void)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This comes straight out of NuLib2, and uses NufxLib data structures. While
|
* Much of this was adapted from NuLib2.
|
||||||
* it may seem strange to use these structures for non-NuFX archives, they are
|
|
||||||
* convenient and hold at least as much information as any other format needs.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef bool Boolean;
|
|
||||||
|
|
||||||
/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen,
|
/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen,
|
||||||
NuDateTime* pDateTime)
|
NuDateTime* pDateTime)
|
||||||
{
|
{
|
||||||
|
@ -625,99 +620,6 @@ typedef bool Boolean;
|
||||||
pDateTime->weekDay = ptm->tm_wday +1;
|
pDateTime->weekDay = ptm->tm_wday +1;
|
||||||
}
|
}
|
||||||
|
|
||||||
NuError GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts,
|
|
||||||
const WCHAR* pathname, struct _stat* psb, FileDetails* pDetails)
|
|
||||||
{
|
|
||||||
//char* livePathStr;
|
|
||||||
time_t now;
|
|
||||||
|
|
||||||
ASSERT(pAddOpts != NULL);
|
|
||||||
ASSERT(pathname != NULL);
|
|
||||||
ASSERT(pDetails != NULL);
|
|
||||||
|
|
||||||
/* init to defaults */
|
|
||||||
//pDetails->threadID = kNuThreadIDDataFork;
|
|
||||||
pDetails->entryKind = FileDetails::kFileKindDataFork;
|
|
||||||
//pDetails->fileSysID = kNuFileSysUnknown;
|
|
||||||
pDetails->fileSysFmt = DiskImg::kFormatUnknown;
|
|
||||||
pDetails->fileSysInfo = PathProposal::kDefaultStoredFssep;
|
|
||||||
pDetails->fileType = 0;
|
|
||||||
pDetails->extraType = 0;
|
|
||||||
pDetails->storageType = kNuStorageUnknown; /* let NufxLib worry about it */
|
|
||||||
if (psb->st_mode & S_IWUSR)
|
|
||||||
pDetails->access = kNuAccessUnlocked;
|
|
||||||
else
|
|
||||||
pDetails->access = kNuAccessLocked;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* if this is a disk image, fill in disk-specific fields */
|
|
||||||
if (NState_GetModAddAsDisk(pState)) {
|
|
||||||
if ((psb->st_size & 0x1ff) != 0) {
|
|
||||||
/* reject anything whose size isn't a multiple of 512 bytes */
|
|
||||||
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
|
|
||||||
(long)psb->st_size, livePathStr);
|
|
||||||
} else {
|
|
||||||
/* set fields; note the "preserve" stuff can override this */
|
|
||||||
pDetails->threadID = kNuThreadIDDiskImage;
|
|
||||||
pDetails->storageType = 512;
|
|
||||||
pDetails->extraType = psb->st_size / 512;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
now = time(NULL);
|
|
||||||
UNIXTimeToDateTime(&now, &pDetails->archiveWhen);
|
|
||||||
UNIXTimeToDateTime(&psb->st_mtime, &pDetails->modWhen);
|
|
||||||
UNIXTimeToDateTime(&psb->st_ctime, &pDetails->createWhen);
|
|
||||||
|
|
||||||
/* get adjusted filename, along with any preserved type info */
|
|
||||||
PathProposal pathProp;
|
|
||||||
pathProp.Init(pathname);
|
|
||||||
pathProp.LocalToArchive(pAddOpts);
|
|
||||||
|
|
||||||
/* set up the local and archived pathnames */
|
|
||||||
pDetails->storageName = "";
|
|
||||||
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
|
|
||||||
pDetails->storageName += pAddOpts->fStoragePrefix;
|
|
||||||
pDetails->storageName += pathProp.fStoredFssep;
|
|
||||||
}
|
|
||||||
pDetails->storageName += pathProp.fStoredPathName;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fill in the NuFileDetails struct.
|
|
||||||
*
|
|
||||||
* We use GetBuffer to get the string to ensure that the CString object
|
|
||||||
* doesn't do anything while we're not looking. The string won't be
|
|
||||||
* modified, and it won't be around for very long, so it's not strictly
|
|
||||||
* necessary to do this. It is, however, the correct approach.
|
|
||||||
*/
|
|
||||||
pDetails->origName = pathname;
|
|
||||||
pDetails->fileSysInfo = pathProp.fStoredFssep;
|
|
||||||
pDetails->fileType = pathProp.fFileType;
|
|
||||||
pDetails->extraType = pathProp.fAuxType;
|
|
||||||
switch (pathProp.fThreadKind) {
|
|
||||||
case GenericEntry::kDataThread:
|
|
||||||
//pDetails->threadID = kNuThreadIDDataFork;
|
|
||||||
pDetails->entryKind = FileDetails::kFileKindDataFork;
|
|
||||||
break;
|
|
||||||
case GenericEntry::kRsrcThread:
|
|
||||||
//pDetails->threadID = kNuThreadIDRsrcFork;
|
|
||||||
pDetails->entryKind = FileDetails::kFileKindRsrcFork;
|
|
||||||
break;
|
|
||||||
case GenericEntry::kDiskImageThread:
|
|
||||||
//pDetails->threadID = kNuThreadIDDiskImage;
|
|
||||||
pDetails->entryKind = FileDetails::kFileKindDiskImage;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ASSERT(false);
|
|
||||||
// was initialized to default earlier
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*bail:*/
|
|
||||||
return kNuErrNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Directory structure and functions, based on zDIR in Info-Zip sources.
|
* Directory structure and functions, based on zDIR in Info-Zip sources.
|
||||||
*/
|
*/
|
||||||
|
@ -864,8 +766,8 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
|
||||||
const WCHAR* pathname, CString* pErrMsg)
|
const WCHAR* pathname, CString* pErrMsg)
|
||||||
{
|
{
|
||||||
NuError err = kNuErrNone;
|
NuError err = kNuErrNone;
|
||||||
Boolean exists, isDir, isReadable;
|
bool exists, isDir, isReadable;
|
||||||
FileDetails details;
|
LocalFileDetails details;
|
||||||
struct _stat sb;
|
struct _stat sb;
|
||||||
|
|
||||||
ASSERT(pAddOpts != NULL);
|
ASSERT(pAddOpts != NULL);
|
||||||
|
@ -901,18 +803,16 @@ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts,
|
||||||
* filetype and auxtype it has, and whether or not it's actually the
|
* filetype and auxtype it has, and whether or not it's actually the
|
||||||
* resource fork of another file.
|
* resource fork of another file.
|
||||||
*/
|
*/
|
||||||
LOGI("+++ ADD '%ls'", pathname);
|
LOGD("+++ ADD '%ls'", pathname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill out the "details" structure. The class has an automatic
|
* Fill out the "details" structure.
|
||||||
* conversion to NuFileDetails, but it relies on the CString storage
|
|
||||||
* in the FileDetails, so be careful how you use it.
|
|
||||||
*/
|
*/
|
||||||
err = GetFileDetails(pAddOpts, pathname, &sb, &details);
|
err = details.SetFields(pAddOpts, pathname, &sb);
|
||||||
if (err != kNuErrNone)
|
if (err != kNuErrNone)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
assert(wcscmp(pathname, details.origName) == 0);
|
assert(wcscmp(pathname, details.GetLocalPathName()) == 0);
|
||||||
err = DoAddFile(pAddOpts, &details);
|
err = DoAddFile(pAddOpts, &details);
|
||||||
if (err == kNuErrSkipped) // ignore "skipped" result
|
if (err == kNuErrSkipped) // ignore "skipped" result
|
||||||
err = kNuErrNone;
|
err = kNuErrNone;
|
||||||
|
@ -934,80 +834,144 @@ NuError GenericArchive::AddFile(const AddFilesDialog* pAddOpts,
|
||||||
return Win32AddFile(pAddOpts, pathname, pErrMsg);
|
return Win32AddFile(pAddOpts, pathname, pErrMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ===========================================================================
|
* ===========================================================================
|
||||||
* GenericArchive::FileDetails
|
* GenericArchive::FileDetails
|
||||||
* ===========================================================================
|
* ===========================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GenericArchive::FileDetails::FileDetails(void)
|
GenericArchive::LocalFileDetails::LocalFileDetails(void)
|
||||||
|
: fEntryKind(kFileKindUnknown),
|
||||||
|
fFileSysFmt(DiskImg::kFormatUnknown),
|
||||||
|
fFssep('\0'),
|
||||||
|
fFileType(0),
|
||||||
|
fExtraType(0),
|
||||||
|
fAccess(0),
|
||||||
|
fStorageType(0)
|
||||||
{
|
{
|
||||||
//threadID = 0;
|
memset(&fCreateWhen, 0, sizeof(fCreateWhen));
|
||||||
entryKind = kFileKindUnknown;
|
memset(&fModWhen, 0, sizeof(fModWhen));
|
||||||
fileSysFmt = DiskImg::kFormatUnknown;
|
memset(&fArchiveWhen, 0, sizeof(fArchiveWhen));
|
||||||
fileSysInfo = storageType = 0;
|
|
||||||
access = fileType = extraType = 0;
|
// set these for debugging
|
||||||
memset(&createWhen, 0, sizeof(createWhen));
|
memset(&fNuFileDetails, 0xcc, sizeof(fNuFileDetails));
|
||||||
memset(&modWhen, 0, sizeof(modWhen));
|
memset(&fCreateParms, 0xcc, sizeof(&fCreateParms));
|
||||||
memset(&archiveWhen, 0, sizeof(archiveWhen));
|
fStoragePathNameMOR = "!INIT!";
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericArchive::FileDetails::operator const NuFileDetails() const
|
NuError GenericArchive::LocalFileDetails::SetFields(const AddFilesDialog* pAddOpts,
|
||||||
|
const WCHAR* pathname, struct _stat* psb)
|
||||||
{
|
{
|
||||||
NuFileDetails details;
|
time_t now;
|
||||||
|
|
||||||
//details.threadID = threadID;
|
ASSERT(pAddOpts != NULL);
|
||||||
switch (entryKind) {
|
ASSERT(pathname != NULL);
|
||||||
case kFileKindDataFork:
|
|
||||||
details.threadID = kNuThreadIDDataFork;
|
/* get adjusted filename, along with any preserved type info */
|
||||||
|
PathProposal pathProp;
|
||||||
|
pathProp.Init(pathname);
|
||||||
|
pathProp.LocalToArchive(pAddOpts);
|
||||||
|
|
||||||
|
/* set up the local and archived pathnames */
|
||||||
|
fLocalPathName = pathname;
|
||||||
|
fStrippedLocalPathName = L"";
|
||||||
|
if (!pAddOpts->fStoragePrefix.IsEmpty()) {
|
||||||
|
fStrippedLocalPathName += pAddOpts->fStoragePrefix;
|
||||||
|
fStrippedLocalPathName += pathProp.fStoredFssep;
|
||||||
|
}
|
||||||
|
fStrippedLocalPathName += pathProp.fStoredPathName;
|
||||||
|
GenerateStoragePathName();
|
||||||
|
|
||||||
|
fFileSysFmt = DiskImg::kFormatUnknown;
|
||||||
|
fStorageType = kNuStorageUnknown; /* let NufxLib et.al. worry about it */
|
||||||
|
if (psb->st_mode & S_IWUSR)
|
||||||
|
fAccess = kNuAccessUnlocked;
|
||||||
|
else
|
||||||
|
fAccess = kNuAccessLocked;
|
||||||
|
fEntryKind = LocalFileDetails::kFileKindDataFork;
|
||||||
|
fFssep = pathProp.fStoredFssep;
|
||||||
|
fFileType = pathProp.fFileType;
|
||||||
|
fExtraType = pathProp.fAuxType;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* if this is a disk image, fill in disk-specific fields */
|
||||||
|
if (NState_GetModAddAsDisk(pState)) {
|
||||||
|
if ((psb->st_size & 0x1ff) != 0) {
|
||||||
|
/* reject anything whose size isn't a multiple of 512 bytes */
|
||||||
|
printf("NOT storing odd-sized (%ld) file as disk image: %ls\n",
|
||||||
|
(long)psb->st_size, livePathStr);
|
||||||
|
} else {
|
||||||
|
/* set fields; note the "preserve" stuff can override this */
|
||||||
|
pDetails->threadID = kNuThreadIDDiskImage;
|
||||||
|
pDetails->storageType = 512;
|
||||||
|
pDetails->extraType = psb->st_size / 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
UNIXTimeToDateTime(&now, &fArchiveWhen);
|
||||||
|
UNIXTimeToDateTime(&psb->st_mtime, &fModWhen);
|
||||||
|
UNIXTimeToDateTime(&psb->st_ctime, &fCreateWhen);
|
||||||
|
|
||||||
|
switch (pathProp.fThreadKind) {
|
||||||
|
case GenericEntry::kDataThread:
|
||||||
|
//pDetails->threadID = kNuThreadIDDataFork;
|
||||||
|
fEntryKind = LocalFileDetails::kFileKindDataFork;
|
||||||
break;
|
break;
|
||||||
case kFileKindBothForks: // not exactly supported, doesn't really matter
|
case GenericEntry::kRsrcThread:
|
||||||
case kFileKindRsrcFork:
|
//pDetails->threadID = kNuThreadIDRsrcFork;
|
||||||
details.threadID = kNuThreadIDRsrcFork;
|
fEntryKind = LocalFileDetails::kFileKindRsrcFork;
|
||||||
break;
|
break;
|
||||||
case kFileKindDiskImage:
|
case GenericEntry::kDiskImageThread:
|
||||||
details.threadID = kNuThreadIDDiskImage;
|
//pDetails->threadID = kNuThreadIDDiskImage;
|
||||||
|
fEntryKind = LocalFileDetails::kFileKindDiskImage;
|
||||||
break;
|
break;
|
||||||
case kFileKindDirectory:
|
|
||||||
default:
|
default:
|
||||||
LOGW("Invalid entryKind (%d) for NuFileDetails conversion",
|
|
||||||
entryKind);
|
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
details.threadID = 0; // that makes it an old-style comment?!
|
// was initialized to default earlier
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The NuFileDetails origName field was added to NufxLib so that CiderPress
|
/*bail:*/
|
||||||
// could receive the original Windows pathname in the NuErrorStatus
|
return kNuErrNone;
|
||||||
// callback. This is a fairly compelling argument for making origName
|
}
|
||||||
// an opaque field.
|
|
||||||
//
|
|
||||||
// Whatever the case, we need to pass narrow-string versions of the
|
|
||||||
// names into NufxLib via the NuFileDetails struct. Since we're returning
|
|
||||||
// the NuFileDetails struct, the strings will out-live the scope of this
|
|
||||||
// function, so we have to store them somewhere else. There's nowhere
|
|
||||||
// in NuFileDetails, so we have to keep them in FileDetails, which currently
|
|
||||||
// exposes all of its fields publicly. I'm going to kluge it for now and
|
|
||||||
// just "snapshot" the wide strings into narrow-string fields. This is
|
|
||||||
// all pretty rickety and needs to be redone.
|
|
||||||
|
|
||||||
// TODO: make origName an opaque (void*) field in NufxLib and pass the
|
const NuFileDetails& GenericArchive::LocalFileDetails::GetNuFileDetails()
|
||||||
// wide char representation through
|
{
|
||||||
fOrigNameA = origName;
|
//details.threadID = threadID;
|
||||||
details.origName = fOrigNameA;
|
switch (fEntryKind) {
|
||||||
fStorageNameA = storageName;
|
case kFileKindDataFork:
|
||||||
details.storageNameMOR = fStorageNameA;
|
fNuFileDetails.threadID = kNuThreadIDDataFork;
|
||||||
//details.fileSysID = fileSysID;
|
break;
|
||||||
details.fileSysInfo = fileSysInfo;
|
case kFileKindBothForks: // not exactly supported, doesn't really matter
|
||||||
details.access = access;
|
case kFileKindRsrcFork:
|
||||||
details.fileType = fileType;
|
fNuFileDetails.threadID = kNuThreadIDRsrcFork;
|
||||||
details.extraType = extraType;
|
break;
|
||||||
details.storageType = storageType;
|
case kFileKindDiskImage:
|
||||||
details.createWhen = createWhen;
|
fNuFileDetails.threadID = kNuThreadIDDiskImage;
|
||||||
details.modWhen = modWhen;
|
break;
|
||||||
details.archiveWhen = archiveWhen;
|
case kFileKindDirectory:
|
||||||
|
default:
|
||||||
|
LOGW("Invalid entryKind (%d) for NuFileDetails conversion", fEntryKind);
|
||||||
|
ASSERT(false);
|
||||||
|
fNuFileDetails.threadID = 0; // that makes it an old-style comment?!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (fileSysFmt) {
|
fNuFileDetails.origName = (LPCWSTR) fLocalPathName;
|
||||||
|
fNuFileDetails.storageNameMOR = (LPCSTR) fStoragePathNameMOR;
|
||||||
|
fNuFileDetails.fileSysInfo = fFssep;
|
||||||
|
fNuFileDetails.access = fAccess;
|
||||||
|
fNuFileDetails.fileType = fFileType;
|
||||||
|
fNuFileDetails.extraType = fExtraType;
|
||||||
|
fNuFileDetails.storageType = fStorageType;
|
||||||
|
fNuFileDetails.createWhen = fCreateWhen;
|
||||||
|
fNuFileDetails.modWhen = fModWhen;
|
||||||
|
fNuFileDetails.archiveWhen = fArchiveWhen;
|
||||||
|
|
||||||
|
switch (fFileSysFmt) {
|
||||||
case DiskImg::kFormatProDOS:
|
case DiskImg::kFormatProDOS:
|
||||||
case DiskImg::kFormatDOS33:
|
case DiskImg::kFormatDOS33:
|
||||||
case DiskImg::kFormatDOS32:
|
case DiskImg::kFormatDOS32:
|
||||||
|
@ -1021,42 +985,65 @@ GenericArchive::FileDetails::operator const NuFileDetails() const
|
||||||
//kFormatHighSierra
|
//kFormatHighSierra
|
||||||
case DiskImg::kFormatISO9660:
|
case DiskImg::kFormatISO9660:
|
||||||
/* these map directly */
|
/* these map directly */
|
||||||
details.fileSysID = (enum NuFileSysID) fileSysFmt;
|
fNuFileDetails.fileSysID = (enum NuFileSysID) fFileSysFmt;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DiskImg::kFormatRDOS33:
|
case DiskImg::kFormatRDOS33:
|
||||||
case DiskImg::kFormatRDOS32:
|
case DiskImg::kFormatRDOS32:
|
||||||
case DiskImg::kFormatRDOS3:
|
case DiskImg::kFormatRDOS3:
|
||||||
/* these look like DOS33, e.g. text is high-ASCII */
|
/* these look like DOS33, e.g. text is high-ASCII */
|
||||||
details.fileSysID = kNuFileSysDOS33;
|
fNuFileDetails.fileSysID = kNuFileSysDOS33;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
details.fileSysID = kNuFileSysUnknown;
|
fNuFileDetails.fileSysID = kNuFileSysUnknown;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return stack copy, which copies into compiler temporary with our
|
return fNuFileDetails;
|
||||||
// copy constructor.
|
|
||||||
return details;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ void GenericArchive::FileDetails::CopyFields(FileDetails* pDst,
|
const DiskFS::CreateParms& GenericArchive::LocalFileDetails::GetCreateParms()
|
||||||
const FileDetails* pSrc)
|
|
||||||
{
|
{
|
||||||
//pDst->threadID = pSrc->threadID;
|
fCreateParms.pathName = (LPCSTR) fStoragePathNameMOR;
|
||||||
pDst->entryKind = pSrc->entryKind;
|
fCreateParms.fssep = fFssep;
|
||||||
pDst->origName = pSrc->origName;
|
fCreateParms.storageType = fStorageType;
|
||||||
pDst->storageName = pSrc->storageName;
|
fCreateParms.fileType = fFileType;
|
||||||
pDst->fileSysFmt = pSrc->fileSysFmt;
|
fCreateParms.auxType = fExtraType;
|
||||||
pDst->fileSysInfo = pSrc->fileSysInfo;
|
fCreateParms.access = fAccess;
|
||||||
pDst->access = pSrc->access;
|
fCreateParms.createWhen = NufxArchive::DateTimeToSeconds(&fCreateWhen);
|
||||||
pDst->fileType = pSrc->fileType;
|
fCreateParms.modWhen = NufxArchive::DateTimeToSeconds(&fModWhen);
|
||||||
pDst->extraType = pSrc->extraType;
|
return fCreateParms;
|
||||||
pDst->storageType = pSrc->storageType;
|
}
|
||||||
pDst->createWhen = pSrc->createWhen;
|
|
||||||
pDst->modWhen = pSrc->modWhen;
|
void GenericArchive::LocalFileDetails::GenerateStoragePathName()
|
||||||
pDst->archiveWhen = pSrc->archiveWhen;
|
{
|
||||||
|
// TODO(Unicode): generate MOR name from Unicode, instead of just
|
||||||
|
// doing a generic CP-1252 conversion. We need to do this on both
|
||||||
|
// sides though, so until we can extract MOR->Unicode we don't
|
||||||
|
// want to add Unicode->MOR. And it all depends on NufxLib and
|
||||||
|
// DiskImg being able to handle UTF-16 filenames.
|
||||||
|
fStoragePathNameMOR = fStrippedLocalPathName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void GenericArchive::LocalFileDetails::CopyFields(LocalFileDetails* pDst,
|
||||||
|
const LocalFileDetails* pSrc)
|
||||||
|
{
|
||||||
|
// don't copy fNuFileDetails, fCreateParms
|
||||||
|
pDst->fEntryKind = pSrc->fEntryKind;
|
||||||
|
pDst->fLocalPathName = pSrc->fLocalPathName;
|
||||||
|
pDst->fStrippedLocalPathName = pSrc->fStrippedLocalPathName;
|
||||||
|
pDst->fStoragePathNameMOR = pSrc->fStoragePathNameMOR;
|
||||||
|
pDst->fFileSysFmt = pSrc->fFileSysFmt;
|
||||||
|
pDst->fFssep = pSrc->fFssep;
|
||||||
|
pDst->fAccess = pSrc->fAccess;
|
||||||
|
pDst->fFileType = pSrc->fFileType;
|
||||||
|
pDst->fExtraType = pSrc->fExtraType;
|
||||||
|
pDst->fStorageType = pSrc->fStorageType;
|
||||||
|
pDst->fCreateWhen = pSrc->fCreateWhen;
|
||||||
|
pDst->fModWhen = pSrc->fModWhen;
|
||||||
|
pDst->fArchiveWhen = pSrc->fArchiveWhen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1073,7 +1060,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
|
||||||
* (at least under Win2K), which is a good thing. It appears that, if you
|
* (at least under Win2K), which is a good thing. It appears that, if you
|
||||||
* just grab indices 0..N, you will get them in order.
|
* just grab indices 0..N, you will get them in order.
|
||||||
*/
|
*/
|
||||||
LOGI("CreateFromSelection (threadMask=0x%02x)", threadMask);
|
LOGD("CreateFromSelection (threadMask=0x%02x)", threadMask);
|
||||||
|
|
||||||
POSITION posn;
|
POSITION posn;
|
||||||
posn = pContentList->GetFirstSelectedItemPosition();
|
posn = pContentList->GetFirstSelectedItemPosition();
|
||||||
|
@ -1090,7 +1077,7 @@ void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask
|
||||||
|
|
||||||
void SelectionSet::CreateFromAll(ContentList* pContentList, int threadMask)
|
void SelectionSet::CreateFromAll(ContentList* pContentList, int threadMask)
|
||||||
{
|
{
|
||||||
LOGI("CreateFromAll (threadMask=0x%02x)", threadMask);
|
LOGD("CreateFromAll (threadMask=0x%02x)", threadMask);
|
||||||
|
|
||||||
int count = pContentList->GetItemCount();
|
int count = pContentList->GetItemCount();
|
||||||
for (int idx = 0; idx < count; idx++) {
|
for (int idx = 0; idx < count; idx++) {
|
||||||
|
@ -1104,7 +1091,7 @@ void SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask)
|
||||||
{
|
{
|
||||||
SelectionEntry* pSelEntry;
|
SelectionEntry* pSelEntry;
|
||||||
|
|
||||||
//LOGI(" Sel '%ls'", pEntry->GetPathName());
|
LOGV(" Sel '%ls'", pEntry->GetPathName());
|
||||||
|
|
||||||
if (!(threadMask & GenericEntry::kAllowVolumeDir) &&
|
if (!(threadMask & GenericEntry::kAllowVolumeDir) &&
|
||||||
pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)
|
pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)
|
||||||
|
@ -1173,7 +1160,7 @@ void SelectionSet::DeleteEntries(void)
|
||||||
SelectionEntry* pEntry;
|
SelectionEntry* pEntry;
|
||||||
SelectionEntry* pNext;
|
SelectionEntry* pNext;
|
||||||
|
|
||||||
LOGI("Deleting selection entries");
|
LOGD("Deleting selection entries");
|
||||||
|
|
||||||
pEntry = GetEntries();
|
pEntry = GetEntries();
|
||||||
while (pEntry != NULL) {
|
while (pEntry != NULL) {
|
||||||
|
|
|
@ -221,7 +221,6 @@ public:
|
||||||
LONGLONG GetUncompressedLen(void) const {
|
LONGLONG GetUncompressedLen(void) const {
|
||||||
return fDataForkLen + fRsrcForkLen;
|
return fDataForkLen + fRsrcForkLen;
|
||||||
}
|
}
|
||||||
//void SetUncompressedLen(LONGLONG len) { fUncompressedLen = len; }
|
|
||||||
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
|
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
|
||||||
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
|
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
|
||||||
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
|
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
|
||||||
|
@ -259,8 +258,7 @@ public:
|
||||||
/*
|
/*
|
||||||
* Check to see if this is a high-ASCII file.
|
* Check to see if this is a high-ASCII file.
|
||||||
*/
|
*/
|
||||||
static bool CheckHighASCII(const uint8_t* buffer,
|
static bool CheckHighASCII(const uint8_t* buffer, size_t count);
|
||||||
unsigned long count);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decide, based on the contents of the buffer, whether we should do an
|
* Decide, based on the contents of the buffer, whether we should do an
|
||||||
|
@ -269,7 +267,7 @@ public:
|
||||||
* Returns kConvEOLOff or kConvEOLOn.
|
* Returns kConvEOLOff or kConvEOLOn.
|
||||||
*/
|
*/
|
||||||
static ConvertEOL DetermineConversion(const uint8_t* buffer,
|
static ConvertEOL DetermineConversion(const uint8_t* buffer,
|
||||||
long count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
|
size_t count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write data to a file, possibly converting EOL markers to Windows CRLF
|
* Write data to a file, possibly converting EOL markers to Windows CRLF
|
||||||
|
@ -309,7 +307,6 @@ private:
|
||||||
time_t fModWhen;
|
time_t fModWhen;
|
||||||
RecordKind fRecordKind; // forked file, disk image, ??
|
RecordKind fRecordKind; // forked file, disk image, ??
|
||||||
const WCHAR* fFormatStr; // static str; compression or fs format
|
const WCHAR* fFormatStr; // static str; compression or fs format
|
||||||
//LONGLONG fUncompressedLen;
|
|
||||||
LONGLONG fDataForkLen; // also for disk images
|
LONGLONG fDataForkLen; // also for disk images
|
||||||
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
|
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
|
||||||
LONGLONG fCompressedLen; // data/disk + rsrc
|
LONGLONG fCompressedLen; // data/disk + rsrc
|
||||||
|
@ -515,111 +512,202 @@ public:
|
||||||
void AddEntry(GenericEntry* pEntry);
|
void AddEntry(GenericEntry* pEntry);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This class holds details about a file that we're adding.
|
* This class holds details about a file that we're adding from local disk.
|
||||||
*
|
|
||||||
* It's based on the NuFileDetails class from NufxLib (which used to be
|
|
||||||
* used everywhere).
|
|
||||||
*
|
|
||||||
* TODO: fix this
|
|
||||||
* TODO: may want to hold a raw 8-bit pathname for copy & paste, which
|
|
||||||
* doesn't need to convert in and out of MacRoman
|
|
||||||
*/
|
*/
|
||||||
class FileDetails {
|
class LocalFileDetails {
|
||||||
public:
|
public:
|
||||||
FileDetails(void);
|
LocalFileDetails(void);
|
||||||
virtual ~FileDetails(void) {}
|
virtual ~LocalFileDetails(void) {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Automatic cast to NuFileDetails. The NuFileDetails structure will
|
* Set the various fields, based on the pathname and characteristics
|
||||||
* have a pointer to at least one of our strings, so structures
|
* of the file.
|
||||||
* filled out this way need to be short-lived. (Yes, this is
|
|
||||||
* annoying, but it's how NufxLib works.)
|
|
||||||
*
|
|
||||||
* TODO: make this a "GenNuFileDetails" method that returns a
|
|
||||||
* NuFileDetails struct owned by this object. It fills out the
|
|
||||||
* fields and references internal strings. Because we own the
|
|
||||||
* object, we can control the lifespan of the NuFileDetails, and
|
|
||||||
* ensure it doesn't live longer than our strings.
|
|
||||||
*/
|
*/
|
||||||
operator const NuFileDetails() const;
|
NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
||||||
|
struct _stat* psb);
|
||||||
/*
|
|
||||||
* Provide operator= and copy constructor. This'd be easier without
|
|
||||||
* the strings.
|
|
||||||
*/
|
|
||||||
FileDetails& operator=(const FileDetails& src) {
|
|
||||||
if (&src != this)
|
|
||||||
CopyFields(this, &src);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
FileDetails(const FileDetails& src) {
|
|
||||||
CopyFields(this, &src);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* What kind of file this is. Files being added to NuFX from Windows
|
* What kind of file this is. Files being added to NuFX from Windows
|
||||||
* can't be "BothForks" because each the forks are stored in
|
* can't be "BothForks" because the forks are stored in
|
||||||
* separate files. However, files being transferred from a NuFX
|
* separate files. However, files being transferred from a NuFX
|
||||||
* archive, a disk image, or in from the clipboard can be both.
|
* archive, a disk image, or in from the clipboard can be both.
|
||||||
*
|
*
|
||||||
* (NOTE: this gets embedded into clipboard data. If you change
|
* (NOTE: this gets embedded into clipboard data. If you change
|
||||||
* these values, update the version info in Clipboard.cpp.)
|
* these values, update the version number in Clipboard.cpp.)
|
||||||
*/
|
*/
|
||||||
typedef enum FileKind {
|
enum FileKind {
|
||||||
kFileKindUnknown = 0,
|
kFileKindUnknown = 0,
|
||||||
kFileKindDataFork,
|
kFileKindDataFork,
|
||||||
kFileKindRsrcFork,
|
kFileKindRsrcFork,
|
||||||
kFileKindDiskImage,
|
kFileKindDiskImage,
|
||||||
kFileKindBothForks,
|
kFileKindBothForks,
|
||||||
kFileKindDirectory,
|
kFileKindDirectory,
|
||||||
} FileKind;
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Data fields. While transitioning from general use of NuFileDetails
|
* Provide operator= and copy constructor.
|
||||||
* (v1.2.x to v2.0) I'm just going to leave these public.
|
|
||||||
* TODO: make this not public
|
|
||||||
*/
|
*/
|
||||||
|
LocalFileDetails& operator=(const LocalFileDetails& src) {
|
||||||
//NuThreadID threadID; /* data, rsrc, disk img? */
|
if (&src != this)
|
||||||
FileKind entryKind;
|
CopyFields(this, &src);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
LocalFileDetails(const LocalFileDetails& src) {
|
||||||
|
CopyFields(this, &src);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Original full pathname as found on Windows.
|
* Returns a reference to a NuFileDetails structure with the contents
|
||||||
*/
|
* of the LocalFileDetails.
|
||||||
CString origName;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "Normalized" pathname. This is the full path with any of our
|
|
||||||
* added bits removed (e.g. file type & fork identifiers). It has
|
|
||||||
* not been sanitized for any specific target filesystem.
|
|
||||||
*
|
*
|
||||||
* This is generated by PathProposal::LocalToArchive().
|
* The returned structure may not be used after the LocalFileDetails
|
||||||
|
* is modified or released.
|
||||||
*/
|
*/
|
||||||
CString storageName;
|
const NuFileDetails& GetNuFileDetails();
|
||||||
|
|
||||||
//NuFileSysID fileSysID;
|
/*
|
||||||
DiskImg::FSFormat fileSysFmt;
|
* Returns a reference to a NuFileDetails structure with the contents
|
||||||
uint16_t fileSysInfo; /* fssep lurks here */
|
* of the LocalFileDetails.
|
||||||
uint32_t access;
|
*
|
||||||
uint32_t fileType;
|
* The returned structure may not be used after the LocalFileDetails
|
||||||
uint32_t extraType;
|
* is modified or released.
|
||||||
uint16_t storageType; /* "Unknown" or disk block size */
|
*/
|
||||||
NuDateTime createWhen;
|
const DiskFS::CreateParms& GetCreateParms();
|
||||||
NuDateTime modWhen;
|
|
||||||
NuDateTime archiveWhen;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the "local" pathname, i.e. the name of a Windows file.
|
||||||
|
*/
|
||||||
|
const CString& GetLocalPathName() const {
|
||||||
|
return fLocalPathName;
|
||||||
|
}
|
||||||
|
|
||||||
// temporary kluge to get things working
|
void SetLocalPathName(const CString& newName) {
|
||||||
mutable CStringA fOrigNameA;
|
fLocalPathName = newName;
|
||||||
mutable CStringA fStorageNameA;
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the "local" pathname with any file type preservation
|
||||||
|
* strings removed.
|
||||||
|
*/
|
||||||
|
const CString& GetStrippedLocalPathName() const {
|
||||||
|
return fStrippedLocalPathName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the "stripped" local path name, and updates the MOR version.
|
||||||
|
* Does not alter the full local path name.
|
||||||
|
*/
|
||||||
|
void SetStrippedLocalPathName(const CString& newName) {
|
||||||
|
fStrippedLocalPathName = newName;
|
||||||
|
GenerateStoragePathName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a copy of the stripped local pathname that has been
|
||||||
|
* converted to Mac OS Roman.
|
||||||
|
*
|
||||||
|
* The returned string will be invalid if SetStrippedLocalPathName
|
||||||
|
* is subsequently called.
|
||||||
|
*/
|
||||||
|
const CStringA& GetStoragePathNameMOR() const {
|
||||||
|
return fStoragePathNameMOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileKind GetEntryKind() const { return fEntryKind; }
|
||||||
|
void SetEntryKind(FileKind kind) { fEntryKind = kind; }
|
||||||
|
|
||||||
|
DiskImg::FSFormat GetFileSysFmt() const { return fFileSysFmt; }
|
||||||
|
void SetFileSysFmt(DiskImg::FSFormat fmt) { fFileSysFmt = fmt; }
|
||||||
|
|
||||||
|
char GetFssep() const { return fFssep; }
|
||||||
|
void SetFssep(char fssep) { fFssep = fssep; }
|
||||||
|
|
||||||
|
uint32_t GetFileType() const { return fFileType; }
|
||||||
|
void SetFileType(uint32_t type) { fFileType = type; }
|
||||||
|
|
||||||
|
uint32_t GetExtraType() const { return fExtraType; }
|
||||||
|
void SetExtraType(uint32_t type) { fExtraType = type; }
|
||||||
|
|
||||||
|
uint32_t GetAccess() const { return fAccess; }
|
||||||
|
void SetAccess(uint32_t access) { fAccess = access; }
|
||||||
|
|
||||||
|
uint16_t GetStorageType() const { return fStorageType; }
|
||||||
|
void SetStorageType(uint16_t type) { fStorageType = type; }
|
||||||
|
|
||||||
|
const NuDateTime& GetArchiveWhen() const { return fArchiveWhen; }
|
||||||
|
void SetArchiveWhen(const NuDateTime& when) { fArchiveWhen = when; }
|
||||||
|
|
||||||
|
const NuDateTime& GetModWhen() const { return fModWhen; }
|
||||||
|
void SetModWhen(const NuDateTime& when) { fModWhen = when; }
|
||||||
|
|
||||||
|
const NuDateTime& GetCreateWhen() const { return fCreateWhen; }
|
||||||
|
void SetCreateWhen(const NuDateTime& when) { fCreateWhen = when; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* Copy the contents of our object to a new object.
|
* Copy the contents of our object to a new object, field by field,
|
||||||
|
* so the CStrings copy correctly.
|
||||||
*
|
*
|
||||||
* Useful for operator= and copy construction.
|
* Useful for operator= and copy construction.
|
||||||
*/
|
*/
|
||||||
static void CopyFields(FileDetails* pDst, const FileDetails* pSrc);
|
static void CopyFields(LocalFileDetails* pDst,
|
||||||
|
const LocalFileDetails* pSrc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates fStoragePathNameMOR from fStrippedLocalPathName.
|
||||||
|
*/
|
||||||
|
void GenerateStoragePathName();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are the structs that the libraries want, so we provide calls
|
||||||
|
* that populate them and return a reference. These are "hairy"
|
||||||
|
* structures, so we have to place limitations on their lifetime.
|
||||||
|
*
|
||||||
|
* Ideally these would either be proper objects with destructors,
|
||||||
|
* so we could create them and not worry about who will free up the
|
||||||
|
* hairy bits, or we would omit the hairy bits from the structure and
|
||||||
|
* pass them separately.
|
||||||
|
*/
|
||||||
|
NuFileDetails fNuFileDetails;
|
||||||
|
DiskFS::CreateParms fCreateParms;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* What does this entry represent?
|
||||||
|
*/
|
||||||
|
FileKind fEntryKind;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Full pathname of the Windows file.
|
||||||
|
*/
|
||||||
|
CString fLocalPathName; // was origName
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "Stripped" pathname. This is the full path with any of our
|
||||||
|
* added bits removed (e.g. file type & fork identifiers).
|
||||||
|
*
|
||||||
|
* This is generated by PathProposal::LocalToArchive().
|
||||||
|
*/
|
||||||
|
CString fStrippedLocalPathName; // was storageName
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The storage name, generated by converting strippedLocalPathName
|
||||||
|
* from Unicode to Mac OS Roman. This is what will be passed to
|
||||||
|
* DiskImg and NufxLib as the name of the file to create in the
|
||||||
|
* archive. For DiskImg there's an additional "normalization" pass
|
||||||
|
* make the filename suitable for use on the filesystem; that name
|
||||||
|
* is stored in a FileAddData object, not here.
|
||||||
|
*/
|
||||||
|
CStringA fStoragePathNameMOR;
|
||||||
|
|
||||||
|
DiskImg::FSFormat fFileSysFmt; // only set for xfers?
|
||||||
|
uint8_t fFssep;
|
||||||
|
uint32_t fFileType;
|
||||||
|
uint32_t fExtraType;
|
||||||
|
uint32_t fAccess;
|
||||||
|
uint16_t fStorageType; // "Unknown" or disk block size
|
||||||
|
NuDateTime fCreateWhen;
|
||||||
|
NuDateTime fModWhen;
|
||||||
|
NuDateTime fArchiveWhen;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare for file transfers.
|
// Prepare for file transfers.
|
||||||
|
@ -634,7 +722,7 @@ public:
|
||||||
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
|
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
|
||||||
// necessary for the interface to work this way because the NufxArchive
|
// necessary for the interface to work this way because the NufxArchive
|
||||||
// version just tucks the pointers into NufxLib structures.)
|
// version just tucks the pointers into NufxLib structures.)
|
||||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
|
||||||
|
|
||||||
// Abort progress. Not all subclasses are capable of "undo".
|
// Abort progress. Not all subclasses are capable of "undo".
|
||||||
|
@ -656,16 +744,6 @@ protected:
|
||||||
|
|
||||||
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst);
|
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst);
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the contents of a NuFileDetails structure, based on the pathname
|
|
||||||
* and characteristics of the file.
|
|
||||||
*
|
|
||||||
* For efficiency and simplicity, the pathname fields are set to CStrings in
|
|
||||||
* the GenericArchive object instead of newly-allocated storage.
|
|
||||||
*/
|
|
||||||
NuError GetFileDetails(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
|
||||||
struct _stat* psb, FileDetails* pDetails);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare a directory for reading.
|
* Prepare a directory for reading.
|
||||||
*
|
*
|
||||||
|
@ -725,7 +803,7 @@ protected:
|
||||||
* information in "*pDetails" may be modified.
|
* information in "*pDetails" may be modified.
|
||||||
*/
|
*/
|
||||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails) = 0;
|
LocalFileDetails* pDetails) = 0;
|
||||||
|
|
||||||
void SetPathName(const WCHAR* pathName) {
|
void SetPathName(const WCHAR* pathName) {
|
||||||
free(fPathName);
|
free(fPathName);
|
||||||
|
|
24
app/Main.cpp
24
app/Main.cpp
|
@ -1844,7 +1844,7 @@ int MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension,
|
||||||
GenericArchive::OpenResult openResult;
|
GenericArchive::OpenResult openResult;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
GenericArchive* pOpenArchive = NULL;
|
GenericArchive* pOpenArchive = NULL;
|
||||||
int origFilterIndex = filterIndex;
|
const int origFilterIndex = filterIndex;
|
||||||
CString errStr, appName;
|
CString errStr, appName;
|
||||||
|
|
||||||
CheckedLoadString(&appName, IDS_MB_APP_NAME);
|
CheckedLoadString(&appName, IDS_MB_APP_NAME);
|
||||||
|
@ -1883,7 +1883,7 @@ try_again:
|
||||||
if (filterIndex == kFilterIndexBinaryII) {
|
if (filterIndex == kFilterIndexBinaryII) {
|
||||||
/* try Binary II and nothing else */
|
/* try Binary II and nothing else */
|
||||||
ASSERT(!createFile);
|
ASSERT(!createFile);
|
||||||
LOGI(" Trying Binary II");
|
LOGD(" Trying Binary II");
|
||||||
pOpenArchive = new BnyArchive;
|
pOpenArchive = new BnyArchive;
|
||||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||||
if (openResult != GenericArchive::kResultSuccess) {
|
if (openResult != GenericArchive::kResultSuccess) {
|
||||||
|
@ -1896,7 +1896,7 @@ try_again:
|
||||||
if (filterIndex == kFilterIndexACU) {
|
if (filterIndex == kFilterIndexACU) {
|
||||||
/* try ACU and nothing else */
|
/* try ACU and nothing else */
|
||||||
ASSERT(!createFile);
|
ASSERT(!createFile);
|
||||||
LOGI(" Trying ACU");
|
LOGD(" Trying ACU");
|
||||||
pOpenArchive = new AcuArchive;
|
pOpenArchive = new AcuArchive;
|
||||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||||
if (openResult != GenericArchive::kResultSuccess) {
|
if (openResult != GenericArchive::kResultSuccess) {
|
||||||
|
@ -1909,7 +1909,7 @@ try_again:
|
||||||
if (filterIndex == kFilterIndexDiskImage) {
|
if (filterIndex == kFilterIndexDiskImage) {
|
||||||
/* try various disk image formats */
|
/* try various disk image formats */
|
||||||
ASSERT(!createFile);
|
ASSERT(!createFile);
|
||||||
LOGI(" Trying disk images");
|
LOGD(" Trying disk images");
|
||||||
|
|
||||||
pOpenArchive = new DiskArchive;
|
pOpenArchive = new DiskArchive;
|
||||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||||
|
@ -1933,13 +1933,13 @@ try_again:
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (openResult != GenericArchive::kResultSuccess) {
|
} else if (openResult != GenericArchive::kResultSuccess) {
|
||||||
if (filterIndex != origFilterIndex) {
|
//if (filterIndex != origFilterIndex) {
|
||||||
/*
|
// /*
|
||||||
* Kluge: assume we guessed disk image and were wrong.
|
// * Kluge: assume we guessed disk image and were wrong.
|
||||||
*/
|
// */
|
||||||
errStr = L"File doesn't appear to be a valid archive"
|
// errStr = L"File doesn't appear to be a valid archive"
|
||||||
L" or disk image.";
|
// L" or disk image.";
|
||||||
}
|
//}
|
||||||
if (!errStr.IsEmpty())
|
if (!errStr.IsEmpty())
|
||||||
ShowFailureMsg(this, errStr, IDS_FAILED);
|
ShowFailureMsg(this, errStr, IDS_FAILED);
|
||||||
result = -1;
|
result = -1;
|
||||||
|
@ -1948,7 +1948,7 @@ try_again:
|
||||||
} else
|
} else
|
||||||
if (filterIndex == kFilterIndexNuFX) {
|
if (filterIndex == kFilterIndexNuFX) {
|
||||||
/* try NuFX (including its embedded-in-BNY form) */
|
/* try NuFX (including its embedded-in-BNY form) */
|
||||||
LOGI(" Trying NuFX");
|
LOGD(" Trying NuFX");
|
||||||
|
|
||||||
pOpenArchive = new NufxArchive;
|
pOpenArchive = new NufxArchive;
|
||||||
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
openResult = pOpenArchive->Open(fileName, readOnly, &errStr);
|
||||||
|
|
|
@ -239,10 +239,10 @@ public:
|
||||||
*
|
*
|
||||||
* On failure, returns with an error message in errMsg.
|
* On failure, returns with an error message in errMsg.
|
||||||
*/
|
*/
|
||||||
static bool SaveToArchive(GenericArchive::FileDetails* pDetails,
|
static bool SaveToArchive(GenericArchive::LocalFileDetails* pDetails,
|
||||||
const uint8_t* dataBuf, long dataLen,
|
const uint8_t* dataBuf, long dataLen,
|
||||||
const uint8_t* rsrcBuf, long rsrcLen,
|
const uint8_t* rsrcBuf, long rsrcLen,
|
||||||
CString& errMsg, CWnd* pDialog);
|
CString* pErrMsg, CWnd* pDialog);
|
||||||
|
|
||||||
static const WCHAR kOpenNuFX[];
|
static const WCHAR kOpenNuFX[];
|
||||||
static const WCHAR kOpenBinaryII[];
|
static const WCHAR kOpenBinaryII[];
|
||||||
|
|
|
@ -1156,51 +1156,57 @@ bail:
|
||||||
}
|
}
|
||||||
|
|
||||||
NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails)
|
LocalFileDetails* pDetails)
|
||||||
{
|
{
|
||||||
NuError err;
|
NuError err;
|
||||||
NuRecordIdx recordIdx = 0;
|
NuRecordIdx recordIdx = 0;
|
||||||
NuFileDetails nuFileDetails;
|
|
||||||
|
|
||||||
retry:
|
|
||||||
nuFileDetails = *pDetails; // stuff class contents into struct
|
|
||||||
CStringA origNameA(pDetails->origName);
|
|
||||||
err = NuAddFile(fpArchive, origNameA /*pathname*/,
|
|
||||||
&nuFileDetails, false, &recordIdx);
|
|
||||||
|
|
||||||
if (err == kNuErrNone) {
|
do {
|
||||||
fNumAdded++;
|
// Must re-get the NuFileDetails, because updating the filename for
|
||||||
} else if (err == kNuErrSkipped) {
|
// rename will invalidate the previous version.
|
||||||
/* "maybe overwrite" UI causes this if user declines */
|
const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
|
||||||
// fall through with the error
|
|
||||||
LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->origName);
|
|
||||||
} else if (err == kNuErrRecordExists) {
|
|
||||||
AddClashDialog dlg;
|
|
||||||
|
|
||||||
dlg.fWindowsName = pDetails->origName;
|
// TODO(Unicode): NufxLib should accept wide pathname
|
||||||
dlg.fStorageName = pDetails->storageName;
|
CStringA origNameA(pDetails->GetLocalPathName());
|
||||||
if (dlg.DoModal() != IDOK) {
|
CString dummyMsg;
|
||||||
err = kNuErrAborted;
|
if (!PathName::TestNarrowConversion(pDetails->GetLocalPathName(),
|
||||||
|
origNameA, &dummyMsg)) {
|
||||||
|
err = kNuErrInvalidFilename;
|
||||||
goto bail_quiet;
|
goto bail_quiet;
|
||||||
}
|
}
|
||||||
if (dlg.fDoRename) {
|
err = NuAddFile(fpArchive, origNameA, &nuFileDetails, false, &recordIdx);
|
||||||
LOGD("add clash: rename to '%ls'", (LPCWSTR) dlg.fNewName);
|
|
||||||
pDetails->storageName = dlg.fNewName;
|
if (err == kNuErrNone) {
|
||||||
goto retry;
|
fNumAdded++;
|
||||||
} else {
|
} else if (err == kNuErrSkipped) {
|
||||||
LOGD("add clash: skip");
|
/* "maybe overwrite" UI causes this if user declines */
|
||||||
err = kNuErrSkipped;
|
// fall through with the error
|
||||||
// fall through with error
|
LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->GetLocalPathName());
|
||||||
}
|
} else if (err == kNuErrRecordExists) {
|
||||||
}
|
AddClashDialog dlg;
|
||||||
//if (err != kNuErrNone)
|
|
||||||
// goto bail;
|
dlg.fWindowsName = pDetails->GetLocalPathName();
|
||||||
|
dlg.fStorageName = pDetails->GetStrippedLocalPathName();
|
||||||
|
if (dlg.DoModal() != IDOK) {
|
||||||
|
err = kNuErrAborted;
|
||||||
|
goto bail_quiet;
|
||||||
|
}
|
||||||
|
if (dlg.fDoRename) {
|
||||||
|
LOGD("add clash: rename to '%ls'", (LPCWSTR) dlg.fNewName);
|
||||||
|
pDetails->SetStrippedLocalPathName(dlg.fNewName);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
LOGD("add clash: skip");
|
||||||
|
err = kNuErrSkipped;
|
||||||
|
// fall through with error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
|
||||||
//bail:
|
|
||||||
if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) {
|
if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) {
|
||||||
CString msg;
|
CString msg;
|
||||||
msg.Format(L"Unable to add file '%ls': %hs.",
|
msg.Format(L"Unable to add file '%ls': %hs.",
|
||||||
(LPCWSTR) pDetails->origName, NuStrError(err));
|
(LPCWSTR) pDetails->GetLocalPathName(), NuStrError(err));
|
||||||
ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED);
|
ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED);
|
||||||
}
|
}
|
||||||
bail_quiet:
|
bail_quiet:
|
||||||
|
@ -1303,7 +1309,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
|
||||||
ASSERT(pErrorStatus->canOverwrite);
|
ASSERT(pErrorStatus->canOverwrite);
|
||||||
ASSERT(pErrorStatus->canSkip);
|
ASSERT(pErrorStatus->canSkip);
|
||||||
ASSERT(pErrorStatus->canAbort);
|
ASSERT(pErrorStatus->canAbort);
|
||||||
ASSERT(!pErrorStatus->canRename);
|
ASSERT(!pErrorStatus->canRename); // TODO: remember why we can't rename
|
||||||
|
|
||||||
/* no firm policy, ask the user */
|
/* no firm policy, ask the user */
|
||||||
ConfirmOverwriteDialog confOvwr;
|
ConfirmOverwriteDialog confOvwr;
|
||||||
|
@ -1313,8 +1319,7 @@ NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus)
|
||||||
confOvwr.fExistingFileModWhen =
|
confOvwr.fExistingFileModWhen =
|
||||||
DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen);
|
DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen);
|
||||||
if (pErrorStatus->origPathname != NULL) {
|
if (pErrorStatus->origPathname != NULL) {
|
||||||
// TODO: use wchar_t instead for origPathname
|
confOvwr.fNewFileSource = (WCHAR*) pErrorStatus->origPathname;
|
||||||
confOvwr.fNewFileSource = (char*) pErrorStatus->origPathname;
|
|
||||||
PathName checkPath(confOvwr.fNewFileSource);
|
PathName checkPath(confOvwr.fNewFileSource);
|
||||||
confOvwr.fNewFileModWhen = checkPath.GetModWhen();
|
confOvwr.fNewFileModWhen = checkPath.GetModWhen();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1858,7 +1863,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
* I think this now throws kXferCancelled whenever it's supposed to. Not
|
* I think this now throws kXferCancelled whenever it's supposed to. Not
|
||||||
* 100% sure, but it looks good.
|
* 100% sure, but it looks good.
|
||||||
*/
|
*/
|
||||||
LOGI("NufxArchive XferSelection!");
|
LOGD("NufxArchive XferSelection!");
|
||||||
XferStatus retval = kXferFailed;
|
XferStatus retval = kXferFailed;
|
||||||
unsigned char* dataBuf = NULL;
|
unsigned char* dataBuf = NULL;
|
||||||
unsigned char* rsrcBuf = NULL;
|
unsigned char* rsrcBuf = NULL;
|
||||||
|
@ -1870,7 +1875,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) {
|
for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) {
|
||||||
long dataLen=-1, rsrcLen=-1;
|
long dataLen=-1, rsrcLen=-1;
|
||||||
NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry();
|
NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry();
|
||||||
FileDetails fileDetails;
|
LocalFileDetails fileDetails;
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
|
|
||||||
ASSERT(dataBuf == NULL);
|
ASSERT(dataBuf == NULL);
|
||||||
|
@ -1883,25 +1888,29 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI(" XFER converting '%ls'", pEntry->GetDisplayName());
|
LOGD(" XFER converting '%ls'", pEntry->GetDisplayName());
|
||||||
|
|
||||||
fileDetails.storageName = pEntry->GetDisplayName();
|
fileDetails.SetStrippedLocalPathName(pEntry->GetDisplayName());
|
||||||
fileDetails.fileType = pEntry->GetFileType();
|
fileDetails.SetFssep(PathProposal::kDefaultStoredFssep);
|
||||||
fileDetails.fileSysFmt = DiskImg::kFormatUnknown;
|
fileDetails.SetFileSysFmt(DiskImg::kFormatUnknown);
|
||||||
fileDetails.fileSysInfo = PathProposal::kDefaultStoredFssep;
|
fileDetails.SetFileType(pEntry->GetFileType());
|
||||||
fileDetails.access = pEntry->GetAccess();
|
fileDetails.SetExtraType(pEntry->GetAuxType());
|
||||||
fileDetails.extraType = pEntry->GetAuxType();
|
fileDetails.SetAccess(pEntry->GetAccess());
|
||||||
fileDetails.storageType = kNuStorageSeedling;
|
fileDetails.SetStorageType(kNuStorageSeedling);
|
||||||
|
|
||||||
time_t when;
|
time_t when;
|
||||||
|
NuDateTime nuwhen;
|
||||||
when = time(NULL);
|
when = time(NULL);
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.archiveWhen);
|
UNIXTimeToDateTime(&when, &nuwhen);
|
||||||
|
fileDetails.SetArchiveWhen(nuwhen);
|
||||||
when = pEntry->GetModWhen();
|
when = pEntry->GetModWhen();
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.modWhen);
|
UNIXTimeToDateTime(&when, &nuwhen);
|
||||||
|
fileDetails.SetModWhen(nuwhen);
|
||||||
when = pEntry->GetCreateWhen();
|
when = pEntry->GetCreateWhen();
|
||||||
UNIXTimeToDateTime(&when, &fileDetails.createWhen);
|
UNIXTimeToDateTime(&when, &nuwhen);
|
||||||
|
fileDetails.SetCreateWhen(nuwhen);
|
||||||
|
|
||||||
pActionProgress->SetArcName(fileDetails.storageName);
|
pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName());
|
||||||
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
if (pActionProgress->SetProgress(0) == IDCANCEL) {
|
||||||
retval = kXferCancelled;
|
retval = kXferCancelled;
|
||||||
goto bail;
|
goto bail;
|
||||||
|
@ -1975,7 +1984,7 @@ GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd,
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileDetails.storageType = kNuStorageExtended;
|
fileDetails.SetStorageType(kNuStorageExtended);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(rsrcBuf == NULL);
|
ASSERT(rsrcBuf == NULL);
|
||||||
}
|
}
|
||||||
|
@ -2026,22 +2035,23 @@ void NufxArchive::XferPrepare(const XferFileOptions* pXferOpts)
|
||||||
(void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true);
|
(void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
CString NufxArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, unsigned char** pRsrcBuf, long rsrcLen)
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen)
|
||||||
{
|
{
|
||||||
NuError nerr;
|
NuError nerr;
|
||||||
const int kFileTypeTXT = 0x04;
|
const int kFileTypeTXT = 0x04;
|
||||||
NuDataSource* pSource = NULL;
|
NuDataSource* pSource = NULL;
|
||||||
CString errMsg;
|
CString errMsg;
|
||||||
|
|
||||||
LOGI(" NufxArchive::XferFile '%ls'", (LPCWSTR) pDetails->storageName);
|
LOGI(" NufxArchive::XferFile '%ls'",
|
||||||
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
LOGI(" dataBuf=0x%p dataLen=%ld rsrcBuf=0x%p rsrcLen=%ld",
|
LOGI(" dataBuf=0x%p dataLen=%ld rsrcBuf=0x%p rsrcLen=%ld",
|
||||||
*pDataBuf, dataLen, *pRsrcBuf, rsrcLen);
|
*pDataBuf, dataLen, *pRsrcBuf, rsrcLen);
|
||||||
ASSERT(pDataBuf != NULL);
|
ASSERT(pDataBuf != NULL);
|
||||||
ASSERT(pRsrcBuf != NULL);
|
ASSERT(pRsrcBuf != NULL);
|
||||||
|
|
||||||
/* NuFX doesn't explicitly store directories */
|
/* NuFX doesn't explicitly store directories */
|
||||||
if (pDetails->entryKind == FileDetails::kFileKindDirectory) {
|
if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDirectory) {
|
||||||
delete[] *pDataBuf;
|
delete[] *pDataBuf;
|
||||||
delete[] *pRsrcBuf;
|
delete[] *pRsrcBuf;
|
||||||
*pDataBuf = *pRsrcBuf = NULL;
|
*pDataBuf = *pRsrcBuf = NULL;
|
||||||
|
@ -2051,11 +2061,6 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||||
ASSERT(dataLen >= 0 || rsrcLen >= 0);
|
ASSERT(dataLen >= 0 || rsrcLen >= 0);
|
||||||
ASSERT(*pDataBuf != NULL || *pRsrcBuf != NULL);
|
ASSERT(*pDataBuf != NULL || *pRsrcBuf != NULL);
|
||||||
|
|
||||||
/* add the record; we have "allow duplicates" enabled for clashes */
|
|
||||||
NuRecordIdx recordIdx;
|
|
||||||
NuFileDetails nuFileDetails;
|
|
||||||
nuFileDetails = *pDetails;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Odd bit of trivia: NufxLib refuses to accept an fssep of '\0'. It
|
* Odd bit of trivia: NufxLib refuses to accept an fssep of '\0'. It
|
||||||
* really wants to have one. Which is annoying, since files coming
|
* really wants to have one. Which is annoying, since files coming
|
||||||
|
@ -2072,12 +2077,17 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||||
* One issue: we don't currently allow changing the fssep when renaming
|
* One issue: we don't currently allow changing the fssep when renaming
|
||||||
* a file. We need to fix this, or else there's no way to rename a
|
* a file. We need to fix this, or else there's no way to rename a
|
||||||
* file into a subdirectory once it has been pasted in this way.
|
* file into a subdirectory once it has been pasted in this way.
|
||||||
|
*
|
||||||
|
* TODO: fix this in NufxLib
|
||||||
*/
|
*/
|
||||||
if (NuGetSepFromSysInfo(nuFileDetails.fileSysInfo) == 0) {
|
if (pDetails->GetFssep() == '\0') {
|
||||||
nuFileDetails.fileSysInfo =
|
pDetails->SetFssep(kNufxNoFssep);
|
||||||
NuSetSepInSysInfo(nuFileDetails.fileSysInfo, kNufxNoFssep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* add the record; we have "allow duplicates" enabled for clashes */
|
||||||
|
NuRecordIdx recordIdx;
|
||||||
|
const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails();
|
||||||
|
|
||||||
nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx);
|
nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx);
|
||||||
if (nerr != kNuErrNone) {
|
if (nerr != kNuErrNone) {
|
||||||
if (nerr != kNuErrAborted) {
|
if (nerr != kNuErrAborted) {
|
||||||
|
@ -2092,13 +2102,13 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||||
ASSERT(*pDataBuf != NULL);
|
ASSERT(*pDataBuf != NULL);
|
||||||
|
|
||||||
/* strip the high ASCII from DOS and RDOS text files */
|
/* strip the high ASCII from DOS and RDOS text files */
|
||||||
if (pDetails->entryKind != FileDetails::kFileKindDiskImage &&
|
if (pDetails->GetEntryKind() != LocalFileDetails::kFileKindDiskImage &&
|
||||||
pDetails->fileType == kFileTypeTXT &&
|
pDetails->GetFileType() == kFileTypeTXT &&
|
||||||
DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt))
|
DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt()))
|
||||||
{
|
{
|
||||||
LOGI(" Stripping high ASCII from '%ls'",
|
LOGI(" Stripping high ASCII from '%ls'",
|
||||||
(LPCWSTR) pDetails->storageName);
|
(LPCWSTR) pDetails->GetStrippedLocalPathName());
|
||||||
unsigned char* ucp = *pDataBuf;
|
uint8_t* ucp = *pDataBuf;
|
||||||
long len = dataLen;
|
long len = dataLen;
|
||||||
|
|
||||||
while (len--)
|
while (len--)
|
||||||
|
@ -2117,7 +2127,7 @@ CString NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
|
||||||
|
|
||||||
/* add the data fork, as a disk image if appropriate */
|
/* add the data fork, as a disk image if appropriate */
|
||||||
NuThreadID targetID;
|
NuThreadID targetID;
|
||||||
if (pDetails->entryKind == FileDetails::kFileKindDiskImage)
|
if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDiskImage)
|
||||||
targetID = kNuThreadIDDiskImage;
|
targetID = kNuThreadIDDiskImage;
|
||||||
else
|
else
|
||||||
targetID = kNuThreadIDDataFork;
|
targetID = kNuThreadIDDataFork;
|
||||||
|
|
|
@ -171,7 +171,7 @@ private:
|
||||||
CString* pErrMsg);
|
CString* pErrMsg);
|
||||||
|
|
||||||
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
virtual void XferPrepare(const XferFileOptions* pXferOpts) override;
|
||||||
virtual CString XferFile(FileDetails* pDetails, uint8_t** pDataBuf,
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
||||||
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override;
|
||||||
virtual void XferAbort(CWnd* pMsgWnd) override;
|
virtual void XferAbort(CWnd* pMsgWnd) override;
|
||||||
virtual void XferFinish(CWnd* pMsgWnd) override;
|
virtual void XferFinish(CWnd* pMsgWnd) override;
|
||||||
|
@ -188,7 +188,7 @@ private:
|
||||||
void AddFinish(void);
|
void AddFinish(void);
|
||||||
|
|
||||||
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
||||||
FileDetails* pDetails) override;
|
LocalFileDetails* pDetails) override;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error handler callback for "bulk" adds.
|
* Error handler callback for "bulk" adds.
|
||||||
|
|
|
@ -2468,7 +2468,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||||
*/
|
*/
|
||||||
dierr = ValidateCreateFormat();
|
dierr = ValidateCreateFormat();
|
||||||
if (dierr != kDIErrNone) {
|
if (dierr != kDIErrNone) {
|
||||||
LOGI("ERROR: CIC arg validation failed, bailing");
|
LOGE("ERROR: CIC arg validation failed, bailing");
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2486,7 +2486,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||||
fd = open(pathName, O_CREAT | O_EXCL, 0644);
|
fd = open(pathName, O_CREAT | O_EXCL, 0644);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
dierr = (DIError) errno;
|
dierr = (DIError) errno;
|
||||||
LOGI("ERROR: unable to create file '%s' (errno=%d)",
|
LOGE("ERROR: unable to create file '%s' (errno=%d)",
|
||||||
pathName, dierr);
|
pathName, dierr);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
@ -2627,7 +2627,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fpImageWrapper == NULL) {
|
if (fpImageWrapper == NULL) {
|
||||||
LOGI(" DI couldn't figure out the file format");
|
LOGW(" DI couldn't figure out the file format");
|
||||||
dierr = kDIErrUnrecognizedFileFmt;
|
dierr = kDIErrUnrecognizedFileFmt;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
@ -2637,7 +2637,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||||
dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder,
|
dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder,
|
||||||
fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD);
|
fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD);
|
||||||
if (dierr != kDIErrNone) {
|
if (dierr != kDIErrNone) {
|
||||||
LOGI("ImageWrapper Create failed, err=%d", dierr);
|
LOGE("ImageWrapper Create failed, err=%d", dierr);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
assert(fpDataGFD != NULL);
|
assert(fpDataGFD != NULL);
|
||||||
|
@ -2658,7 +2658,7 @@ DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName
|
||||||
assert(!skipFormat); // don't skip low-level nibble formatting!
|
assert(!skipFormat); // don't skip low-level nibble formatting!
|
||||||
if (fDOSVolumeNum == kVolumeNumNotSet) {
|
if (fDOSVolumeNum == kVolumeNumNotSet) {
|
||||||
fDOSVolumeNum = kDefaultNibbleVolumeNum;
|
fDOSVolumeNum = kDefaultNibbleVolumeNum;
|
||||||
LOGI(" Using default nibble volume num");
|
LOGD(" Using default nibble volume num");
|
||||||
}
|
}
|
||||||
|
|
||||||
dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff
|
dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff
|
||||||
|
@ -2697,7 +2697,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
*/
|
*/
|
||||||
if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger?
|
if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger?
|
||||||
if (fFileFormat != kFileFormatUnadorned) {
|
if (fFileFormat != kFileFormatUnadorned) {
|
||||||
LOGI("CreateImage: images >= 2GB can only be unadorned");
|
LOGW("CreateImage: images >= 2GB can only be unadorned");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2707,14 +2707,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
fOrder == kSectorOrderUnknown ||
|
fOrder == kSectorOrderUnknown ||
|
||||||
fFormat == kFormatUnknown)
|
fFormat == kFormatUnknown)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: ambiguous format");
|
LOGW("CreateImage: ambiguous format");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fOuterFormat != kOuterFormatNone &&
|
if (fOuterFormat != kOuterFormatNone &&
|
||||||
fOuterFormat != kOuterFormatGzip &&
|
fOuterFormat != kOuterFormatGzip &&
|
||||||
fOuterFormat != kOuterFormatZip)
|
fOuterFormat != kOuterFormatZip)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: unsupported outer format %d", fOuterFormat);
|
LOGW("CreateImage: unsupported outer format %d", fOuterFormat);
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fFileFormat != kFileFormatUnadorned &&
|
if (fFileFormat != kFileFormatUnadorned &&
|
||||||
|
@ -2726,7 +2726,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
fFileFormat != kFileFormatNuFX &&
|
fFileFormat != kFileFormatNuFX &&
|
||||||
fFileFormat != kFileFormatDDD)
|
fFileFormat != kFileFormatDDD)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: unsupported file format %d", fFileFormat);
|
LOGW("CreateImage: unsupported file format %d", fFileFormat);
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fFormat != kFormatGenericPhysicalOrd &&
|
if (fFormat != kFormatGenericPhysicalOrd &&
|
||||||
|
@ -2734,7 +2734,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
fFormat != kFormatGenericDOSOrd &&
|
fFormat != kFormatGenericDOSOrd &&
|
||||||
fFormat != kFormatGenericCPMOrd)
|
fFormat != kFormatGenericCPMOrd)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: may only use 'generic' formats");
|
LOGW("CreateImage: may only use 'generic' formats");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2743,25 +2743,25 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
*/
|
*/
|
||||||
if (fPhysical != kPhysicalFormatSectors) {
|
if (fPhysical != kPhysicalFormatSectors) {
|
||||||
if (fOrder != kSectorOrderPhysical) {
|
if (fOrder != kSectorOrderPhysical) {
|
||||||
LOGI("CreateImage: nibble images are always 'physical' order");
|
LOGW("CreateImage: nibble images are always 'physical' order");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetHasSectors() == false && GetHasNibbles() == false) {
|
if (GetHasSectors() == false && GetHasNibbles() == false) {
|
||||||
LOGI("CreateImage: must set hasSectors(%d) or hasNibbles(%d)",
|
LOGW("CreateImage: must set hasSectors(%d) or hasNibbles(%d)",
|
||||||
GetHasSectors(), GetHasNibbles());
|
GetHasSectors(), GetHasNibbles());
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) {
|
if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) {
|
||||||
LOGI("CreateImage: must provide NibbleDescr for non-sector");
|
LOGW("CreateImage: must provide NibbleDescr for non-sector");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fpNibbleDescr != NULL &&
|
if (fpNibbleDescr != NULL &&
|
||||||
fpNibbleDescr->numSectors != GetNumSectPerTrack())
|
fpNibbleDescr->numSectors != GetNumSectPerTrack())
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d",
|
LOGW("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d",
|
||||||
fpNibbleDescr->numSectors, GetNumSectPerTrack());
|
fpNibbleDescr->numSectors, GetNumSectPerTrack());
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
@ -2773,14 +2773,14 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
fpNibbleDescr->encoding != kNibbleEnc62))
|
fpNibbleDescr->encoding != kNibbleEnc62))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: sector count/encoding mismatch");
|
LOGW("CreateImage: sector count/encoding mismatch");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetNumTracks() != kTrackCount525 &&
|
if (GetNumTracks() != kTrackCount525 &&
|
||||||
!(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar))
|
!(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar))
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: unexpected track count %ld", GetNumTracks());
|
LOGW("CreateImage: unexpected track count %ld", GetNumTracks());
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2788,7 +2788,7 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
if (fPhysical != kPhysicalFormatSectors &&
|
if (fPhysical != kPhysicalFormatSectors &&
|
||||||
fPhysical != kPhysicalFormatNib525_6656)
|
fPhysical != kPhysicalFormatNib525_6656)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: 2MG can't handle physical %d", fPhysical);
|
LOGW("CreateImage: 2MG can't handle physical %d", fPhysical);
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2796,78 +2796,78 @@ DIError DiskImg::ValidateCreateFormat(void) const
|
||||||
(fOrder != kSectorOrderProDOS &&
|
(fOrder != kSectorOrderProDOS &&
|
||||||
fOrder != kSectorOrderDOS))
|
fOrder != kSectorOrderDOS))
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: 2MG requires DOS or ProDOS ordering");
|
LOGW("CreateImage: 2MG requires DOS or ProDOS ordering");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatNuFX) {
|
if (fFileFormat == kFileFormatNuFX) {
|
||||||
if (fOuterFormat != kOuterFormatNone) {
|
if (fOuterFormat != kOuterFormatNone) {
|
||||||
LOGI("CreateImage: can't mix NuFX and outer wrapper");
|
LOGW("CreateImage: can't mix NuFX and outer wrapper");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fPhysical != kPhysicalFormatSectors) {
|
if (fPhysical != kPhysicalFormatSectors) {
|
||||||
LOGI("CreateImage: NuFX physical must be sectors");
|
LOGW("CreateImage: NuFX physical must be sectors");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fOrder != kSectorOrderProDOS) {
|
if (fOrder != kSectorOrderProDOS) {
|
||||||
LOGI("CreateImage: NuFX is always ProDOS-order");
|
LOGW("CreateImage: NuFX is always ProDOS-order");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatDiskCopy42) {
|
if (fFileFormat == kFileFormatDiskCopy42) {
|
||||||
if (fPhysical != kPhysicalFormatSectors) {
|
if (fPhysical != kPhysicalFormatSectors) {
|
||||||
LOGI("CreateImage: DC42 physical must be sectors");
|
LOGW("CreateImage: DC42 physical must be sectors");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if ((GetHasBlocks() && GetNumBlocks() != 1600) ||
|
if ((GetHasBlocks() && GetNumBlocks() != 1600) ||
|
||||||
(GetHasSectors() &&
|
(GetHasSectors() &&
|
||||||
(GetNumTracks() != 200 || GetNumSectPerTrack() != 16)))
|
(GetNumTracks() != 200 || GetNumSectPerTrack() != 16)))
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: DC42 only for 800K disks");
|
LOGW("CreateImage: DC42 only for 800K disks");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fOrder != kSectorOrderProDOS &&
|
if (fOrder != kSectorOrderProDOS &&
|
||||||
fOrder != kSectorOrderDOS) // used for UNIDOS disks??
|
fOrder != kSectorOrderDOS) // used for UNIDOS disks??
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: DC42 is always ProDOS or DOS");
|
LOGW("CreateImage: DC42 is always ProDOS or DOS");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatSim2eHDV) {
|
if (fFileFormat == kFileFormatSim2eHDV) {
|
||||||
if (fPhysical != kPhysicalFormatSectors) {
|
if (fPhysical != kPhysicalFormatSectors) {
|
||||||
LOGI("CreateImage: Sim2eHDV physical must be sectors");
|
LOGW("CreateImage: Sim2eHDV physical must be sectors");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fOrder != kSectorOrderProDOS) {
|
if (fOrder != kSectorOrderProDOS) {
|
||||||
LOGI("CreateImage: Sim2eHDV is always ProDOS-order");
|
LOGW("CreateImage: Sim2eHDV is always ProDOS-order");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatTrackStar) {
|
if (fFileFormat == kFileFormatTrackStar) {
|
||||||
if (fPhysical != kPhysicalFormatNib525_Var) {
|
if (fPhysical != kPhysicalFormatNib525_Var) {
|
||||||
LOGI("CreateImage: TrackStar physical must be var-nibbles");
|
LOGW("CreateImage: TrackStar physical must be var-nibbles");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatFDI) {
|
if (fFileFormat == kFileFormatFDI) {
|
||||||
if (fPhysical != kPhysicalFormatNib525_Var) {
|
if (fPhysical != kPhysicalFormatNib525_Var) {
|
||||||
LOGI("CreateImage: FDI physical must be var-nibbles");
|
LOGW("CreateImage: FDI physical must be var-nibbles");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fFileFormat == kFileFormatDDD) {
|
if (fFileFormat == kFileFormatDDD) {
|
||||||
if (fPhysical != kPhysicalFormatSectors) {
|
if (fPhysical != kPhysicalFormatSectors) {
|
||||||
LOGI("CreateImage: DDD physical must be sectors");
|
LOGW("CreateImage: DDD physical must be sectors");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (fOrder != kSectorOrderDOS) {
|
if (fOrder != kSectorOrderDOS) {
|
||||||
LOGI("CreateImage: DDD is always DOS-order");
|
LOGW("CreateImage: DDD is always DOS-order");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
if (!GetHasSectors() || GetNumTracks() != 35 ||
|
if (!GetHasSectors() || GetNumTracks() != 35 ||
|
||||||
GetNumSectPerTrack() != 16)
|
GetNumSectPerTrack() != 16)
|
||||||
{
|
{
|
||||||
LOGI("CreateImage: DDD is only for 16-sector 35-track disks");
|
LOGW("CreateImage: DDD is only for 16-sector 35-track disks");
|
||||||
return kDIErrInvalidCreateReq;
|
return kDIErrInvalidCreateReq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1159,6 +1159,13 @@ public:
|
||||||
* normalized string. If the buffer is NULL or isn't big enough, no part
|
* normalized string. If the buffer is NULL or isn't big enough, no part
|
||||||
* of the normalized path will be copied into the buffer, and a specific
|
* of the normalized path will be copied into the buffer, and a specific
|
||||||
* error (kDIErrDataOverrun) will be returned.
|
* error (kDIErrDataOverrun) will be returned.
|
||||||
|
*
|
||||||
|
* Generally speaking, the normalization process involves separating
|
||||||
|
* the pathname into its components, modifying each filename as needed
|
||||||
|
* to make it legal on the current filesystem, and then reassembling
|
||||||
|
* the components.
|
||||||
|
*
|
||||||
|
* The input and output strings are Mac OS Roman.
|
||||||
*/
|
*/
|
||||||
virtual DIError NormalizePath(const char* path, char fssep,
|
virtual DIError NormalizePath(const char* path, char fssep,
|
||||||
char* normalizedBuf, int* pNormalizedBufLen)
|
char* normalizedBuf, int* pNormalizedBufLen)
|
||||||
|
@ -1184,9 +1191,9 @@ public:
|
||||||
const char* pathName; // full pathname for file on disk image
|
const char* pathName; // full pathname for file on disk image
|
||||||
char fssep;
|
char fssep;
|
||||||
int storageType; // determines normal, subdir, or forked
|
int storageType; // determines normal, subdir, or forked
|
||||||
long fileType;
|
uint32_t fileType;
|
||||||
long auxType;
|
uint32_t auxType;
|
||||||
int access;
|
uint32_t access;
|
||||||
time_t createWhen;
|
time_t createWhen;
|
||||||
time_t modWhen;
|
time_t modWhen;
|
||||||
} CreateParms;
|
} CreateParms;
|
||||||
|
@ -1459,10 +1466,13 @@ public:
|
||||||
* NOTE: there is no guarantee that GetPathName will return unique values
|
* NOTE: there is no guarantee that GetPathName will return unique values
|
||||||
* (duplicates are possible). We don't guarantee that you won't get an
|
* (duplicates are possible). We don't guarantee that you won't get an
|
||||||
* empty string back (it's valid to have an empty filename in the dir in
|
* empty string back (it's valid to have an empty filename in the dir in
|
||||||
* DOS 3.3, and it's possible for other filesystems to be damaged). The
|
* DOS 3.3, and it's possible for other filesystems to be damaged).
|
||||||
* pathname may receive some minor sanitizing, e.g. removal or conversion
|
*
|
||||||
* of high ASCII and control characters, but some filesystems (like HFS)
|
* The filename returned is defined to be null-terminated Mac OS Roman.
|
||||||
* make use of them.
|
* For most filesystems this is automatic, as filenames are restricted
|
||||||
|
* to ASCII, but for DOS 3.3 it requires stripping high bits. It also
|
||||||
|
* means that embedded nulls in HFS filenames (which are discouraged but
|
||||||
|
* allowed) will be lost.
|
||||||
*
|
*
|
||||||
* We do guarantee that the contents of subdirectories are grouped
|
* We do guarantee that the contents of subdirectories are grouped
|
||||||
* together. This makes it much easier to construct a hierarchy out of
|
* together. This makes it much easier to construct a hierarchy out of
|
||||||
|
@ -1474,7 +1484,7 @@ public:
|
||||||
virtual char GetFssep(void) const = 0; // '\0' if none
|
virtual char GetFssep(void) const = 0; // '\0' if none
|
||||||
virtual uint32_t GetFileType(void) const = 0;
|
virtual uint32_t GetFileType(void) const = 0;
|
||||||
virtual uint32_t GetAuxType(void) const = 0;
|
virtual uint32_t GetAuxType(void) const = 0;
|
||||||
virtual uint32_t GetAccess(void) const = 0; // ProDOS-style perms
|
virtual uint32_t GetAccess(void) const = 0; // ProDOS-style perms
|
||||||
virtual time_t GetCreateWhen(void) const = 0;
|
virtual time_t GetCreateWhen(void) const = 0;
|
||||||
virtual time_t GetModWhen(void) const = 0;
|
virtual time_t GetModWhen(void) const = 0;
|
||||||
virtual di_off_t GetDataLength(void) const = 0; // len of data fork
|
virtual di_off_t GetDataLength(void) const = 0; // len of data fork
|
||||||
|
|
|
@ -1407,10 +1407,6 @@ private:
|
||||||
/*
|
/*
|
||||||
* Holds DOS files. Works for DOS33, DOS32, and "wide" DOS implementations.
|
* Holds DOS files. Works for DOS33, DOS32, and "wide" DOS implementations.
|
||||||
*
|
*
|
||||||
* Catalog contents are public so anyone who wants gory details of DOS 3.3
|
|
||||||
* stuff can poke at whatever they want. Anybody else can use the virtual
|
|
||||||
* interfaces to get standardized answers for things like file type.
|
|
||||||
*
|
|
||||||
* The embedded address and length fields found in Applesoft, Integer, and
|
* The embedded address and length fields found in Applesoft, Integer, and
|
||||||
* Binary files are quietly skipped over with the fDataOffset field when
|
* Binary files are quietly skipped over with the fDataOffset field when
|
||||||
* files are read.
|
* files are read.
|
||||||
|
@ -1470,6 +1466,10 @@ public:
|
||||||
|
|
||||||
void Dump(void) const;
|
void Dump(void) const;
|
||||||
|
|
||||||
|
friend class DiskFSDOS33;
|
||||||
|
friend class A2FDDOS;
|
||||||
|
|
||||||
|
private:
|
||||||
typedef DiskFSDOS33::TrackSector TrackSector;
|
typedef DiskFSDOS33::TrackSector TrackSector;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1490,7 +1490,7 @@ public:
|
||||||
|
|
||||||
// these are computed or determined from the file contents
|
// these are computed or determined from the file contents
|
||||||
uint16_t fAuxType; // addr for bin, etc.
|
uint16_t fAuxType; // addr for bin, etc.
|
||||||
short fDataOffset; // for 'A'/'B'/'I' with embedded len
|
uint16_t fDataOffset; // 0/2/4, for 'A'/'B'/'I' with embedded len
|
||||||
di_off_t fLength; // file length, in bytes
|
di_off_t fLength; // file length, in bytes
|
||||||
di_off_t fSparseLength; // file length, factoring sparse out
|
di_off_t fSparseLength; // file length, factoring sparse out
|
||||||
|
|
||||||
|
@ -1503,7 +1503,6 @@ public:
|
||||||
static void MakeDOSName(char* buf, const char* name);
|
static void MakeDOSName(char* buf, const char* name);
|
||||||
static void TrimTrailingSpaces(char* filename);
|
static void TrimTrailingSpaces(char* filename);
|
||||||
|
|
||||||
private:
|
|
||||||
DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList,
|
DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList,
|
||||||
int* pLastNonZero);
|
int* pLastNonZero);
|
||||||
|
|
||||||
|
|
|
@ -254,11 +254,16 @@ DIError GFDFile::Open(const char* filename, bool readOnly)
|
||||||
|
|
||||||
fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0);
|
fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0);
|
||||||
if (fFd < 0) {
|
if (fFd < 0) {
|
||||||
if (errno == EACCES)
|
if (errno == EACCES) {
|
||||||
dierr = kDIErrAccessDenied;
|
dierr = kDIErrAccessDenied;
|
||||||
else
|
} else if (errno == EINVAL) {
|
||||||
|
// Happens on Win32 if a Unicode filename conversion failed,
|
||||||
|
// because non-converting chars turn into '?', which is illegal.
|
||||||
|
dierr = kDIErrInvalidArg;
|
||||||
|
} else {
|
||||||
dierr = ErrnoOrGeneric();
|
dierr = ErrnoOrGeneric();
|
||||||
LOGI(" GDFile Open failed opening '%s', ro=%d (err=%d)",
|
}
|
||||||
|
LOGW(" GDFile Open failed opening '%s', ro=%d (err=%d)",
|
||||||
filename, readOnly, dierr);
|
filename, readOnly, dierr);
|
||||||
return dierr;
|
return dierr;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +283,7 @@ DIError GFDFile::Read(void* buf, size_t length, size_t* pActual)
|
||||||
return kDIErrEOF;
|
return kDIErrEOF;
|
||||||
if (actual < 0) {
|
if (actual < 0) {
|
||||||
dierr = ErrnoOrGeneric();
|
dierr = ErrnoOrGeneric();
|
||||||
LOGI(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
|
LOGW(" GDFile Read failed on %d bytes (actual=%d, err=%d)",
|
||||||
length, actual, dierr);
|
length, actual, dierr);
|
||||||
return dierr;
|
return dierr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1097,15 +1097,16 @@ NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
|
||||||
err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI);
|
err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI);
|
||||||
BailError(err);
|
BailError(err);
|
||||||
|
|
||||||
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
|
|
||||||
BailError(err);
|
|
||||||
|
|
||||||
#if defined(MAC_LIKE)
|
#if defined(MAC_LIKE)
|
||||||
/* could also do this earlier and pass the fd for fsetxattr */
|
/* could also do this earlier and pass the fd for fsetxattr */
|
||||||
|
/* NOTE: must do this before Nu_SetFileAccess */
|
||||||
err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI);
|
err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI);
|
||||||
BailError(err);
|
BailError(err);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI);
|
||||||
|
BailError(err);
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
return kNuErrNone;
|
return kNuErrNone;
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,25 +429,25 @@ union NuDataSink {
|
||||||
* the first arguments to Nu_ReportError, so we don't have to type them
|
* the first arguments to Nu_ReportError, so we don't have to type them
|
||||||
* in every time we use the function. It would've been much easier to
|
* in every time we use the function. It would've been much easier to
|
||||||
* use a gcc-style varargs macro, but not everybody uses gcc.
|
* use a gcc-style varargs macro, but not everybody uses gcc.
|
||||||
|
*
|
||||||
|
* TODO: Visual C++ has vararg macros now; time to replace this.
|
||||||
*/
|
*/
|
||||||
#ifdef DEBUG_MSGS
|
#ifdef HAS__FUNCTION__
|
||||||
#ifdef HAS__FUNCTION__
|
# define _FUNCTION_ __FUNCTION__
|
||||||
#define _FUNCTION_ __FUNCTION__
|
#else
|
||||||
#else
|
# define _FUNCTION_ ""
|
||||||
#define _FUNCTION_ ""
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
|
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
|
||||||
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
|
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
|
||||||
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
|
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
|
||||||
#define DebugShowError(err) \
|
|
||||||
|
#ifdef DEBUG_MSGS
|
||||||
|
# define DebugShowError(err) \
|
||||||
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
|
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
|
||||||
true, err, "(DEBUG)");
|
true, err, "(DEBUG)");
|
||||||
#else
|
#else
|
||||||
#define NU_BLOB pArchive, "", 0, "", false
|
# define DebugShowError(err) ((void)0)
|
||||||
#define NU_BLOB_DEBUG pArchive, "", 0, "", true
|
|
||||||
#define NU_NILBLOB NULL, "", 0, "", false
|
|
||||||
#define DebugShowError(err) ((void)0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -76,8 +76,24 @@ void DebugLog::Log(LogSeverity severity, const char* file, int line,
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
char textBuf[4096];
|
char textBuf[4096];
|
||||||
|
|
||||||
|
// TODO: _vsnprintf() doesn't deal with "%ls" well. If it encounters a
|
||||||
|
// character it can't translate, it stops and returns -1. This is
|
||||||
|
// very annoying if you're trying to print a wide-char filename that
|
||||||
|
// includes characters that aren't in CP-1252.
|
||||||
|
//
|
||||||
|
// We can probably fix this by converting "format" to a wide string,
|
||||||
|
// printing with _vsnwprintf(), and then converting the output back
|
||||||
|
// to narrow manually (selecting options that prevent it from stopping
|
||||||
|
// mid-string on failure). As an optimization, we only need to do this
|
||||||
|
// if the format string includes a "%ls" specifier.
|
||||||
|
//
|
||||||
|
// The interpretation of plain "%s" will change if we use a wide printf
|
||||||
|
// function (it's a Microsoft extension, not ANSI behavior), so we'd also
|
||||||
|
// want to check for "%s" and complain if we see it. All callers must
|
||||||
|
// use explicit "%hs" or "%ls".
|
||||||
|
|
||||||
va_start(argptr, format);
|
va_start(argptr, format);
|
||||||
_vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
|
(void) _vsnprintf(textBuf, NELEM(textBuf) - 1, format, argptr);
|
||||||
va_end(argptr);
|
va_end(argptr);
|
||||||
textBuf[NELEM(textBuf) - 1] = '\0';
|
textBuf[NELEM(textBuf) - 1] = '\0';
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,30 @@ public:
|
||||||
*/
|
*/
|
||||||
static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep);
|
static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test to see if a wide-to-narrow filename conversion failed.
|
||||||
|
*
|
||||||
|
* Returns true if all is well, false with *pErrMsg set if something
|
||||||
|
* went wrong.
|
||||||
|
*/
|
||||||
|
static bool TestNarrowConversion(const CString& original,
|
||||||
|
const CStringA& converted, CString* pErrMsg) {
|
||||||
|
int index = converted.ReverseFind('?');
|
||||||
|
if (index < 0) {
|
||||||
|
// no '?' is good
|
||||||
|
} else if (index == 2 && converted.Left(4) == "\\\\?\\") {
|
||||||
|
// Win32 file namespace path strings start with "\\?\". If that's
|
||||||
|
// the first occurrence of '?', we're still good.
|
||||||
|
} else {
|
||||||
|
// This is most likely the result of a failed wide-to-narrow
|
||||||
|
// string conversion.
|
||||||
|
pErrMsg->Format(L"Unable to open '%ls' -- Unicode filename "
|
||||||
|
L"conversion is invalid ('%hs')",
|
||||||
|
(LPCWSTR) original, (LPCSTR) converted);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
DECLARE_COPY_AND_OPEQ(PathName)
|
DECLARE_COPY_AND_OPEQ(PathName)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue