/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Main window management. */ #include "stdafx.h" #include "Main.h" #include "mdc.h" #include "AboutDlg.h" #include "ProgressDlg.h" #include "resource.h" #include "../diskimg/DiskImg.h" #include "../zlib/zlib.h" const WCHAR* kWebSiteURL = L"http://www.a2ciderpress.com/"; BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) ON_COMMAND(IDM_FILE_SCAN, OnFileScan) ON_COMMAND(IDM_FILE_EXIT, OnFileExit) ON_COMMAND(IDM_HELP_WEBSITE, OnHelpWebSite) ON_COMMAND(IDM_HELP_ABOUT, OnHelpAbout) END_MESSAGE_MAP() MainWindow::MainWindow() { static const WCHAR* kAppName = L"MDC"; CString wndClass = AfxRegisterWndClass( CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, gMyApp.LoadStandardCursor(IDC_ARROW), (HBRUSH) (COLOR_WINDOW + 1), gMyApp.LoadIcon(IDI_MDC) ); Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/, rectDefault, NULL, MAKEINTRESOURCE(IDC_MDC)); LoadAccelTable(MAKEINTRESOURCE(IDC_MDC)); // initialize some OLE garbage //AfxOleInit(); // required by MFC if Rich Edit controls are used //AfxInitRichEdit(); DiskImgLib::Global::SetDebugMsgHandler(DebugMsgHandler); DiskImgLib::Global::AppInit(); NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); //fTitleAnimation = 0; fCancelFlag = false; } MainWindow::~MainWindow() { DiskImgLib::Global::AppCleanup(); } void MainWindow::OnFileExit(void) { // Handle Exit item by sending a close request. SendMessage(WM_CLOSE, 0, 0); } void MainWindow::OnHelpWebSite(void) { // Go to the CiderPress web site. int err; err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL, SW_SHOWNORMAL); if (err <= 32) { CString msg; msg.Format(L"Unable to launch web browser (err=%d).", err); ShowFailureMsg(this, msg, IDS_FAILED); } } void MainWindow::OnHelpAbout(void) { int result; AboutDlg dlg(this); result = dlg.DoModal(); LOGI("HelpAbout returned %d", result); } void MainWindow::OnFileScan(void) { if (0) { CString msg; CheckedLoadString(&msg, IDS_MUST_REGISTER); ShowFailureMsg(this, msg, IDS_APP_TITLE); } else { ScanFiles(); } } BOOL MainWindow::PeekAndPump(void) { MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!AfxGetApp()->PumpMessage()) { ::PostQuitMessage(0); return FALSE; } } LONG lIdle = 0; while (AfxGetApp()->OnIdle(lIdle++)) ; return TRUE; } /* * ========================================================================== * Disk image processing * ========================================================================== */ /*static*/ void MainWindow::DebugMsgHandler(const char* file, int line, const char* msg) { ASSERT(file != NULL); ASSERT(msg != NULL); LOG_BASE(DebugLog::LOG_INFO, file, line, " %hs", msg); } /*static*/ NuResult MainWindow::NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) { const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; LOG_BASE(pErrorMessage->isDebug ? DebugLog::LOG_DEBUG : DebugLog::LOG_WARN, pErrorMessage->file, pErrorMessage->line, " %hs", pErrorMessage->message); return kNuOK; } const int kLocalFssep = '\\'; struct ScanOpts { FILE* outfp; ProgressDlg* pProgress; }; void MainWindow::ScanFiles(void) { WCHAR curDir[MAX_PATH] = L""; CString errMsg, newDir; bool doResetDir = false; ScanOpts scanOpts; memset(&scanOpts, 0, sizeof(scanOpts)); // choose input files SelectFilesDialog chooseFiles(L"IDD_CHOOSE_FILES", false, this); chooseFiles.SetWindowTitle(L"Choose Files..."); INT_PTR retval = chooseFiles.DoModal(); if (retval != IDOK) { return; } // choose output file; use an Explorer-style dialog for consistency CString outPath; CFileDialog dlg(FALSE, L"txt", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN, L"Text Files (*.txt)|*.txt|All Files (*.*)|*.*||", this, 0, FALSE /*disable Vista style*/); dlg.m_ofn.lpstrTitle = L"Save Output As..."; wcscpy(dlg.m_ofn.lpstrFile, L"mdc-out.txt"); if (dlg.DoModal() != IDOK) { goto bail; } outPath = dlg.GetPathName(); LOGI("NEW FILE '%ls'", (LPCWSTR) outPath); scanOpts.outfp = _wfopen(outPath, L"w"); if (scanOpts.outfp == NULL) { ShowFailureMsg(this, "Unable to open output file", IDS_FAILED); goto bail; } int32_t major, minor, bug; DiskImgLib::Global::GetVersion(&major, &minor, &bug); fprintf(scanOpts.outfp, "MDC for Windows v%d.%d.%d (DiskImg library v%ld.%ld.%ld)\n", kAppMajorVersion, kAppMinorVersion, kAppBugVersion, major, minor, bug); fprintf(scanOpts.outfp, "Copyright (C) 2014 by faddenSoft, LLC. All rights reserved.\n"); fprintf(scanOpts.outfp, "MDC is part of CiderPress, available from http://www.a2ciderpress.com/.\n"); NuGetVersion(&major, &minor, &bug, NULL, NULL); fprintf(scanOpts.outfp, "Linked against NufxLib v%ld.%ld.%ld and zlib v%hs\n", major, minor, bug, zlibVersion()); fprintf(scanOpts.outfp, "\n"); /* change to base directory */ if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { errMsg = L"Unable to get current directory."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } newDir = chooseFiles.GetDirectory(); if (SetCurrentDirectory(newDir) == false) { errMsg.Format(L"Unable to change current directory to '%ls'.", (LPCWSTR) newDir); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } doResetDir = true; time_t now; now = time(NULL); fprintf(scanOpts.outfp, "Run started at %.24hs in '%ls'\n\n", ctime(&now), (LPCWSTR) newDir); /* obstruct input to the main window */ EnableWindow(FALSE); /* create a modeless dialog with a cancel button */ scanOpts.pProgress = new ProgressDlg; if (scanOpts.pProgress == NULL) goto bail; scanOpts.pProgress->fpCancelFlag = &fCancelFlag; fCancelFlag = false; if (scanOpts.pProgress->Create(this) == FALSE) { LOGI("WARNING: ProgressDlg init failed"); ASSERT(false); } else { scanOpts.pProgress->CenterWindow(this); } time_t start, end; start = time(NULL); /* start cranking */ const CStringArray& arr = chooseFiles.GetFileNames(); for (int i = 0; i < arr.GetCount(); i++) { const CString& name = arr.GetAt(i); if (Process(name, &scanOpts, &errMsg) != 0) { LOGI("Skipping '%ls': %ls.", (LPCWSTR) name, (LPCWSTR) errMsg); } if (fCancelFlag) { LOGI("Canceled by user"); MessageBox(L"Canceled!", L"MDC", MB_OK); goto bail; } } end = time(NULL); fprintf(scanOpts.outfp, "\nScan completed in %ld seconds.\n", (long) (end - start)); { SetWindowText(L"MDC Done!"); CString doneMsg = L"Processing completed."; CString appName; CheckedLoadString(&appName, IDS_APP_TITLE); scanOpts.pProgress->MessageBox(doneMsg, appName, MB_OK|MB_ICONINFORMATION); } bail: if (scanOpts.outfp != NULL) fclose(scanOpts.outfp); if (doResetDir && SetCurrentDirectory(curDir) == false) { errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); ShowFailureMsg(this, errMsg, IDS_FAILED); // bummer } // restore the main window EnableWindow(TRUE); if (scanOpts.pProgress != NULL) scanOpts.pProgress->DestroyWindow(); SetWindowText(L"MDC"); } /* * Directory structure and functions, modified from zDIR in Info-Zip sources. */ typedef struct Win32dirent { DWORD d_attr; WCHAR d_name[MAX_PATH]; int d_first; HANDLE d_hFindFile; } Win32dirent; static const WCHAR kWildMatchAll[] = L"*.*"; Win32dirent* MainWindow::OpenDir(const WCHAR* name) { Win32dirent* dir = NULL; WCHAR* tmpStr = NULL; WCHAR* cp; WIN32_FIND_DATA fnd; dir = (Win32dirent*) malloc(sizeof(*dir)); tmpStr = (WCHAR*) malloc((wcslen(name) + wcslen(kWildMatchAll) + 2) * sizeof(WCHAR)); if (dir == NULL || tmpStr == NULL) goto failed; wcscpy(tmpStr, name); cp = tmpStr + wcslen(tmpStr); /* don't end in a colon (e.g. "C:") */ if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, ':') == (cp - 1)) *cp++ = '.'; /* must end in a slash */ if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, kLocalFssep) != (cp - 1)) *cp++ = kLocalFssep; wcscpy(cp, kWildMatchAll); dir->d_hFindFile = FindFirstFile(tmpStr, &fnd); if (dir->d_hFindFile == INVALID_HANDLE_VALUE) goto failed; wcscpy(dir->d_name, fnd.cFileName); dir->d_attr = fnd.dwFileAttributes; dir->d_first = 1; bail: free(tmpStr); return dir; failed: free(dir); dir = NULL; goto bail; } Win32dirent* MainWindow::ReadDir(Win32dirent* dir) { if (dir->d_first) dir->d_first = 0; else { WIN32_FIND_DATA fnd; if (!FindNextFile(dir->d_hFindFile, &fnd)) return NULL; wcscpy(dir->d_name, fnd.cFileName); dir->d_attr = (unsigned char) fnd.dwFileAttributes; } return dir; } void MainWindow::CloseDir(Win32dirent* dir) { if (dir == NULL) return; FindClose(dir->d_hFindFile); free(dir); } int MainWindow::Process(const WCHAR* pathname, ScanOpts* pScanOpts, CString* pErrMsg) { bool exists, isDir, isReadable; struct _stat sb; int result = -1; if (fCancelFlag) return -1; ASSERT(pathname != NULL); ASSERT(pErrMsg != NULL); PathName checkPath(pathname); int ierr = checkPath.CheckFileStatus(&sb, &exists, &isReadable, &isDir); if (ierr != 0) { pErrMsg->Format(L"Unexpected error while examining '%ls': %hs", pathname, strerror(ierr)); goto bail; } if (!exists) { pErrMsg->Format(L"Couldn't find '%ls'", pathname); goto bail; } if (!isReadable) { pErrMsg->Format(L"File '%ls' isn't readable", pathname); goto bail; } if (isDir) { result = ProcessDirectory(pathname, pScanOpts, pErrMsg); goto bail; } (void) ScanDiskImage(pathname, pScanOpts); result = 0; bail: if (result != 0 && pErrMsg->IsEmpty()) { pErrMsg->Format(L"Unable to add file '%ls'", pathname); } return result; } int MainWindow::ProcessDirectory(const WCHAR* dirName, ScanOpts* pScanOpts, CString* pErrMsg) { Win32dirent* dirp = NULL; Win32dirent* entry; WCHAR nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ WCHAR fssep; int len; int result = -1; ASSERT(dirName != NULL); ASSERT(pErrMsg != NULL); LOGI("+++ DESCEND: '%ls'", (LPCWSTR) dirName); dirp = OpenDir(dirName); if (dirp == NULL) { pErrMsg->Format(L"Failed on '%ls': %hs", dirName, strerror(errno)); goto bail; } fssep = kLocalFssep; /* could use readdir_r, but we don't care about reentrancy here */ while ((entry = ReadDir(dirp)) != NULL) { /* skip the dotsies */ if (wcscmp(entry->d_name, L".") == 0 || wcscmp(entry->d_name, L"..") == 0) continue; len = wcslen(dirName); if (len + wcslen(entry->d_name) + 2 > MAX_PATH) { LOGE("ERROR: Filename exceeds %d bytes: %ls%c%ls", MAX_PATH, dirName, fssep, entry->d_name); goto bail; } /* form the new name, inserting an fssep if needed */ wcscpy(nbuf, dirName); if (dirName[len - 1] != fssep) { nbuf[len++] = fssep; } wcscpy(nbuf+len, entry->d_name); result = Process(nbuf, pScanOpts, pErrMsg); if (result != 0) goto bail; } result = 0; bail: if (dirp != NULL) (void)CloseDir(dirp); return result; } int MainWindow::ScanDiskImage(const WCHAR* pathName, ScanOpts* pScanOpts) { ASSERT(pathName != NULL); ASSERT(pScanOpts != NULL); ASSERT(pScanOpts->outfp != NULL); DIError dierr; CString errMsg; DiskImg diskImg; DiskFS* pDiskFS = NULL; PathName path(pathName); CString ext = path.GetExtension(); /* first, some housekeeping */ PeekAndPump(); pScanOpts->pProgress->SetCurrentFile(pathName); if (fCancelFlag) return -1; CString title; title = L"MDC "; title += PathName::FilenameOnly(pathName, '\\'); SetWindowText(title); fprintf(pScanOpts->outfp, "File: %ls\n", pathName); fflush(pScanOpts->outfp); // in case we crash if (!ext.IsEmpty()) { /* delete the leading '.' */ ext.Delete(0, 1); } CStringA pathNameA(pathName); dierr = diskImg.OpenImage(pathNameA, '\\', true); if (dierr != kDIErrNone) { errMsg.Format(L"Unable to open '%ls': %hs", pathName, DiskImgLib::DIStrError(dierr)); goto bail; } dierr = diskImg.AnalyzeImage(); if (dierr != kDIErrNone) { errMsg.Format(L"Analysis of '%ls' failed: %hs", pathName, DiskImgLib::DIStrError(dierr)); goto bail; } if (diskImg.GetFSFormat() == DiskImg::kFormatUnknown || diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { errMsg.Format(L"Unable to identify filesystem on '%ls'", pathName); goto bail; } /* create an appropriate DiskFS object */ pDiskFS = diskImg.OpenAppropriateDiskFS(); if (pDiskFS == NULL) { /* unknown FS should've been caught above! */ ASSERT(false); errMsg.Format(L"Format of '%ls' not recognized.", pathName); goto bail; } pDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); /* object created; prep it */ dierr = pDiskFS->Initialize(&diskImg, DiskFS::kInitFull); if (dierr != kDIErrNone) { errMsg.Format(L"Error reading list of files from disk: %hs", DiskImgLib::DIStrError(dierr)); goto bail; } int kbytes; if (pDiskFS->GetDiskImg()->GetHasBlocks()) kbytes = pDiskFS->GetDiskImg()->GetNumBlocks() / 2; else if (pDiskFS->GetDiskImg()->GetHasSectors()) kbytes = (pDiskFS->GetDiskImg()->GetNumTracks() * pDiskFS->GetDiskImg()->GetNumSectPerTrack()) / 4; else kbytes = 0; fprintf(pScanOpts->outfp, "Disk: %hs%hs (%dKB)\n", pDiskFS->GetVolumeID(), pDiskFS->GetFSDamaged() ? " [*]" : "", kbytes); fprintf(pScanOpts->outfp, " Name Type Auxtyp Modified" " Format Length\n"); fprintf(pScanOpts->outfp, "------------------------------------------------------" "------------------------\n"); if (LoadDiskFSContents(pDiskFS, "", pScanOpts) != 0) { errMsg.Format(L"Failed while loading contents of '%ls'.", pathName); goto bail; } fprintf(pScanOpts->outfp, "------------------------------------------------------" "------------------------\n\n"); bail: delete pDiskFS; //PeekAndPump(); if (!errMsg.IsEmpty()) { fprintf(pScanOpts->outfp, "Failed: %ls\n\n", (LPCWSTR) errMsg); return -1; } else { return 0; } } void MainWindow::AnalyzeFile(const A2File* pFile, RecordKind* pRecordKind, LONGLONG* pTotalLen, LONGLONG* pTotalCompLen) { if (pFile->IsVolumeDirectory()) { /* volume dir entry */ ASSERT(pFile->GetRsrcLength() < 0); *pRecordKind = kRecordKindVolumeDir; *pTotalLen = pFile->GetDataLength(); *pTotalCompLen = pFile->GetDataLength(); } else if (pFile->IsDirectory()) { /* directory entry */ ASSERT(pFile->GetRsrcLength() < 0); *pRecordKind = kRecordKindDirectory; *pTotalLen = pFile->GetDataLength(); *pTotalCompLen = pFile->GetDataLength(); } else if (pFile->GetRsrcLength() >= 0) { /* has resource fork */ *pRecordKind = kRecordKindForkedFile; *pTotalLen = pFile->GetDataLength() + pFile->GetRsrcLength(); *pTotalCompLen = pFile->GetDataSparseLength() + pFile->GetRsrcSparseLength(); } else { /* just data fork */ *pRecordKind = kRecordKindFile; *pTotalLen = pFile->GetDataLength(); *pTotalCompLen = pFile->GetDataSparseLength(); } } bool MainWindow::IsRecordReadOnly(int access) { if (access == 0x21L || access == 0x01L) return true; else return false; } /* ProDOS file type names; must be entirely in upper case */ static const char gFileTypeNames[256][4] = { "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", "$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F", "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" }; /*static*/ const char* MainWindow::GetFileTypeString(unsigned long fileType) { if (fileType < NELEM(gFileTypeNames)) return gFileTypeNames[fileType]; else return "???"; } /* * Sanitize a string. The Mac likes to stick control characters into * things, e.g. ^C and ^M, and uses high ASCII for special characters. * * TODO(Unicode): we could do a Mac OS Roman to Unicode conversion, but * we'd probably want to output UTF-8 (which Windows accessories like * Notepad *are* able to read). */ static void MacSanitize(char* str) { while (*str != '\0') { *str = DiskImg::MacToASCII(*str); str++; } } int MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, ScanOpts* pScanOpts) { static const char* kBlankFileNameMOR = ""; DiskFS::SubVolume* pSubVol = NULL; A2File* pFile; ASSERT(pDiskFS != NULL); pFile = pDiskFS->GetNextFile(NULL); for ( ; pFile != NULL; pFile = pDiskFS->GetNextFile(pFile)) { CStringA subVolName, dispName; RecordKind recordKind; LONGLONG totalLen, totalCompLen; char tmpbuf[16]; AnalyzeFile(pFile, &recordKind, &totalLen, &totalCompLen); if (recordKind == kRecordKindVolumeDir) { /* this is a volume directory */ LOGI("Not displaying volume dir '%hs'", pFile->GetPathName()); continue; } /* prepend volName for sub-volumes; must be valid Win32 dirname */ if (volName[0] != '\0') subVolName.Format("_%hs", volName); const char* ccp = pFile->GetPathName(); ASSERT(ccp != NULL); if (strlen(ccp) == 0) ccp = kBlankFileNameMOR; CStringA path(ccp); if (DiskImg::UsesDOSFileStructure(pFile->GetFSFormat()) && 0) { InjectLowercase(&path); } if (subVolName.IsEmpty()) dispName = path; else { dispName = subVolName; dispName += ':'; dispName += path; } /* strip out ctrl chars and high ASCII in HFS names */ // TODO: consider having a Unicode output mode MacSanitize(dispName.GetBuffer(0)); dispName.ReleaseBuffer(); ccp = dispName; int len = strlen(ccp); if (len <= 32) { fprintf(pScanOpts->outfp, "%c%-32.32s ", IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', ccp); } else { fprintf(pScanOpts->outfp, "%c..%-30.30s ", IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', ccp + len - 30); } switch (recordKind) { case kRecordKindUnknown: fprintf(pScanOpts->outfp, "%hs- $%04lX ", GetFileTypeString(pFile->GetFileType()), pFile->GetAuxType()); break; case kRecordKindDisk: sprintf(tmpbuf, "%I64dk", totalLen / 1024); fprintf(pScanOpts->outfp, "Disk %-6hs ", tmpbuf); break; case kRecordKindFile: case kRecordKindForkedFile: case kRecordKindDirectory: if (pDiskFS->GetDiskImg()->GetFSFormat() == DiskImg::kFormatMacHFS) { if (recordKind != kRecordKindDirectory && pFile->GetFileType() >= 0 && pFile->GetFileType() <= 0xff && pFile->GetAuxType() >= 0 && pFile->GetAuxType() <= 0xffff) { /* ProDOS type embedded in HFS */ fprintf(pScanOpts->outfp, "%hs%c $%04lX ", GetFileTypeString(pFile->GetFileType()), recordKind == kRecordKindForkedFile ? '+' : ' ', pFile->GetAuxType()); } else { char typeStr[5]; char creatorStr[5]; unsigned long val; val = pFile->GetAuxType(); creatorStr[0] = (unsigned char) (val >> 24); creatorStr[1] = (unsigned char) (val >> 16); creatorStr[2] = (unsigned char) (val >> 8); creatorStr[3] = (unsigned char) val; creatorStr[4] = '\0'; val = pFile->GetFileType(); typeStr[0] = (unsigned char) (val >> 24); typeStr[1] = (unsigned char) (val >> 16); typeStr[2] = (unsigned char) (val >> 8); typeStr[3] = (unsigned char) val; typeStr[4] = '\0'; MacSanitize(creatorStr); MacSanitize(typeStr); if (recordKind == kRecordKindDirectory) { fprintf(pScanOpts->outfp, "DIR %-4s ", creatorStr); } else { fprintf(pScanOpts->outfp, "%-4s%c %-4s ", typeStr, pFile->GetRsrcLength() > 0 ? '+' : ' ', creatorStr); } } } else { fprintf(pScanOpts->outfp, "%hs%c $%04lX ", GetFileTypeString(pFile->GetFileType()), recordKind == kRecordKindForkedFile ? '+' : ' ', pFile->GetAuxType()); } break; case kRecordKindVolumeDir: /* should've trapped this earlier */ ASSERT(0); fprintf(pScanOpts->outfp, "ERROR "); break; default: ASSERT(0); fprintf(pScanOpts->outfp, "ERROR "); break; } CString date; if (pFile->GetModWhen() == 0) FormatDate(kDateNone, &date); else FormatDate(pFile->GetModWhen(), &date); fprintf(pScanOpts->outfp, "%-15ls ", (LPCWSTR) date); const char* fmtStr; switch (pFile->GetFSFormat()) { case DiskImg::kFormatDOS33: case DiskImg::kFormatDOS32: case DiskImg::kFormatUNIDOS: case DiskImg::kFormatOzDOS: fmtStr = "DOS "; break; case DiskImg::kFormatProDOS: fmtStr = "ProDOS"; break; case DiskImg::kFormatPascal: fmtStr = "Pascal"; break; case DiskImg::kFormatCPM: fmtStr = "CP/M "; break; case DiskImg::kFormatRDOS33: case DiskImg::kFormatRDOS32: case DiskImg::kFormatRDOS3: fmtStr = "RDOS "; break; case DiskImg::kFormatMacHFS: fmtStr = "HFS "; break; case DiskImg::kFormatMSDOS: fmtStr = "MS-DOS"; break; case DiskImg::kFormatGutenberg: fmtStr = "Gutenb"; break; default: fmtStr = "??? "; break; } if (pFile->GetQuality() == A2File::kQualityDamaged) fmtStr = "BROKEN"; else if (pFile->GetQuality() == A2File::kQualitySuspicious) fmtStr = "BAD? "; fprintf(pScanOpts->outfp, "%hs ", fmtStr); #if 0 /* compute the percent size */ if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen)) fprintf(pScanOpts->outfp, "--- "); /* weird */ else if (totalLen < totalCompLen) fprintf(pScanOpts->outfp, ">100%% "); /* compression failed? */ else { sprintf(tmpbuf, "%02d%%", ComputePercent(totalCompLen, totalLen)); fprintf(pScanOpts->outfp, "%4s ", tmpbuf); } #endif if (!totalLen && totalCompLen) fprintf(pScanOpts->outfp, " ????"); /* weird */ else fprintf(pScanOpts->outfp, "%8I64d", totalLen); fprintf(pScanOpts->outfp, "\n"); } /* * Load all sub-volumes. */ pSubVol = pDiskFS->GetNextSubVolume(NULL); while (pSubVol != NULL) { const char* subVolName; int ret; subVolName = pSubVol->GetDiskFS()->GetVolumeName(); if (subVolName == NULL) subVolName = "+++"; // could probably do better than this ret = LoadDiskFSContents(pSubVol->GetDiskFS(), subVolName, pScanOpts); if (ret != 0) return ret; pSubVol = pDiskFS->GetNextSubVolume(pSubVol); } return 0; }