/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Disk editor implementation. * * Note to self: it should be possible to open an image in "nibble" mode, * switch to one of the various "sector" modes, and maybe even try out a * "block" mode for a while. Locking the editor into one particular mode * makes for a cumbersome interface when dealing with certain kinds of disks. * It should also be possible to configure the "custom" NibbleDescr and then * re-analyze the disk, possibly transferring the customization over to * the main file listing. (Perhaps the customization affects a global slot? * Probably want more than one set of custom nibble values, with the config * dialog accessible from main menu and from within disk editor.) * * A track copier would be neat too. */ #include "stdafx.h" #include "SubVolumeDialog.h" #include "DEFileDialog.h" #include "HelpTopics.h" #include "DiskEditDialog.h" /* * =========================================================================== * DiskEditDialog (base class) * =========================================================================== */ BEGIN_MESSAGE_MAP(DiskEditDialog, CDialog) ON_BN_CLICKED(IDC_DISKEDIT_DONE, OnDone) ON_BN_CLICKED(IDC_DISKEDIT_HEX, OnHexMode) ON_BN_CLICKED(IDC_DISKEDIT_DOREAD, OnDoRead) ON_BN_CLICKED(IDC_DISKEDIT_DOWRITE, OnDoWrite) ON_BN_CLICKED(IDC_DISKEDIT_PREV, OnReadPrev) ON_BN_CLICKED(IDC_DISKEDIT_NEXT, OnReadNext) ON_BN_CLICKED(IDC_DISKEDIT_SUBVOLUME, OnSubVolume) ON_BN_CLICKED(IDC_DISKEDIT_OPENFILE, OnOpenFile) ON_BN_CLICKED(IDHELP, OnHelp) ON_CBN_SELCHANGE(IDC_DISKEDIT_NIBBLE_PARMS, OnNibbleParms) ON_WM_HELPINFO() END_MESSAGE_MAP() /* * Initialize the controls. */ BOOL DiskEditDialog::OnInitDialog(void) { ASSERT(!fFileName.IsEmpty()); ASSERT(fpDiskFS != nil); /* * Disable the write button. */ if (fReadOnly) { CButton* pButton = (CButton*) GetDlgItem(IDC_DISKEDIT_DOWRITE); ASSERT(pButton != nil); pButton->EnableWindow(FALSE); } /* * Use modified spin controls so we're not limited to 16 bits. */ ReplaceSpinCtrl(&fTrackSpinner, IDC_DISKEDIT_TRACKSPIN, IDC_DISKEDIT_TRACK); ReplaceSpinCtrl(&fSectorSpinner, IDC_DISKEDIT_SECTORSPIN, IDC_DISKEDIT_SECTOR); /* * Configure the RichEdit control. */ CRichEditCtrl* pEdit = (CRichEditCtrl*) GetDlgItem(IDC_DISKEDIT_EDIT); ASSERT(pEdit != nil); /* set the font to 10-point Courier New */ CHARFORMAT cf; cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_FACE | CFM_SIZE; wcscpy(cf.szFaceName, L"Courier New"); cf.yHeight = 10 * 20; // point size in twips BOOL cc = pEdit->SetDefaultCharFormat(cf); if (cc == FALSE) { WMSG0("SetDefaultCharFormat failed?\n"); ASSERT(FALSE); } /* set to read-only */ pEdit->SetReadOnly(); /* retain the selection even if we lose focus [can't do this in OnInit] */ pEdit->SetOptions(ECOOP_OR, ECO_SAVESEL); /* * Disable the sub-volume and/or file open buttons if the DiskFS doesn't * have the appropriate stuff inside. */ if (fpDiskFS->GetNextFile(nil) == nil) { CWnd* pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); pWnd->EnableWindow(FALSE); } if (fpDiskFS->GetNextSubVolume(nil) == nil) { CWnd* pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); pWnd->EnableWindow(FALSE); } /* * Configure the nibble parm drop-list in an appropriate fashion. */ InitNibbleParmList(); /* * If this is a sub-volume edit window, pop us up slightly offset from the * parent window so the user can see that there's more than one thing * open. */ if (fPositionShift != 0) { CRect rect; GetWindowRect(&rect); rect.top += fPositionShift; rect.left += fPositionShift; rect.bottom += fPositionShift; rect.right += fPositionShift; MoveWindow(&rect); } /* * Set the window title. */ CString title("Disk Viewer - "); title += fFileName; if (fpDiskFS->GetVolumeID() != nil) { title += " ("; title += fpDiskFS->GetVolumeID(); title += ")"; } SetWindowText(title); return TRUE; } /* * Initialize the nibble parm drop-list. */ void DiskEditDialog::InitNibbleParmList(void) { ASSERT(fpDiskFS != nil); DiskImg* pDiskImg = fpDiskFS->GetDiskImg(); CComboBox* pCombo; pCombo = (CComboBox*) GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); ASSERT(pCombo != nil); if (pDiskImg->GetHasNibbles()) { const DiskImg::NibbleDescr* pTable; const DiskImg::NibbleDescr* pCurrent; int i, count; pTable = pDiskImg->GetNibbleDescrTable(&count); if (pTable == nil || count <= 0) { WMSG2("WHOOPS: nibble parm got table=0x%08lx, count=%d\n", (long) pTable, count); return; } pCurrent = pDiskImg->GetNibbleDescr(); /* configure the selection to match the disk analysis */ int dflt = -1; if (pCurrent != nil) { for (i = 0; i < count; i++) { if (memcmp(&pTable[i], pCurrent, sizeof(*pCurrent)) == 0) { WMSG1(" NibbleParm match on entry %d\n", i); dflt = i; break; } } if (dflt == -1) { WMSG0(" GLITCH: no match on nibble descr in table?!\n"); dflt = 0; } } for (i = 0; i < count; i++) { if (pTable[i].numSectors > 0) { CString description(pTable[i].description); pCombo->AddString(description); } else { /* only expecting this on the last, "custom" entry */ ASSERT(i == count-1); } } pCombo->SetCurSel(dflt); } else { pCombo->AddString(L"Nibble Parms"); pCombo->SetCurSel(0); pCombo->EnableWindow(FALSE); } } /* * Replace a spin button with our improved version. */ int DiskEditDialog::ReplaceSpinCtrl(MySpinCtrl* pNewSpin, int idSpin, int idEdit) { CSpinButtonCtrl* pSpin; // CRect rect; DWORD style; pSpin = (CSpinButtonCtrl*)GetDlgItem(idSpin); if (pSpin == nil) return -1; // pSpin->GetWindowRect(&rect); // ScreenToClient(&rect); style = pSpin->GetStyle(); style &= ~(UDS_SETBUDDYINT); //style |= UDS_AUTOBUDDY; ASSERT(!(style & UDS_AUTOBUDDY)); pSpin->DestroyWindow(); pNewSpin->Create(style, CRect(0,0,0,0), this, idSpin); pNewSpin->SetBuddy(GetDlgItem(idEdit)); return 0; } /* * Special keypress handling. */ BOOL DiskEditDialog::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { //WMSG0("RETURN!\n"); LoadData(); return TRUE; } return CDialog::PreTranslateMessage(pMsg); } /* * F1 key hit, or '?' button in title bar used to select help for an * item in the dialog. */ BOOL DiskEditDialog::OnHelpInfo(HELPINFO* lpHelpInfo) { WMSG4("HELP: size=%d contextType=%d ctrlID=0x%x contextID=0x%08lx\n", lpHelpInfo->cbSize, lpHelpInfo->iContextType, lpHelpInfo->iCtrlId, lpHelpInfo->dwContextId); DWORD context = lpHelpInfo->iCtrlId; /* map all of the track/sector selection stuff to one item */ if (context == IDC_DISKEDIT_TRACK || context == IDC_DISKEDIT_TRACKSPIN || context == IDC_STEXT_TRACK || context == IDC_DISKEDIT_SECTOR || context == IDC_DISKEDIT_SECTORSPIN || context == IDC_STEXT_SECTOR) { context = IDC_DISKEDIT_TRACK; } WinHelp(context, HELP_CONTEXTPOPUP); return TRUE; // indicate success?? } /* * User pressed the "Help" button. */ void DiskEditDialog::OnHelp(void) { WinHelp(HELP_TOPIC_DISKEDIT, HELP_CONTEXT); } /* * Handle the "Done" button. We don't use IDOK because we don't want * to bail out of the dialog. */ void DiskEditDialog::OnDone(void) { WMSG0("DiskEditDialog OnDone\n"); EndDialog(IDOK); } /* * Toggle the spin button / edit controls. */ void DiskEditDialog::OnHexMode(void) { int base; CButton* pButton = (CButton*) GetDlgItem(IDC_DISKEDIT_HEX); ASSERT(pButton != nil); if (pButton->GetCheck() == 0) base = 10; else base = 16; SetSpinMode(IDC_DISKEDIT_TRACKSPIN, base); SetSpinMode(IDC_DISKEDIT_SECTORSPIN, base); } /* * Create a new instance of the disk edit dialog, for a sub-volume. */ void DiskEditDialog::OnSubVolume(void) { SubVolumeDialog subv(this); bool showAsBlocks; subv.Setup(fpDiskFS); if (subv.DoModal() == IDOK) { WMSG1("SELECTED subv %d\n", subv.fListBoxIndex); DiskFS::SubVolume* pSubVol = fpDiskFS->GetNextSubVolume(nil); if (pSubVol == nil) return; while (subv.fListBoxIndex-- > 0) { pSubVol = fpDiskFS->GetNextSubVolume(pSubVol); } ASSERT(pSubVol != nil); BlockEditDialog blockEdit; SectorEditDialog sectorEdit; DiskEditDialog* pEditDialog; showAsBlocks = pSubVol->GetDiskImg()->ShowAsBlocks(); if (showAsBlocks) pEditDialog = &blockEdit; else pEditDialog = §orEdit; CString volumeID(fpDiskFS->GetVolumeID()); pEditDialog->Setup(pSubVol->GetDiskFS(), volumeID); pEditDialog->SetPositionShift(8); (void) pEditDialog->DoModal(); } } /* * Change the mode of a spin button. The Windows control doesn't * immediately update with a hex display, so we do it manually. (Our * replacement class does this correctly, but I'm leaving the code alone * for now.) */ void DiskEditDialog::SetSpinMode(int id, int base) { CString valStr; ASSERT(base == 10 || base == 16); MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); if (pSpin == nil) { // expected behavior in "block" mode for sector button WMSG1("Couldn't find spin button %d\n", id); return; } long val = pSpin->GetPos(); if (val & 0xff000000) { WMSG0("NOTE: hex transition while value is invalid\n"); val = 0; } if (base == 10) valStr.Format(L"%d", val); else valStr.Format(L"%X", val); pSpin->SetBase(base); pSpin->GetBuddy()->SetWindowText(valStr); WMSG2("Set spin button base to %d val=%d\n", base, val); } /* * Read a value from a spin control. * * Returns 0 on success, -1 if the return value from the spin control was * invalid. In the latter case, an error dialog will be displayed. */ int DiskEditDialog::ReadSpinner(int id, long* pVal) { MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); ASSERT(pSpin != nil); long val = pSpin->GetPos(); if (val & 0xff000000) { /* error */ CString msg; CString err; err.LoadString(IDS_ERROR); int lower, upper; pSpin->GetRange32(lower, upper); msg.Format(L"Please enter a value between %d and %d (0x%x and 0x%x).", lower, upper, lower, upper); MessageBox(msg, err, MB_OK|MB_ICONEXCLAMATION); return -1; } *pVal = val; return 0; } /* * Set the value of a spin control. */ void DiskEditDialog::SetSpinner(int id, long val) { MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); ASSERT(pSpin != nil); /* sanity check */ int lower, upper; pSpin->GetRange32(lower, upper); ASSERT(val >= lower && val <= upper); pSpin->SetPos(val); } /* * Convert a chunk of data into a hex dump, and stuff it into the edit control. */ void DiskEditDialog::DisplayData(const BYTE* srcBuf, int size) { WCHAR textBuf[80 * 16 * 2]; WCHAR* cp; int i, j; ASSERT(srcBuf != nil); ASSERT(size == kSectorSize || size == kBlockSize); CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); ASSERT(pEdit != nil); /* * If we have an alert message, show that instead. */ if (!fAlertMsg.IsEmpty()) { const int kWidth = 72; int indent = (kWidth/2) - (fAlertMsg.GetLength() / 2); if (indent < 0) indent = 0; CString msg = L" " L" "; ASSERT(msg.GetLength() == kWidth); msg = msg.Left(indent); msg += fAlertMsg; for (i = 0; i < (size / 16)-2; i += 2) { textBuf[i] = '\r'; textBuf[i+1] = '\n'; } wcscpy(&textBuf[i], msg); pEdit->SetWindowText(textBuf); return; } /* * No alert, do the usual thing. */ cp = textBuf; for (i = 0; i < size/16; i++) { if (size == kSectorSize) { /* two-nybble addr */ wsprintf(cp, L" %02x: %02x %02x %02x %02x %02x %02x %02x %02x " L"%02x %02x %02x %02x %02x %02x %02x %02x ", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } else { /* three-nybble addr */ wsprintf(cp, L"%03x: %02x %02x %02x %02x %02x %02x %02x %02x " L"%02x %02x %02x %02x %02x %02x %02x %02x ", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } ASSERT(wcslen(cp) == 54); cp += 54; // strlen(cp) for (j = 0; j < 16; j++) *cp++ = PrintableChar(srcBuf[j]); *cp++ = '\r'; *cp++ = '\n'; srcBuf += 16; } /* kill the last EOL, so the cursor doesn't move past that line */ cp--; *cp = '\0'; pEdit->SetWindowText(textBuf); } /* * Display a track full of nibble data. */ void DiskEditDialog::DisplayNibbleData(const unsigned char* srcBuf, int size) { ASSERT(srcBuf != nil); ASSERT(size > 0); ASSERT(fAlertMsg.IsEmpty()); int bufSize = ((size+15) / 16) * 80; WCHAR* textBuf = new WCHAR[bufSize]; WCHAR* cp; int i; if (textBuf == nil) return; cp = textBuf; for (i = 0; size > 0; i++) { if (size >= 16) { wsprintf(cp, L"%04x: %02x %02x %02x %02x %02x %02x %02x %02x " L"%02x %02x %02x %02x %02x %02x %02x %02x", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); ASSERT(wcslen(cp) == 53); cp += 53; // strlen(cp) } else { wsprintf(cp, L"%04x:", i * 16); cp += 5; for (int j = 0; j < size; j++) { wsprintf(cp, L" %02x", srcBuf[j]); cp += 3; } } *cp++ = '\r'; *cp++ = '\n'; srcBuf += 16; size -= 16; } /* kill the last EOL, so the cursor doesn't move past that line */ cp--; *cp = '\0'; CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); ASSERT(pEdit != nil); pEdit->SetWindowText(textBuf); /* * Handle resize of edit box. We have to do this late or the scroll bar * won't appear under Win98. (Whatever.) */ if (fFirstResize) { fFirstResize = false; const int kStretchHeight = 249; CRect rect; GetWindowRect(&rect); CRect inner; pEdit->GetRect(&inner); inner.bottom += kStretchHeight; pEdit->GetWindowRect(&rect); ScreenToClient(&rect); rect.bottom += kStretchHeight; pEdit->MoveWindow(&rect); pEdit->SetRect(&inner); } delete[] textBuf; } /* * Open a file in a disk image. * * Returns a pointer to the A2File and A2FileDescr structures on success, nil * on failure. The pointer placed in "ppOpenFile" must be freed by invoking * its Close function. */ DIError DiskEditDialog::OpenFile(const WCHAR* fileName, bool openRsrc, A2File** ppFile, A2FileDescr** ppOpenFile) { A2File* pFile; A2FileDescr* pOpenFile = nil; WMSG2(" OpenFile '%ls' rsrc=%d\n", fileName, openRsrc); CStringA fileNameA(fileName); pFile = fpDiskFS->GetFileByName(fileNameA); if (pFile == nil) { CString msg, failed; msg.Format(IDS_DEFILE_FIND_FAILED, fileName); failed.LoadString(IDS_FAILED); MessageBox(msg, failed, MB_OK | MB_ICONSTOP); return kDIErrFileNotFound; } DIError dierr; dierr = pFile->Open(&pOpenFile, true, openRsrc); if (dierr != kDIErrNone) { CString msg, failed; msg.Format(IDS_DEFILE_OPEN_FAILED, fileName, DiskImgLib::DIStrError(dierr)); failed.LoadString(IDS_FAILED); MessageBox(msg, failed, MB_OK | MB_ICONSTOP); return dierr; } *ppFile = pFile; *ppOpenFile = pOpenFile; return kDIErrNone; } /* * Change the nibble parms. * * Assumes the parm list is linear and unbroken. */ void DiskEditDialog::OnNibbleParms(void) { DiskImg* pDiskImg = fpDiskFS->GetDiskImg(); CComboBox* pCombo; int sel; ASSERT(pDiskImg != nil); ASSERT(pDiskImg->GetHasNibbles()); pCombo = (CComboBox*) GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); ASSERT(pCombo != nil); sel = pCombo->GetCurSel(); if (sel == CB_ERR) return; WMSG1(" OnNibbleParms: entry %d now selected\n", sel); const DiskImg::NibbleDescr* pNibbleTable; int count; pNibbleTable = pDiskImg->GetNibbleDescrTable(&count); ASSERT(sel < count); pDiskImg->SetNibbleDescr(sel); LoadData(); } #if 0 /* * Make a "sparse" block in a file obvious by filling it with the word * "sparse". */ void DiskEditDialog::FillWithPattern(unsigned char* buf, int size, const char* pattern) { const char* cp; unsigned char* ucp; ucp = buf; cp = pattern; while (ucp < buf+size) { *ucp++ = *cp++; if (*cp == '\0') cp = pattern; } } #endif /* * =========================================================================== * SectorEditDialog * =========================================================================== */ /* * Prep the dialog. */ BOOL SectorEditDialog::OnInitDialog(void) { /* * Do base-class construction. */ DiskEditDialog::OnInitDialog(); /* * Change track/sector text. */ CString trackStr; CWnd* pWnd; trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); trackStr.Format(L"Sector (%d):", fpDiskFS->GetDiskImg()->GetNumSectPerTrack()); pWnd = GetDlgItem(IDC_STEXT_SECTOR); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); /* * Configure the spin buttons. */ MySpinCtrl* pSpin; pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); ASSERT(pSpin != nil); pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumTracks()-1); pSpin->SetPos(0); pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_SECTORSPIN); ASSERT(pSpin != nil); pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumSectPerTrack()-1); pSpin->SetPos(0); /* give us something to look at */ LoadData(); return TRUE; } /* * Load the current track/sector data into the edit control. * * Returns 0 on success, -1 on error. */ int SectorEditDialog::LoadData(void) { //WMSG0("SED LoadData\n"); ASSERT(fpDiskFS != nil); ASSERT(fpDiskFS->GetDiskImg() != nil); if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fTrack) != 0) return -1; if (ReadSpinner(IDC_DISKEDIT_SECTORSPIN, &fSector) != 0) return -1; WMSG2("LoadData reading t=%d s=%d\n", fTrack, fSector); fAlertMsg = ""; DIError dierr; dierr = fpDiskFS->GetDiskImg()->ReadTrackSector(fTrack, fSector, fSectorData); if (dierr != kDIErrNone) { WMSG1("SED sector read failed: %hs\n", DiskImgLib::DIStrError(dierr)); //CString msg; //CString err; //err.LoadString(IDS_ERROR); //msg.Format(IDS_DISKEDIT_NOREADTS, fTrack, fSector); //MessageBox(msg, err, MB_OK|MB_ICONSTOP); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADSECTOR); //return -1; } DisplayData(); return 0; } /* * Read the currently specified track/sector. */ void SectorEditDialog::OnDoRead(void) { LoadData(); } /* * Write the currently loaded track/sector. */ void SectorEditDialog::OnDoWrite(void) { MessageBox(L"Write!"); } /* * Back up to the previous track/sector. */ void SectorEditDialog::OnReadPrev(void) { if (fTrack == 0 && fSector == 0) return; if (fSector == 0) { fSector = fpDiskFS->GetDiskImg()->GetNumSectPerTrack() -1; fTrack--; } else { fSector--; } SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); LoadData(); } /* * Same as OnReadPrev, but moving forward. */ void SectorEditDialog::OnReadNext(void) { int numTracks = fpDiskFS->GetDiskImg()->GetNumTracks(); int numSects = fpDiskFS->GetDiskImg()->GetNumSectPerTrack(); if (fTrack == numTracks-1 && fSector == numSects-1) return; if (fSector == numSects-1) { fSector = 0; fTrack++; } else { fSector++; } SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); LoadData(); } /* * Open a file on the disk image. If successful, open a new edit dialog * that's in "file follow" mode. */ void SectorEditDialog::OnOpenFile(void) { DEFileDialog fileDialog(this); if (fileDialog.DoModal() == IDOK) { SectorFileEditDialog fileEdit(this, this); A2File* pFile; A2FileDescr* pOpenFile = nil; DIError dierr; dierr = OpenFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, &pFile, &pOpenFile); if (dierr != kDIErrNone) return; fileEdit.SetupFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, pFile, pOpenFile); fileEdit.SetPositionShift(8); (void) fileEdit.DoModal(); pOpenFile->Close(); } } /* * =========================================================================== * SectorFileEditDialog * =========================================================================== */ /* * Minor changes for file editing. */ BOOL SectorFileEditDialog::OnInitDialog(void) { BOOL retval; /* do base class first */ retval = SectorEditDialog::OnInitDialog(); /* disable direct entry of tracks and sectors */ CWnd* pWnd; pWnd = GetDlgItem(IDC_DISKEDIT_TRACKSPIN); ASSERT(pWnd != nil); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); ASSERT(pWnd != nil); pWnd->EnableWindow(FALSE); /* disallow opening of sub-volumes and files */ pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); pWnd->EnableWindow(FALSE); CEdit* pEdit; pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_TRACK); ASSERT(pEdit != nil); pEdit->SetReadOnly(TRUE); pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_SECTOR); ASSERT(pEdit != nil); pEdit->SetReadOnly(TRUE); /* set the window title */ CString title; CString rsrcIndic; rsrcIndic.LoadString(IDS_INDIC_RSRC); title.Format(L"Disk Viewer - %hs%ls (%ld bytes)", (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", fLength); SetWindowText(title); return retval; } /* * Load data from the current offset into the edit control. * * Returns 0 on success, -1 on error. */ int SectorFileEditDialog::LoadData(void) { ASSERT(fpDiskFS != nil); ASSERT(fpDiskFS->GetDiskImg() != nil); DIError dierr; WMSG1("SFED LoadData reading index=%d\n", fSectorIdx); #if 0 WMSG1("LoadData reading offset=%d\n", fOffset); size_t actual = 0; dierr = fpFile->Seek(fOffset, EmbeddedFD::kSeekSet); if (dierr == kDIErrNone) { dierr = fpFile->Read(fSectorData, 1 /*kSectorSize*/, &actual); } if (dierr != kDIErrNone) { CString msg, failed; failed.LoadString(IDS_FAILED); msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImg::DIStrError(dierr)); MessageBox(msg, failed, MB_OK); // TO DO: mark contents as invalid, so editing fails return -1; } if (actual != kSectorSize) { WMSG1(" SFED partial read of %d bytes\n", actual); ASSERT(actual < kSectorSize && actual >= 0); } /* * We've read the data, but we can't use it. We're a sector editor, * not a file editor, and we need to get the actual sector data without * EOF trimming or CP/M 0xe5 removal. */ fpFile->GetLastLocationRead(&fTrack, &fSector); if (fTrack == A2File::kLastWasSparse && fSector == A2File::kLastWasSparse) ; #endif fAlertMsg = ""; dierr = fpOpenFile->GetStorage(fSectorIdx, &fTrack, &fSector); if (dierr == kDIErrInvalidIndex && fSectorIdx == 0) { // no first sector; should only happen on CP/M //FillWithPattern(fSectorData, sizeof(fSectorData), _T("EMPTY ")); fAlertMsg.LoadString(IDS_DISKEDITMSG_EMPTY); } else if (dierr != kDIErrNone) { CString msg, failed; failed.LoadString(IDS_FAILED); msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImgLib::DIStrError(dierr)); MessageBox(msg, failed, MB_OK); fAlertMsg.LoadString(IDS_FAILED); // TO DO: mark contents as invalid, so editing fails return -1; } else { if (fTrack == 0 && fSector == 0) { WMSG0("LoadData Sparse sector\n"); //FillWithPattern(fSectorData, sizeof(fSectorData), _T("SPARSE ")); fAlertMsg.Format(IDS_DISKEDITMSG_SPARSE, fSectorIdx); } else { WMSG2("LoadData reading T=%d S=%d\n", fTrack, fSector); dierr = fpDiskFS->GetDiskImg()->ReadTrackSector(fTrack, fSector, fSectorData); if (dierr != kDIErrNone) { //CString msg; //CString err; //err.LoadString(IDS_ERROR); //msg.Format(IDS_DISKEDIT_NOREADTS, fTrack, fSector); //MessageBox(msg, err, MB_OK|MB_ICONSTOP); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADSECTOR); //return -1; } } } SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); CWnd* pWnd; pWnd = GetDlgItem(IDC_DISKEDIT_PREV); ASSERT(pWnd != nil); pWnd->EnableWindow(fSectorIdx > 0); if (!pWnd->IsWindowEnabled() && GetFocus() == nil) GetDlgItem(IDC_DISKEDIT_NEXT)->SetFocus(); pWnd = GetDlgItem(IDC_DISKEDIT_NEXT); ASSERT(pWnd != nil); pWnd->EnableWindow(fSectorIdx+1 < fpOpenFile->GetSectorCount()); if (!pWnd->IsWindowEnabled() && GetFocus() == nil) GetDlgItem(IDC_DISKEDIT_PREV)->SetFocus(); DisplayData(); return 0; } /* * Move to the previous sector in the file. */ void SectorFileEditDialog::OnReadPrev(void) { if (fSectorIdx == 0) return; fSectorIdx--; ASSERT(fSectorIdx >= 0); LoadData(); } /* * Move to the next sector in the file. */ void SectorFileEditDialog::OnReadNext(void) { if (fSectorIdx+1 >= fpOpenFile->GetSectorCount()) return; fSectorIdx++; ASSERT(fSectorIdx < fpOpenFile->GetSectorCount()); LoadData(); } /* * =========================================================================== * BlockEditDialog * =========================================================================== */ /* * Rearrange the DiskEdit dialog (which defaults to SectorEdit mode) to * accommodate block editing. */ BOOL BlockEditDialog::OnInitDialog(void) { /* * Get rid of the "sector" input item, and change the "track" input * item to accept blocks instead. */ CWnd* pWnd; pWnd = GetDlgItem(IDC_STEXT_SECTOR); pWnd->DestroyWindow(); pWnd = GetDlgItem(IDC_DISKEDIT_SECTOR); pWnd->DestroyWindow(); pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); pWnd->DestroyWindow(); CString blockStr; //blockStr.LoadString(IDS_BLOCK); blockStr.Format(L"Block (%d):", fpDiskFS->GetDiskImg()->GetNumBlocks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(blockStr); /* * Increase the size of the window to accommodate the larger block size. */ const int kStretchHeight = 250; CRect rect; GetWindowRect(&rect); rect.bottom += kStretchHeight; MoveWindow(&rect); CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); ASSERT(pEdit != nil); CRect inner; pEdit->GetRect(&inner); inner.bottom += kStretchHeight; pEdit->GetWindowRect(&rect); ScreenToClient(&rect); rect.bottom += kStretchHeight; pEdit->MoveWindow(&rect); pEdit->SetRect(&inner); MoveControl(this, IDC_DISKEDIT_DONE, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_OPENFILE, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_SUBVOLUME, 0, kStretchHeight); MoveControl(this, IDHELP, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_NIBBLE_PARMS, 0, kStretchHeight); /* * Do base-class construction. */ DiskEditDialog::OnInitDialog(); /* * Configure the spin button. We use the "track" spin button for blocks. */ MySpinCtrl* pSpin; pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); ASSERT(pSpin != nil); pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumBlocks()-1); pSpin->SetPos(0); /* give us something to look at */ if (LoadData() != 0) { WMSG0("WHOOPS: LoadData() failed, but we're in OnInitDialog\n"); } return TRUE; } #if 0 /* * Move a control so it maintains its same position relative to the bottom * and right edges. */ void BlockEditDialog::MoveControl(int id, int deltaX, int deltaY) { CWnd* pWnd; CRect rect; pWnd = GetDlgItem(id); ASSERT(pWnd != nil); pWnd->GetWindowRect(&rect); ScreenToClient(&rect); rect.left += deltaX; rect.right += deltaX; rect.top += deltaY; rect.bottom += deltaY; pWnd->MoveWindow(&rect, TRUE); } #endif /* * Load the current block data into the edit control. */ int BlockEditDialog::LoadData(void) { //WMSG0("BED LoadData\n"); ASSERT(fpDiskFS != nil); ASSERT(fpDiskFS->GetDiskImg() != nil); if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fBlock) != 0) return -1; WMSG1("LoadData reading block=%d\n", fBlock); fAlertMsg = ""; DIError dierr; dierr = fpDiskFS->GetDiskImg()->ReadBlock(fBlock, fBlockData); if (dierr != kDIErrNone) { WMSG1("BED block read failed: %hs\n", DiskImgLib::DIStrError(dierr)); //CString msg; //CString err; //err.LoadString(IDS_ERROR); //msg.Format(IDS_DISKEDIT_NOREADBLOCK, fBlock); //MessageBox(msg, err, MB_OK|MB_ICONSTOP); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADBLOCK); //return -1; } DisplayData(); return 0; } /* * Read the currently specified track/sector. */ void BlockEditDialog::OnDoRead(void) { LoadData(); } /* * Write the currently loaded track/sector. */ void BlockEditDialog::OnDoWrite(void) { MessageBox(L"Write!"); } /* * Back up to the previous track/sector, or (in follow-file mode) to the * previous sector in the file. */ void BlockEditDialog::OnReadPrev(void) { if (fBlock == 0) return; fBlock--; SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); LoadData(); } /* * Same as OnReadPrev, but moving forward. */ void BlockEditDialog::OnReadNext(void) { ASSERT(fpDiskFS != nil); if (fBlock == fpDiskFS->GetDiskImg()->GetNumBlocks() - 1) return; fBlock++; SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); LoadData(); } /* * Open a file on the disk image. If successful, open a new edit dialog * that's in "file follow" mode. */ void BlockEditDialog::OnOpenFile(void) { DEFileDialog fileDialog(this); if (fileDialog.DoModal() == IDOK) { BlockFileEditDialog fileEdit(this, this); A2File* pFile; A2FileDescr* pOpenFile = nil; DIError dierr; dierr = OpenFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, &pFile, &pOpenFile); if (dierr != kDIErrNone) return; fileEdit.SetupFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, pFile, pOpenFile); fileEdit.SetPositionShift(8); (void) fileEdit.DoModal(); pOpenFile->Close(); } } /* * =========================================================================== * BlockFileEditDialog * =========================================================================== */ /* * Minor changes for file editing. */ BOOL BlockFileEditDialog::OnInitDialog(void) { BOOL retval; /* do base class first */ retval = BlockEditDialog::OnInitDialog(); /* disable direct entry of tracks and Blocks */ CWnd* pWnd; pWnd = GetDlgItem(IDC_DISKEDIT_TRACKSPIN); ASSERT(pWnd != nil); pWnd->EnableWindow(FALSE); /* disallow opening of sub-volumes and files */ pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); pWnd->EnableWindow(FALSE); CEdit* pEdit; pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_TRACK); ASSERT(pEdit != nil); pEdit->SetReadOnly(TRUE); /* set the window title */ CString title; CString rsrcIndic; rsrcIndic.LoadString(IDS_INDIC_RSRC); title.Format(L"Disk Viewer - %hs%ls (%ld bytes)", (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", fLength); SetWindowText(title); return retval; } /* * Load data from the current offset into the edit control. * * Returns 0 on success, -1 on error. */ int BlockFileEditDialog::LoadData(void) { ASSERT(fpDiskFS != nil); ASSERT(fpDiskFS->GetDiskImg() != nil); DIError dierr; WMSG1("BFED LoadData reading index=%d\n", fBlockIdx); #if 0 WMSG1("LoadData reading offset=%d\n", fOffset); size_t actual = 0; dierr = fpFile->Seek(fOffset, EmbeddedFD::kSeekSet); if (dierr == kDIErrNone) { dierr = fpFile->Read(fBlockData, 1 /*kBlockSize*/, &actual); } if (dierr != kDIErrNone) { CString msg, failed; failed.LoadString(IDS_FAILED); msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImg::DIStrError(dierr)); MessageBox(msg, failed, MB_OK); // TO DO: mark contents as invalid, so editing fails return -1; } if (actual != kBlockSize) { WMSG1(" BFED partial read of %d bytes\n", actual); ASSERT(actual < kBlockSize && actual >= 0); } /* * We've read the data, but we can't use it. We're a Block editor, * not a file editor, and we need to get the actual Block data without * EOF trimming or CP/M 0xe5 removal. */ fpFile->GetLastLocationRead(&fBlock); if (fBlock == A2File::kLastWasSparse) ; #endif fAlertMsg = ""; dierr = fpOpenFile->GetStorage(fBlockIdx, &fBlock); if (dierr == kDIErrInvalidIndex && fBlockIdx == 0) { // no first sector; should only happen on CP/M //FillWithPattern(fBlockData, sizeof(fBlockData), _T("EMPTY ")); fAlertMsg.LoadString(IDS_DISKEDITMSG_EMPTY); } else if (dierr != kDIErrNone) { CString msg, failed; failed.LoadString(IDS_FAILED); msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImgLib::DIStrError(dierr)); MessageBox(msg, failed, MB_OK); fAlertMsg.LoadString(IDS_FAILED); // TO DO: mark contents as invalid, so editing fails return -1; } else { if (fBlock == 0) { WMSG0("LoadData Sparse block\n"); //FillWithPattern(fBlockData, sizeof(fBlockData), _T("SPARSE ")); fAlertMsg.Format(IDS_DISKEDITMSG_SPARSE, fBlockIdx); } else { WMSG1("LoadData reading B=%d\n", fBlock); dierr = fpDiskFS->GetDiskImg()->ReadBlock(fBlock, fBlockData); if (dierr != kDIErrNone) { //CString msg; //CString err; //err.LoadString(IDS_ERROR); //msg.Format(IDS_DISKEDIT_NOREADBLOCK, fBlock); //MessageBox(msg, err, MB_OK|MB_ICONSTOP); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADBLOCK); //return -1; } } } SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); CWnd* pWnd; pWnd = GetDlgItem(IDC_DISKEDIT_PREV); ASSERT(pWnd != nil); pWnd->EnableWindow(fBlockIdx > 0); if (!pWnd->IsWindowEnabled() && GetFocus() == nil) GetDlgItem(IDC_DISKEDIT_NEXT)->SetFocus(); pWnd = GetDlgItem(IDC_DISKEDIT_NEXT); ASSERT(pWnd != nil); pWnd->EnableWindow(fBlockIdx+1 < fpOpenFile->GetBlockCount()); if (!pWnd->IsWindowEnabled() && GetFocus() == nil) GetDlgItem(IDC_DISKEDIT_PREV)->SetFocus(); DisplayData(); return 0; } /* * Move to the previous Block in the file. */ void BlockFileEditDialog::OnReadPrev(void) { if (fBlockIdx == 0) return; fBlockIdx--; ASSERT(fBlockIdx >= 0); LoadData(); } /* * Move to the next Block in the file. */ void BlockFileEditDialog::OnReadNext(void) { if (fBlockIdx+1 >= fpOpenFile->GetBlockCount()) return; fBlockIdx++; ASSERT(fBlockIdx < fpOpenFile->GetBlockCount()); LoadData(); } /* * =========================================================================== * NibbleEditDialog * =========================================================================== */ /* * Rearrange the DiskEdit dialog (which defaults to SectorEdit mode) to * accommodate nibble editing. */ BOOL NibbleEditDialog::OnInitDialog(void) { /* * Get rid of the "sector" input item. */ CWnd* pWnd; pWnd = GetDlgItem(IDC_STEXT_SECTOR); pWnd->DestroyWindow(); pWnd = GetDlgItem(IDC_DISKEDIT_SECTOR); pWnd->DestroyWindow(); pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); pWnd->DestroyWindow(); CString trackStr; trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); /* * Increase the size of the window so it's the same height as blocks. * * NOTE: using a pixel constant is probably bad. We want to use something * like GetTextMetrics, but I'm not sure how to get that without a * device context. */ CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); ASSERT(pEdit != nil); const int kStretchHeight = 249; CRect rect; GetWindowRect(&rect); rect.bottom += kStretchHeight; MoveWindow(&rect); /* * Must postpone resize of edit ctrl until after data has been loaded, or * scroll bars fail to appear under Win98. Makes no sense whatsoever, but * that's Windows for you. */ #if 0 CRect inner; pEdit->GetRect(&inner); inner.bottom += kStretchHeight; pEdit->GetWindowRect(&rect); ScreenToClient(&rect); rect.bottom += kStretchHeight; pEdit->MoveWindow(&rect); pEdit->SetRect(&inner); #endif /* show the scroll bar */ pEdit->ShowScrollBar(SB_VERT); MoveControl(this, IDC_DISKEDIT_DONE, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_OPENFILE, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_SUBVOLUME, 0, kStretchHeight); MoveControl(this, IDHELP, 0, kStretchHeight); MoveControl(this, IDC_DISKEDIT_NIBBLE_PARMS, 0, kStretchHeight); /* disable opening of files and sub-volumes */ pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); pWnd->EnableWindow(FALSE); /* * Do base-class construction. */ DiskEditDialog::OnInitDialog(); /* * This currently has no effect on the nibble editor. Someday we may * want to highlight and/or decode address fields. */ pWnd = GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); pWnd->EnableWindow(FALSE); /* * Configure the track spin button. */ MySpinCtrl* pSpin; pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); ASSERT(pSpin != nil); pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumTracks()-1); pSpin->SetPos(0); /* give us something to look at */ LoadData(); return TRUE; } /* * Load the current track data into the edit control. */ int NibbleEditDialog::LoadData(void) { //WMSG0("BED LoadData\n"); ASSERT(fpDiskFS != nil); ASSERT(fpDiskFS->GetDiskImg() != nil); if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fTrack) != 0) return -1; WMSG1("LoadData reading track=%d\n", fTrack); fAlertMsg = ""; DIError dierr; dierr = fpDiskFS->GetDiskImg()->ReadNibbleTrack(fTrack, fNibbleData, &fNibbleDataLen); if (dierr != kDIErrNone) { WMSG1("NED track read failed: %hs\n", DiskImgLib::DIStrError(dierr)); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADTRACK); } DisplayData(); return 0; } /* * Read the currently specified track/sector. */ void NibbleEditDialog::OnDoRead(void) { LoadData(); } /* * Write the currently loaded track/sector. */ void NibbleEditDialog::OnDoWrite(void) { MessageBox(L"Write!"); } /* * Back up to the previous track/sector, or (in follow-file mode) to the * previous sector in the file. */ void NibbleEditDialog::OnReadPrev(void) { if (fTrack == 0) return; fTrack--; SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); LoadData(); } /* * Same as OnReadPrev, but moving forward. */ void NibbleEditDialog::OnReadNext(void) { ASSERT(fpDiskFS != nil); if (fTrack == fpDiskFS->GetDiskImg()->GetNumTracks() - 1) return; fTrack++; SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); LoadData(); }