/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Class definition for DiskEdit dialog. */ #ifndef APP_DISKEDITDIALOG_H #define APP_DISKEDITDIALOG_H #include "../diskimg/DiskImg.h" #include "../util/UtilLib.h" #include "resource.h" /* * An abstract base class to support "sector editing" and "block editing" * dialogs, which differ chiefly in how much data they present at a time. * * NOTE: override OnCancel to insert an "are you sure" message when the * block is dirty. * * NOTE to self: if the initial block/sector read fails, we can be left * with invalid stuff in the buffer. Keep that in mind if editing is * enabled. */ class DiskEditDialog : public CDialog { public: DiskEditDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL) : CDialog(nIDTemplate, pParentWnd) { fReadOnly = true; fpDiskFS = NULL; fFileName = ""; fPositionShift = 0; fFirstResize = true; } virtual ~DiskEditDialog() {} void Setup(DiskFS* pDiskFS, const WCHAR* fileName) { ASSERT(pDiskFS != NULL); ASSERT(fileName != NULL); fpDiskFS = pDiskFS; fFileName = fileName; } enum { kSectorSize=256, kBlockSize=512 }; virtual int LoadData(void) = 0; virtual void DisplayData(void) = 0; /* * Convert a chunk of data into a hex dump, and stuff it into the edit control. */ virtual void DisplayData(const uint8_t* buf, int size); /* * Display a track full of nibble data. */ virtual void DisplayNibbleData(const uint8_t* srcBuf, int size); bool GetReadOnly(void) const { return fReadOnly; } void SetReadOnly(bool val) { fReadOnly = val; } int GetPositionShift(void) const { return fPositionShift; } void SetPositionShift(int val) { fPositionShift = val; } DiskFS* GetDiskFS(void) const { return fpDiskFS; } const WCHAR* GetFileName(void) const { return fFileName; } protected: // return a low-ASCII character so we can read high-ASCII files inline char PrintableChar(unsigned char ch) { if (ch < 0x20) return '.'; else if (ch < 0x80) return ch; else if (ch < 0xa0 || ch == 0xff) // 0xff becomes 0x7f return '.'; else return ch & 0x7f; } virtual BOOL OnInitDialog(void) override; // catch key virtual BOOL PreTranslateMessage(MSG* pMsg) override; /* * Handle the "Done" button. We don't use IDOK because we don't want * to bail out of the dialog. */ afx_msg virtual void OnDone(void); /* * Toggle the spin button / edit controls. */ afx_msg virtual void OnHexMode(void); afx_msg virtual void OnDoRead(void) = 0; afx_msg virtual void OnDoWrite(void) = 0; afx_msg virtual void OnReadPrev(void) = 0; afx_msg virtual void OnReadNext(void) = 0; /* * Create a new instance of the disk edit dialog, for a sub-volume. */ afx_msg virtual void OnSubVolume(void); afx_msg virtual void OnOpenFile(void) = 0; /* * Change the nibble parms. * * Assumes the parm list is linear and unbroken. */ afx_msg virtual void OnNibbleParms(void); afx_msg void OnHelp(void) { MyApp::HandleHelp(this, HELP_TOPIC_DISKEDIT); } afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { return MyApp::HandleHelpInfo(lpHelpInfo); } /* * 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 SetSpinMode(int id, int base); /* * 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 ReadSpinner(int id, long* pVal); /* * Set the value of a spin control. */ void SetSpinner(int id, long val); /* * Open a file in a disk image. * * Returns a pointer to the A2File and A2FileDescr structures on success, NULL * on failure. The pointer placed in "ppOpenFile" must be freed by invoking * its Close function. */ DIError OpenFile(const WCHAR* fileName, bool openRsrc, A2File** ppFile, A2FileDescr** ppOpenFile); DiskFS* fpDiskFS; CString fFileName; CString fAlertMsg; bool fReadOnly; int fPositionShift; private: /* * Initialize the nibble parm drop-list. */ void InitNibbleParmList(void); /* * Replace a spin button with our improved version. */ int ReplaceSpinCtrl(MySpinCtrl* pNewSpin, int idSpin, int idEdit); MySpinCtrl fTrackSpinner; MySpinCtrl fSectorSpinner; bool fFirstResize; //afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; /* * The "sector edit" dialog, which displays 256 bytes at a time, and * accesses a disk by track/sector. */ class SectorEditDialog : public DiskEditDialog { public: SectorEditDialog(CWnd* pParentWnd = NULL) : DiskEditDialog(IDD_DISKEDIT, pParentWnd), fTrack(0), fSector(0), fSectorData() { } virtual ~SectorEditDialog() {} virtual int LoadData(void) override; // load the current track/sector virtual void DisplayData(void) override { DiskEditDialog::DisplayData(fSectorData, kSectorSize); } //void SetTrack(int val) { fTrack = val; } //void SetSector(int val) { fSector = val; } protected: virtual BOOL OnInitDialog(void) override; afx_msg virtual void OnDoRead(void); afx_msg virtual void OnDoWrite(void); /* * Back up to the previous track/sector. */ afx_msg virtual void OnReadPrev(void); /* * Advance to the next track/sector. */ afx_msg virtual void OnReadNext(void); /* * Open a file on the disk image. If successful, open a new edit dialog * that's in "file follow" mode. */ afx_msg virtual void OnOpenFile(void); long fTrack; long fSector; uint8_t fSectorData[kSectorSize]; }; /* * Edit a file sector-by-sector. */ class SectorFileEditDialog : public SectorEditDialog { public: SectorFileEditDialog(SectorEditDialog* pSectEdit, CWnd* pParentWnd = NULL): SectorEditDialog(pParentWnd), fOpenRsrcFork(false), fpFile(NULL), fpOpenFile(NULL), fSectorIdx(0), fLength(0) { DiskEditDialog::Setup(pSectEdit->GetDiskFS(), pSectEdit->GetFileName()); } virtual ~SectorFileEditDialog() {} /* we do NOT own pOpenFile, and should not delete it */ void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, A2FileDescr* pOpenFile) { fOpenFileName = fileName; fOpenRsrcFork = rsrcFork; fpFile = pFile; fpOpenFile = pOpenFile; fLength = 0; if (fpOpenFile->Seek(0, DiskImgLib::kSeekEnd) == kDIErrNone) fLength = fpOpenFile->Tell(); } virtual int LoadData(void); // load data from the current offset private: // overrides virtual BOOL OnInitDialog(void); afx_msg virtual void OnReadPrev(void); afx_msg virtual void OnReadNext(void); CString fOpenFileName; bool fOpenRsrcFork; A2File* fpFile; A2FileDescr* fpOpenFile; //long fOffset; long fSectorIdx; di_off_t fLength; }; /* * The "block edit" dialog, which displays 512 bytes at a time, and * accesses a disk by linear block number. */ class BlockEditDialog : public DiskEditDialog { public: BlockEditDialog(CWnd* pParentWnd = NULL) : DiskEditDialog(IDD_DISKEDIT, pParentWnd) { fBlock = 0; } virtual ~BlockEditDialog() {} virtual int LoadData(void) override; // load the current block virtual void DisplayData(void) override { DiskEditDialog::DisplayData(fBlockData, kBlockSize); } protected: virtual BOOL OnInitDialog(void) override; afx_msg virtual void OnDoRead(void); afx_msg virtual void OnDoWrite(void); /* * Back up to the previous track/sector, or (in follow-file mode) to the * previous sector in the file. */ afx_msg virtual void OnReadPrev(void); /* * Same as OnReadPrev, but moving forward. */ afx_msg virtual void OnReadNext(void); /* * Open a file on the disk image. If successful, open a new edit dialog * that's in "file follow" mode. */ afx_msg virtual void OnOpenFile(void); long fBlock; uint8_t fBlockData[kBlockSize]; }; /* * Edit a file block-by-block. */ class BlockFileEditDialog : public BlockEditDialog { public: BlockFileEditDialog(BlockEditDialog* pBlockEdit, CWnd* pParentWnd = NULL) : BlockEditDialog(pParentWnd), fOpenRsrcFork(false), fpFile(NULL), fpOpenFile(NULL), fBlockIdx(0), fLength(0) { DiskEditDialog::Setup(pBlockEdit->GetDiskFS(), pBlockEdit->GetFileName()); fBlockIdx = 0; } virtual ~BlockFileEditDialog() {} /* we do NOT own pOpenFile, and should not delete it */ void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, A2FileDescr* pOpenFile) { fOpenFileName = fileName; fOpenRsrcFork = rsrcFork; fpFile = pFile; fpOpenFile = pOpenFile; fLength = 0; if (fpOpenFile->Seek(0, DiskImgLib::kSeekEnd) == kDIErrNone) fLength = fpOpenFile->Tell(); } virtual int LoadData(void); // load data from the current offset private: // overrides virtual BOOL OnInitDialog(void); /* * Move to the previous Block in the file. */ afx_msg virtual void OnReadPrev(void); /* * Move to the next Block in the file. */ afx_msg virtual void OnReadNext(void); CString fOpenFileName; bool fOpenRsrcFork; A2File* fpFile; A2FileDescr* fpOpenFile; //long fOffset; long fBlockIdx; di_off_t fLength; }; /* * The "sector edit" dialog, which displays 256 bytes at a time, and * accesses a disk by track/sector. */ class NibbleEditDialog : public DiskEditDialog { public: NibbleEditDialog(CWnd* pParentWnd = NULL) : DiskEditDialog(IDD_DISKEDIT, pParentWnd), fTrack(0), fNibbleData(), fNibbleDataLen(0) { } virtual ~NibbleEditDialog() {} virtual int LoadData(void) override; // load the current track/sector virtual void DisplayData(void) override { DiskEditDialog::DisplayNibbleData(fNibbleData, fNibbleDataLen); } protected: /* * Rearrange the DiskEdit dialog (which defaults to SectorEdit mode) to * accommodate nibble editing. */ virtual BOOL OnInitDialog(void) override; afx_msg virtual void OnDoRead(void); afx_msg virtual void OnDoWrite(void); afx_msg virtual void OnReadPrev(void); afx_msg virtual void OnReadNext(void); afx_msg virtual void OnOpenFile(void) { ASSERT(false); } afx_msg virtual void OnNibbleParms(void) { ASSERT(false); } long fTrack; uint8_t fNibbleData[DiskImgLib::kTrackAllocSize]; long fNibbleDataLen; }; #endif /*APP_DISKEDITDIALOG_H*/