ciderpress/app/DiskEditDialog.cpp

1645 lines
44 KiB
C++

/*
* 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
* <return> 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 = &sectorEdit;
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();
}