diff --git a/app/Main.cpp b/app/Main.cpp index fff6b88..8f5753e 100644 --- a/app/Main.cpp +++ b/app/Main.cpp @@ -1,2710 +1,2716 @@ -/* - * 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 "MyApp.h" -#include "AboutDialog.h" -#include "NufxArchive.h" -#include "DiskArchive.h" -#include "BNYArchive.h" -#include "ACUArchive.h" -#include "ArchiveInfoDialog.h" -#include "PrefsDialog.h" -#include "EnterRegDialog.h" -#include "OpenVolumeDialog.h" -#include "Print.h" -#include "HelpTopics.h" -#include "../util/UtilLib.h" -#include "resource.h" - -/* use MFC's fancy version of new for debugging */ -//#define new DEBUG_NEW - -static const char* kWebSiteURL = "http://www.faddensoft.com/"; - -/* - * Filters for the "open file" command. In some cases a file may be opened - * in more than one format, so it's necessary to keep track of what the - * file filter was set to when the file was opened. - */ -const char MainWindow::kOpenNuFX[] = - "ShrinkIt Archives (.shk .sdk .bxy .sea .bse)|*.shk;*.sdk;*.bxy;*.sea;*.bse|"; -const char MainWindow::kOpenBinaryII[] = - "Binary II Archives (.bny .bqy .bxy)|*.bny;*.bqy;*.bxy|"; -const char MainWindow::kOpenACU[] = - "ACU Archives (.acu)|*.acu|"; -const char MainWindow::kOpenDiskImage[] = - "Disk Images (.shk .sdk .dsk .po .do .d13 .2mg .img .nib .nb2 .raw .hdv .dc .dc6 .ddd .app .fdi .iso .gz .zip)|" - "*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|"; -const char MainWindow::kOpenAll[] = - "All Files (*.*)|*.*|"; -const char MainWindow::kOpenEnd[] = - "|"; - -static const struct { - //const char* extension; - char extension[4]; - FilterIndex idx; -} gExtensionToIndex[] = { - { "shk", kFilterIndexNuFX }, - { "bxy", kFilterIndexNuFX }, - { "bse", kFilterIndexNuFX }, - { "sea", kFilterIndexNuFX }, - { "bny", kFilterIndexBinaryII }, - { "bqy", kFilterIndexBinaryII }, - { "acu", kFilterIndexACU }, - { "dsk", kFilterIndexDiskImage }, - { "po", kFilterIndexDiskImage }, - { "do", kFilterIndexDiskImage }, - { "d13", kFilterIndexDiskImage }, - { "2mg", kFilterIndexDiskImage }, - { "img", kFilterIndexDiskImage }, - { "sdk", kFilterIndexDiskImage }, - { "raw", kFilterIndexDiskImage }, - { "ddd", kFilterIndexDiskImage }, - { "app", kFilterIndexDiskImage }, - { "fdi", kFilterIndexDiskImage }, - { "iso", kFilterIndexDiskImage }, - { "gz", kFilterIndexDiskImage }, // assume disk image inside - { "zip", kFilterIndexDiskImage }, // assume disk image inside -}; - -const char* MainWindow::kModeNuFX = _T("nufx"); -const char* MainWindow::kModeBinaryII = _T("bin2"); -const char* MainWindow::kModeACU = _T("acu"); -const char* MainWindow::kModeDiskImage = _T("disk"); - - -/* - * =========================================================================== - * MainWindow - * =========================================================================== - */ - -static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING); - -BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) - ON_WM_CREATE() - ON_MESSAGE(WMU_LATE_INIT, OnLateInit) - //ON_MESSAGE(WMU_CLOSE_MAIN_DIALOG, OnCloseMainDialog) - ON_WM_SIZE() - ON_WM_GETMINMAXINFO() - ON_WM_PAINT() - //ON_WM_MOUSEWHEEL() - ON_WM_SETFOCUS() - ON_WM_HELPINFO() - ON_WM_QUERYENDSESSION() - ON_WM_ENDSESSION() - ON_REGISTERED_MESSAGE(gFindReplaceID, OnFindDialogMessage) - ON_COMMAND( IDM_FILE_NEW_ARCHIVE, OnFileNewArchive) - ON_COMMAND( IDM_FILE_OPEN, OnFileOpen) - ON_COMMAND( IDM_FILE_OPEN_VOLUME, OnFileOpenVolume) - ON_UPDATE_COMMAND_UI(IDM_FILE_OPEN_VOLUME, OnUpdateFileOpenVolume) - ON_COMMAND( IDM_FILE_REOPEN, OnFileReopen) - ON_UPDATE_COMMAND_UI(IDM_FILE_REOPEN, OnUpdateFileReopen) - ON_COMMAND( IDM_FILE_SAVE, OnFileSave) - ON_UPDATE_COMMAND_UI(IDM_FILE_SAVE, OnUpdateFileSave) - ON_COMMAND( IDM_FILE_CLOSE, OnFileClose) - ON_UPDATE_COMMAND_UI(IDM_FILE_CLOSE, OnUpdateFileClose) - ON_COMMAND( IDM_FILE_ARCHIVEINFO, OnFileArchiveInfo) - ON_UPDATE_COMMAND_UI(IDM_FILE_ARCHIVEINFO, OnUpdateFileArchiveInfo) - ON_COMMAND( IDM_FILE_PRINT, OnFilePrint) - ON_UPDATE_COMMAND_UI(IDM_FILE_PRINT, OnUpdateFilePrint) - ON_COMMAND( IDM_FILE_EXIT, OnFileExit) - ON_COMMAND( IDM_EDIT_COPY, OnEditCopy) - ON_UPDATE_COMMAND_UI(IDM_EDIT_COPY, OnUpdateEditCopy) - ON_COMMAND( IDM_EDIT_PASTE, OnEditPaste) - ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE, OnUpdateEditPaste) - ON_COMMAND( IDM_EDIT_PASTE_SPECIAL, OnEditPasteSpecial) - ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE_SPECIAL, OnUpdateEditPasteSpecial) - ON_COMMAND( IDM_EDIT_FIND, OnEditFind) - ON_UPDATE_COMMAND_UI(IDM_EDIT_FIND, OnUpdateEditFind) - ON_COMMAND( IDM_EDIT_SELECT_ALL, OnEditSelectAll) - ON_UPDATE_COMMAND_UI(IDM_EDIT_SELECT_ALL, OnUpdateEditSelectAll) - ON_COMMAND( IDM_EDIT_INVERT_SELECTION, OnEditInvertSelection) - ON_UPDATE_COMMAND_UI(IDM_EDIT_INVERT_SELECTION, OnUpdateEditInvertSelection) - ON_COMMAND( IDM_EDIT_PREFERENCES, OnEditPreferences) - ON_COMMAND_RANGE( IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnEditSort) - ON_UPDATE_COMMAND_UI_RANGE(IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnUpdateEditSort) - ON_COMMAND( IDM_ACTIONS_VIEW, OnActionsView) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_VIEW, OnUpdateActionsView) - ON_COMMAND( IDM_ACTIONS_ADD_FILES, OnActionsAddFiles) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_FILES, OnUpdateActionsAddFiles) - ON_COMMAND( IDM_ACTIONS_ADD_DISKS, OnActionsAddDisks) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_DISKS, OnUpdateActionsAddDisks) - ON_COMMAND( IDM_ACTIONS_CREATE_SUBDIR, OnActionsCreateSubdir) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CREATE_SUBDIR, OnUpdateActionsCreateSubdir) - ON_COMMAND( IDM_ACTIONS_EXTRACT, OnActionsExtract) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EXTRACT, OnUpdateActionsExtract) - ON_COMMAND( IDM_ACTIONS_TEST, OnActionsTest) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_TEST, OnUpdateActionsTest) - ON_COMMAND( IDM_ACTIONS_DELETE, OnActionsDelete) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_DELETE, OnUpdateActionsDelete) - ON_COMMAND( IDM_ACTIONS_RENAME, OnActionsRename) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME, OnUpdateActionsRename) - ON_COMMAND( IDM_ACTIONS_RECOMPRESS, OnActionsRecompress) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RECOMPRESS, OnUpdateActionsRecompress) - ON_COMMAND( IDM_ACTIONS_OPENASDISK, OnActionsOpenAsDisk) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_OPENASDISK, OnUpdateActionsOpenAsDisk) - ON_COMMAND( IDM_ACTIONS_EDIT_COMMENT, OnActionsEditComment) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_COMMENT, OnUpdateActionsEditComment) - ON_COMMAND( IDM_ACTIONS_EDIT_PROPS, OnActionsEditProps) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_PROPS, OnUpdateActionsEditProps) - ON_COMMAND( IDM_ACTIONS_RENAME_VOLUME, OnActionsRenameVolume) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME_VOLUME, OnUpdateActionsRenameVolume) - ON_COMMAND( IDM_ACTIONS_CONV_DISK, OnActionsConvDisk) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_DISK, OnUpdateActionsConvDisk) - ON_COMMAND( IDM_ACTIONS_CONV_FILE, OnActionsConvFile) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FILE, OnUpdateActionsConvFile) - ON_COMMAND( IDM_ACTIONS_CONV_TOWAV, OnActionsConvToWav) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_TOWAV, OnUpdateActionsConvToWav) - ON_COMMAND( IDM_ACTIONS_CONV_FROMWAV, OnActionsConvFromWav) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FROMWAV, OnUpdateActionsConvFromWav) - ON_COMMAND( IDM_ACTIONS_IMPORT_BAS, OnActionsImportBAS) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_IMPORT_BAS, OnUpdateActionsImportBAS) - ON_COMMAND( IDM_TOOLS_DISKEDIT, OnToolsDiskEdit) - ON_COMMAND( IDM_TOOLS_IMAGECREATOR, OnToolsDiskImageCreator) - ON_COMMAND( IDM_TOOLS_DISKCONV, OnToolsDiskConv) - ON_COMMAND( IDM_TOOLS_BULKDISKCONV, OnToolsBulkDiskConv) - ON_COMMAND( IDM_TOOLS_SST_MERGE, OnToolsSSTMerge) - ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_VOLUME, OnToolsVolumeCopierVolume) - ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_FILE, OnToolsVolumeCopierFile) - ON_COMMAND( IDM_TOOLS_EOLSCANNER, OnToolsEOLScanner) - ON_COMMAND( IDM_TOOLS_TWOIMGPROPS, OnToolsTwoImgProps) - ON_COMMAND( IDM_HELP_CONTENTS, OnHelpContents) - ON_COMMAND( IDM_HELP_WEBSITE, OnHelpWebSite) - ON_COMMAND( IDM_HELP_ORDERING, OnHelpOrdering) - ON_COMMAND( IDM_HELP_ABOUT, OnHelpAbout) -// ON_COMMAND( IDM_RTCLK_DEFAULT, OnRtClkDefault) - - /* this is required to allow "Help" button to work in PropertySheets (!) */ -// ON_COMMAND(ID_HELP, OnHelp) - ON_COMMAND(ID_HELP_FINDER, CFrameWnd::OnHelpFinder) - ON_COMMAND(ID_HELP, CFrameWnd::OnHelp) - ON_COMMAND(ID_CONTEXT_HELP, CFrameWnd::OnContextHelp) - ON_COMMAND(ID_DEFAULT_HELP, CFrameWnd::OnHelpFinder) -END_MESSAGE_MAP() - - -/* - * MainWindow constructor. Creates the main window and sets - * its properties. - */ -MainWindow::MainWindow() -{ - static const char* kAppName = _T("CiderPress"); - - fpContentList = nil; - fpOpenArchive = nil; - //fpSelSet = nil; - fpActionProgress = nil; - fpProgressCounter = nil; - fpFindDialog = nil; - - fFindDown = true; - fFindMatchCase = false; - fFindMatchWholeWord = false; - - fAbortPrinting = false; - fhDevMode = nil; - fhDevNames = nil; - fNeedReopen = false; - - CString wndClass = AfxRegisterWndClass( - CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, - gMyApp.LoadStandardCursor(IDC_ARROW), - NULL /*(HBRUSH) (COLOR_WINDOW + 1)*/, - gMyApp.LoadIcon(IDR_MAINFRAME) ); - - Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/, - rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME)); - - LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME)); - - // initialize some OLE garbage - AfxOleInit(); - - // required by MFC if Rich Edit controls are used - AfxInitRichEdit(); - - // required?? - //AfxEnableControlContainer(); - - SetCPTitle(); - - int cc = PostMessage(WMU_LATE_INIT, 0, 0); - ASSERT(cc != 0); -} - -/* - * MainWindow destructor. Close the archive if one is open, but don't try - * to shut down any controls in child windows. By this point, Windows has - * already snuffed them. - */ -MainWindow::~MainWindow() -{ - WMSG0("~MainWindow\n"); - - //WMSG0("MainWindow destructor\n"); - CloseArchiveWOControls(); - - int cc; - cc = ::WinHelp(m_hWnd, ::AfxGetApp()->m_pszHelpFilePath, HELP_QUIT, 0); - WMSG1("Turning off WinHelp returned %d\n", cc); - - // free stuff used by print dialog - ::GlobalFree(fhDevMode); - ::GlobalFree(fhDevNames); - - fPreferences.SaveToRegistry(); - WMSG0("MainWindow destructor complete\n"); -} - - -/* - * Override the pre-create function to tweak the window style. - */ -BOOL -MainWindow::PreCreateWindow(CREATESTRUCT& cs) -{ - BOOL res = CFrameWnd::PreCreateWindow(cs); - - cs.dwExStyle &= ~(WS_EX_CLIENTEDGE); - - return res; -} - -/* - * Override GetClientRect so we can factor in the status and tool bars. - */ -void -MainWindow::GetClientRect(LPRECT lpRect) const -{ - CRect sizeRect; - int toolBarHeight, statusBarHeight; - - fToolBar.GetWindowRect(&sizeRect); - toolBarHeight = sizeRect.bottom - sizeRect.top; - fStatusBar.GetWindowRect(&sizeRect); - statusBarHeight = sizeRect.bottom - sizeRect.top; - - //WMSG2("HEIGHTS = %d/%d\n", toolBarHeight, statusBarHeight); - CFrameWnd::GetClientRect(lpRect); - lpRect->top += toolBarHeight; - lpRect->bottom -= statusBarHeight; -} - - -/* - * Do some idle processing. - */ -void -MainWindow::DoIdle(void) -{ - /* - * Make sure that the filename field in the content list is always - * visible, since that what the user clicks on to select things. Would - * be nice to have a way to prevent it, but for now we'll just shove - * things back where they're supposed to be. - */ - if (fpContentList != nil) { - /* get the current column 0 width, with current user adjustments */ - fpContentList->ExportColumnWidths(); - int width = fPreferences.GetColumnLayout()->GetColumnWidth(0); - - if (width >= 0 && width < ColumnLayout::kMinCol0Width) { - /* column is too small, but don't change it until user lets mouse up */ - if (::GetAsyncKeyState(VK_LBUTTON) >= 0) { - WMSG0("Resetting column 0 width\n"); - fPreferences.GetColumnLayout()->SetColumnWidth(0, - ColumnLayout::kMinCol0Width); - fpContentList->NewColumnWidths(); - } - } - } - - /* - * Put an asterisk at the end of the title if we have an open archive - * and it has pending modifications. Remove it if nothing is pending. - */ - if (fpOpenArchive != nil) { - CString title; - int len; - - GetWindowText(/*ref*/ title); - len = title.GetLength(); - if (len > 0 && title.GetAt(len-1) == '*') { - if (!fpOpenArchive->IsModified()) { - /* remove the asterisk and the preceeding space */ - title.Delete(len-2, 2); - SetWindowText(title); - } - } else { - if (fpOpenArchive->IsModified()) { - /* add an asterisk */ - title += " *"; - SetWindowText(title); - } - } - } -} - - -/* - * Handle command-line arguments. - * - * Usage: - * CiderPress [[-temparc] [-mode {nufx,bin2,disk}] [-dispname name] filename] - */ -void -MainWindow::ProcessCommandLine(void) -{ - /* - * Get the command line and break it down into an argument vector. - */ - const char* cmdLine = ::GetCommandLine(); - if (cmdLine == nil || strlen(cmdLine) == 0) - return; - - char* mangle = strdup(cmdLine); - if (mangle == nil) - return; - - WMSG1("Mangling '%s'\n", mangle); - char* argv[8]; - int argc = 8; - VectorizeString(mangle, argv, &argc); - - WMSG0("Args:\n"); - for (int i = 0; i < argc; i++) { - WMSG2(" %d '%s'\n", i, argv[i]); - } - - /* - * Figure out what the arguments are. - */ - const char* filename = nil; - const char* dispName = nil; - int filterIndex = kFilterIndexGeneric; - bool temp = false; - - for (i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - if (strcasecmp(argv[i], "-mode") == 0) { - if (i == argc-1) { - WMSG0("WARNING: -mode specified without mode\n"); - } else - i++; - if (strcasecmp(argv[i], kModeNuFX) == 0) - filterIndex = kFilterIndexNuFX; - else if (strcasecmp(argv[i], kModeBinaryII) == 0) - filterIndex = kFilterIndexBinaryII; - else if (strcasecmp(argv[i], kModeACU) == 0) - filterIndex = kFilterIndexACU; - else if (strcasecmp(argv[i], kModeDiskImage) == 0) - filterIndex = kFilterIndexDiskImage; - else { - WMSG1("WARNING: unrecognized mode '%s'\n", argv[i]); - } - } else if (strcasecmp(argv[i], "-dispname") == 0) { - if (i == argc-1) { - WMSG0("WARNING: -dispname specified without name\n"); - } else - i++; - dispName = argv[i]; - } else if (strcasecmp(argv[i], "-temparc") == 0) { - temp = true; - } else if (strcasecmp(argv[i], "-install") == 0) { - // see MyApp::InitInstance - WMSG0("Got '-install' flag, doing nothing\n"); - } else if (strcasecmp(argv[i], "-uninstall") == 0) { - // see MyApp::InitInstance - WMSG0("Got '-uninstall' flag, doing nothing\n"); - } else { - WMSG1("WARNING: unrecognized flag '%s'\n", argv[i]); - } - } else { - /* must be the filename */ - if (i != argc-1) { - WMSG1("WARNING: ignoring extra arguments (e.g. '%s')\n", - argv[i+1]); - } - filename = argv[i]; - break; - } - } - if (argc != 1 && filename == nil) { - WMSG0("WARNING: args specified but no filename found\n"); - } - - WMSG0("Argument handling:\n"); - WMSG3(" index=%d temp=%d filename='%s'\n", - filterIndex, temp, filename == nil ? "(nil)" : filename); - - if (filename != nil) { - PathName path(filename); - CString ext = path.GetExtension(); - - // drop the leading '.' from the extension - if (ext.Left(1) == ".") - ext.Delete(0, 1); - - /* load the archive, mandating read-only if it's a temporary file */ - if (LoadArchive(filename, ext, filterIndex, temp, false) == 0) { - /* success, update title bar */ - if (temp) - fOpenArchivePathName = path.GetFileName(); - else - fOpenArchivePathName = filename; - if (dispName != nil) - fOpenArchivePathName = dispName; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } - - /* if it's a temporary file, arrange to have it deleted before exit */ - if (temp) { - int len = strlen(filename); - - if (len > 4 && strcasecmp(filename + (len-4), ".tmp") == 0) { - fDeleteList.Add(filename); - } else { - WMSG1("NOT adding '%s' to DeleteList -- does not end in '.tmp'\n", - filename); - } - } - } - - free(mangle); -} - - -/* - * =================================== - * Command handlers - * =================================== - */ - -const int kProgressPane = 1; - -/* - * OnCreate handler. Used to add a toolbar and status bar. - */ -int -MainWindow::OnCreate(LPCREATESTRUCT lpcs) -{ - WMSG0("Now in OnCreate!\n"); - if (CFrameWnd::OnCreate(lpcs) == -1) - return -1; - - /* - * Create the tool bar. - */ -#if 0 - static UINT buttonList[] = { - IDM_FILE_OPEN, - IDM_FILE_NEW_ARCHIVE, - // spacer - IDM_FILE_PRINT, - }; -#endif - fToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | - CBRS_TOOLTIPS | CBRS_FLYBY); - fToolBar.LoadToolBar(IDR_TOOLBAR1); - - /* - * Create the status bar. - */ - static UINT indicators[] = { ID_SEPARATOR, ID_INDICATOR_COMPLETE }; - fStatusBar.Create(this); - fStatusBar.SetIndicators(indicators, NELEM(indicators)); - //fStatusBar.SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0); - - fStatusBar.SetPaneText(kProgressPane, ""); - - return 0; -} - - -/* - * Catch a message sent to inspire us to perform one-time initializations of - * preferences and libraries. - * - * We're doing this the long way around because we want to be able to - * put up a dialog box if the version is bad. If we tried to handle this - * in the constructor we'd be acting before the window was fully created. - */ -LONG -MainWindow::OnLateInit(UINT, LONG) -{ - CString result; - CString appName; - CString niftyListFile; - - appName.LoadString(IDS_MB_APP_NAME); - - WMSG0("----- late init begins -----\n"); - - /* - * Handle all other messages. This gives the framework a chance to dim - * all of the toolbar buttons. This is especially useful when opening - * a file from the command line that doesn't exist, causing an error - * dialog and blocking main window messages. - */ - PeekAndPump(); - - /* - * Initialize libraries. This includes a version check. - */ - result = NufxArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - result = DiskArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - result = BnyArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - - niftyListFile = gMyApp.GetExeBaseName(); - niftyListFile += "NList.Data"; - if (!NiftyList::AppInit(niftyListFile)) { - CString file2 = niftyListFile + ".TXT"; - if (!NiftyList::AppInit(file2)) { - CString msg; - msg.Format(IDS_NLIST_DATA_FAILED, niftyListFile, file2); - MessageBox(msg, appName, MB_OK); - } - } - - /* - * Read preferences from registry. - */ - fPreferences.LoadFromRegistry(); - - /* - * Check to see if we're registered; if we're not, and we've expired, it's - * time to bail out. - */ - MyRegistry::RegStatus regStatus; - //regStatus = gMyApp.fRegistry.CheckRegistration(&result); - regStatus = MyRegistry::kRegValid; - WMSG1("CheckRegistration returned %d\n", regStatus); - switch (regStatus) { - case MyRegistry::kRegNotSet: - case MyRegistry::kRegValid: - ASSERT(result.IsEmpty()); - break; - case MyRegistry::kRegExpired: - case MyRegistry::kRegInvalid: - MessageBox(result, appName, MB_OK|MB_ICONINFORMATION); - WMSG0("FORCING REG\n"); -#if 0 - if (EnterRegDialog::GetRegInfo(this) != 0) { - result = ""; - goto fail; - } -#endif - SetCPTitle(); // update title bar with new reg info - break; - case MyRegistry::kRegFailed: - ASSERT(!result.IsEmpty()); - goto fail; - default: - ASSERT(false); - CString confused; - confused.Format("Registration check failed. %s", (LPCTSTR) result); - result = confused; - goto fail; - } - - /* - * Process command-line options, possibly loading an archive. - */ - ProcessCommandLine(); - - return 0; - -fail: - if (!result.IsEmpty()) - ShowFailureMsg(this, result, IDS_FAILED); - int cc = PostMessage(WM_CLOSE, 0, 0); - ASSERT(cc != 0); - - return 0; -} - - -/* - * The system wants to know if we're okay with shutting down. - * - * Return TRUE if it's okay to shut down, FALSE otherwise. - */ -BOOL -MainWindow::OnQueryEndSession(void) -{ - WMSG0("Got QueryEndSession\n"); - return TRUE; -} - -/* - * Notification of shutdown (or not). - */ -void -MainWindow::OnEndSession(BOOL bEnding) -{ - WMSG1("Got EndSession (bEnding=%d)\n", bEnding); - - if (bEnding) { - CloseArchiveWOControls(); - - fPreferences.SaveToRegistry(); - } -} - -/* - * The main window is resizing. We don't automatically redraw on resize, - * so we will need to update the client region. If it's filled with a - * control, the control's resize & redraw function will take care of it. - * If not, we need to explicitly invalidate the client region so the - * window will repaint itself. - */ -void -MainWindow::OnSize(UINT nType, int cx, int cy) -{ - CFrameWnd::OnSize(nType, cx, cy); - ResizeClientArea(); -} -void -MainWindow::ResizeClientArea(void) -{ - CRect sizeRect; - - GetClientRect(&sizeRect); - if (fpContentList != NULL) - fpContentList->MoveWindow(sizeRect); - else - Invalidate(false); -} - -/* - * Restrict the minimum window size to something reasonable. - */ -void -MainWindow::OnGetMinMaxInfo(MINMAXINFO* pMMI) -{ - pMMI->ptMinTrackSize.x = 256; - pMMI->ptMinTrackSize.y = 192; -} - -/* - * Repaint the main window. - */ -void -MainWindow::OnPaint(void) -{ - CPaintDC dc(this); - CRect clientRect; - - GetClientRect(&clientRect); - - /* - * If there's no control in the window, fill in the client area with - * what looks like an empty MDI client rect. - */ - if (fpContentList == nil) { - DrawEmptyClientArea(&dc, clientRect); - } - -#if 0 - CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // red pen, 1 pixel wide - CPen* pOldPen = dc.SelectObject(&pen); - - dc.MoveTo(clientRect.left, clientRect.top); - dc.LineTo(clientRect.right-1, clientRect.top); - dc.LineTo(clientRect.right, clientRect.bottom); - dc.LineTo(clientRect.left, clientRect.bottom-1); - dc.LineTo(clientRect.left, clientRect.top); - - dc.SelectObject(pOldPen); -#endif -} - -#if 0 -afx_msg BOOL -MainWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) -{ - WMSG0("MOUSE WHEEL\n"); - return FALSE; - - WPARAM wparam; - LPARAM lparam; - - wparam = nFlags | (zDelta << 16); - lparam = pt.x | (pt.y << 16); - if (fpContentList != nil) - fpContentList->SendMessage(WM_MOUSEWHEEL, wparam, lparam); - return CWnd::OnMouseWheel(nFlags, zDelta, pt); -// return TRUE; -} -#endif - -/* - * Make sure open controls keep the input focus. - */ -void -MainWindow::OnSetFocus(CWnd* /*pOldWnd*/) -{ - if (fpContentList != nil) { - WMSG0("Returning focus to ContentList\n"); - fpContentList->SetFocus(); - } -} - -/* - * User hit F1. We don't currently have context-sensitive help on the main page. - */ -BOOL -MainWindow::OnHelpInfo(HELPINFO* /*lpHelpInfo*/) -{ - //WinHelp(0, HELP_FINDER); - WinHelp(HELP_TOPIC_WELCOME, HELP_CONTEXT); - return TRUE; // dunno what this means -} - -#if 0 -/* - * Catch-all Help handler, necessary to allow CPropertySheet to display a - * "Help" button. (WTF?) - */ -LONG -MainWindow::OnHelp(UINT wParam, LONG lParam) -{ - HELPINFO* lpHelpInfo = (HELPINFO*) lParam; - - DWORD context = lpHelpInfo->iCtrlId; - WMSG1("MainWindow OnHelp (context=%d)\n", context); - WinHelp(context, HELP_CONTEXTPOPUP); - - return TRUE; // yes, we handled it -} -#endif - -/* - * Handle Edit->Preferences by popping up a property sheet. - */ -void -MainWindow::OnEditPreferences(void) -{ - PrefsSheet ps; - ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); - - /* pull any user header tweaks out of list so we can configure prefs */ - if (fpContentList != nil) - fpContentList->ExportColumnWidths(); - - /* set up PrefsGeneralPage */ - for (int i = 0; i < kNumVisibleColumns; i++) { - ps.fGeneralPage.fColumn[i] = (pColLayout->GetColumnWidth(i) != 0); - } - ps.fGeneralPage.fMimicShrinkIt = fPreferences.GetPrefBool(kPrMimicShrinkIt); - ps.fGeneralPage.fBadMacSHK = fPreferences.GetPrefBool(kPrBadMacSHK); - ps.fGeneralPage.fReduceSHKErrorChecks = fPreferences.GetPrefBool(kPrReduceSHKErrorChecks); - ps.fGeneralPage.fCoerceDOSFilenames = fPreferences.GetPrefBool(kPrCoerceDOSFilenames); - ps.fGeneralPage.fSpacesToUnder = fPreferences.GetPrefBool(kPrSpacesToUnder); - ps.fGeneralPage.fPasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths); - ps.fGeneralPage.fBeepOnSuccess = fPreferences.GetPrefBool(kPrBeepOnSuccess); - - /* set up PrefsDiskImagePage */ - ps.fDiskImagePage.fQueryImageFormat = fPreferences.GetPrefBool(kPrQueryImageFormat); - ps.fDiskImagePage.fOpenVolumeRO = fPreferences.GetPrefBool(kPrOpenVolumeRO); - ps.fDiskImagePage.fOpenVolumePhys0 = fPreferences.GetPrefBool(kPrOpenVolumePhys0); - ps.fDiskImagePage.fProDOSAllowLower = fPreferences.GetPrefBool(kPrProDOSAllowLower); - ps.fDiskImagePage.fProDOSUseSparse = fPreferences.GetPrefBool(kPrProDOSUseSparse); - - /* set up PrefsCompressionPage */ - ps.fCompressionPage.fCompressType = fPreferences.GetPrefLong(kPrCompressionType); - - /* set up PrefsFviewPage */ - ps.fFviewPage.fMaxViewFileSizeKB = - (fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024; - ps.fFviewPage.fNoWrapText = fPreferences.GetPrefBool(kPrNoWrapText); - - ps.fFviewPage.fHighlightHexDump = fPreferences.GetPrefBool(kPrHighlightHexDump); - ps.fFviewPage.fHighlightBASIC = fPreferences.GetPrefBool(kPrHighlightBASIC); - ps.fFviewPage.fConvDisasmOneByteBrkCop = fPreferences.GetPrefBool(kPrDisasmOneByteBrkCop); - ps.fFviewPage.fConvHiResBlackWhite = fPreferences.GetPrefBool(kPrConvHiResBlackWhite); - ps.fFviewPage.fConvDHRAlgorithm = fPreferences.GetPrefLong(kPrConvDHRAlgorithm); - ps.fFviewPage.fRelaxGfxTypeCheck = fPreferences.GetPrefBool(kPrRelaxGfxTypeCheck); -// ps.fFviewPage.fEOLConvRaw = fPreferences.GetPrefBool(kPrEOLConvRaw); -// ps.fFviewPage.fConvHighASCII = fPreferences.GetPrefBool(kPrConvHighASCII); - ps.fFviewPage.fConvTextEOL_HA = fPreferences.GetPrefBool(kPrConvTextEOL_HA); - ps.fFviewPage.fConvCPMText = fPreferences.GetPrefBool(kPrConvCPMText); - ps.fFviewPage.fConvPascalText = fPreferences.GetPrefBool(kPrConvPascalText); - ps.fFviewPage.fConvPascalCode = fPreferences.GetPrefBool(kPrConvPascalCode); - ps.fFviewPage.fConvApplesoft = fPreferences.GetPrefBool(kPrConvApplesoft); - ps.fFviewPage.fConvInteger = fPreferences.GetPrefBool(kPrConvInteger); - ps.fFviewPage.fConvGWP = fPreferences.GetPrefBool(kPrConvGWP); - ps.fFviewPage.fConvText8 = fPreferences.GetPrefBool(kPrConvText8); - ps.fFviewPage.fConvAWP = fPreferences.GetPrefBool(kPrConvAWP); - ps.fFviewPage.fConvADB = fPreferences.GetPrefBool(kPrConvADB); - ps.fFviewPage.fConvASP = fPreferences.GetPrefBool(kPrConvASP); - ps.fFviewPage.fConvSCAssem = fPreferences.GetPrefBool(kPrConvSCAssem); - ps.fFviewPage.fConvDisasm = fPreferences.GetPrefBool(kPrConvDisasm); - ps.fFviewPage.fConvHiRes = fPreferences.GetPrefBool(kPrConvHiRes); - ps.fFviewPage.fConvDHR = fPreferences.GetPrefBool(kPrConvDHR); - ps.fFviewPage.fConvSHR = fPreferences.GetPrefBool(kPrConvSHR); - ps.fFviewPage.fConvPrintShop = fPreferences.GetPrefBool(kPrConvPrintShop); - ps.fFviewPage.fConvMacPaint = fPreferences.GetPrefBool(kPrConvMacPaint); - ps.fFviewPage.fConvProDOSFolder = fPreferences.GetPrefBool(kPrConvProDOSFolder); - ps.fFviewPage.fConvResources = fPreferences.GetPrefBool(kPrConvResources); - - /* set up PrefsFilesPage */ - ps.fFilesPage.fTempPath = fPreferences.GetPrefString(kPrTempPath); - ps.fFilesPage.fExtViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); - - if (ps.DoModal() == IDOK) - ApplyNow(&ps); -} - -/* - * Apply a change from the preferences sheet. - */ -void -MainWindow::ApplyNow(PrefsSheet* pPS) -{ - bool mustReload = false; - - //WMSG0("APPLY CHANGES\n"); - - ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); - - if (pPS->fGeneralPage.fDefaultsPushed) { - /* reset all sizes to defaults, then factor in checkboxes */ - WMSG0(" Resetting all widths to defaults\n"); - - /* copy defaults over */ - for (int i = 0; i < kNumVisibleColumns; i++) - pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); - } - - /* handle column checkboxes */ - for (int i = 0; i < kNumVisibleColumns; i++) { - if (pColLayout->GetColumnWidth(i) == 0 && - pPS->fGeneralPage.fColumn[i]) - { - /* restore column */ - WMSG1(" Column %d restored\n", i); - pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); - } else if (pColLayout->GetColumnWidth(i) != 0 && - !pPS->fGeneralPage.fColumn[i]) - { - /* disable column */ - WMSG1(" Column %d hidden\n", i); - pColLayout->SetColumnWidth(i, 0); - } - } - if (fpContentList != nil) - fpContentList->NewColumnWidths(); - fPreferences.SetPrefBool(kPrMimicShrinkIt, - pPS->fGeneralPage.fMimicShrinkIt != 0); - fPreferences.SetPrefBool(kPrBadMacSHK, pPS->fGeneralPage.fBadMacSHK != 0); - fPreferences.SetPrefBool(kPrReduceSHKErrorChecks, - pPS->fGeneralPage.fReduceSHKErrorChecks != 0); - if (fPreferences.GetPrefBool(kPrCoerceDOSFilenames)!= - (pPS->fGeneralPage.fCoerceDOSFilenames != 0)) - { - WMSG1("DOS filename coercion pref now %d\n", - pPS->fGeneralPage.fCoerceDOSFilenames); - fPreferences.SetPrefBool(kPrCoerceDOSFilenames, - pPS->fGeneralPage.fCoerceDOSFilenames != 0); - mustReload = true; - } - if (fPreferences.GetPrefBool(kPrSpacesToUnder) != - (pPS->fGeneralPage.fSpacesToUnder != 0)) - { - WMSG1("Spaces-to-underscores now %d\n", pPS->fGeneralPage.fSpacesToUnder); - fPreferences.SetPrefBool(kPrSpacesToUnder, pPS->fGeneralPage.fSpacesToUnder != 0); - mustReload = true; - } - fPreferences.SetPrefBool(kPrPasteJunkPaths, pPS->fGeneralPage.fPasteJunkPaths != 0); - fPreferences.SetPrefBool(kPrBeepOnSuccess, pPS->fGeneralPage.fBeepOnSuccess != 0); - - if (pPS->fGeneralPage.fOurAssociations != nil) { - WMSG0("NEW ASSOCIATIONS!\n"); - - for (int assoc = 0; assoc < gMyApp.fRegistry.GetNumFileAssocs(); assoc++) - { - gMyApp.fRegistry.SetFileAssoc(assoc, - pPS->fGeneralPage.fOurAssociations[assoc]); - } - - /* delete them so, if they hit "apply" again, we only update once */ - delete[] pPS->fGeneralPage.fOurAssociations; - pPS->fGeneralPage.fOurAssociations = nil; - } - - fPreferences.SetPrefBool(kPrQueryImageFormat, pPS->fDiskImagePage.fQueryImageFormat != 0); - fPreferences.SetPrefBool(kPrOpenVolumeRO, pPS->fDiskImagePage.fOpenVolumeRO != 0); - fPreferences.SetPrefBool(kPrOpenVolumePhys0, pPS->fDiskImagePage.fOpenVolumePhys0 != 0); - fPreferences.SetPrefBool(kPrProDOSAllowLower, pPS->fDiskImagePage.fProDOSAllowLower != 0); - fPreferences.SetPrefBool(kPrProDOSUseSparse, pPS->fDiskImagePage.fProDOSUseSparse != 0); - - fPreferences.SetPrefLong(kPrCompressionType, pPS->fCompressionPage.fCompressType); - - fPreferences.SetPrefLong(kPrMaxViewFileSize, pPS->fFviewPage.fMaxViewFileSizeKB * 1024); - fPreferences.SetPrefBool(kPrNoWrapText, pPS->fFviewPage.fNoWrapText != 0); - - fPreferences.SetPrefBool(kPrHighlightHexDump, pPS->fFviewPage.fHighlightHexDump != 0); - fPreferences.SetPrefBool(kPrHighlightBASIC, pPS->fFviewPage.fHighlightBASIC != 0); - fPreferences.SetPrefBool(kPrDisasmOneByteBrkCop, pPS->fFviewPage.fConvDisasmOneByteBrkCop != 0); - fPreferences.SetPrefBool(kPrConvHiResBlackWhite, pPS->fFviewPage.fConvHiResBlackWhite != 0); - fPreferences.SetPrefLong(kPrConvDHRAlgorithm, pPS->fFviewPage.fConvDHRAlgorithm); - fPreferences.SetPrefBool(kPrRelaxGfxTypeCheck, pPS->fFviewPage.fRelaxGfxTypeCheck != 0); -// fPreferences.SetPrefBool(kPrEOLConvRaw, pPS->fFviewPage.fEOLConvRaw != 0); -// fPreferences.SetPrefBool(kPrConvHighASCII, pPS->fFviewPage.fConvHighASCII != 0); - fPreferences.SetPrefBool(kPrConvTextEOL_HA, pPS->fFviewPage.fConvTextEOL_HA != 0); - fPreferences.SetPrefBool(kPrConvCPMText, pPS->fFviewPage.fConvCPMText != 0); - fPreferences.SetPrefBool(kPrConvPascalText, pPS->fFviewPage.fConvPascalText != 0); - fPreferences.SetPrefBool(kPrConvPascalCode, pPS->fFviewPage.fConvPascalCode != 0); - fPreferences.SetPrefBool(kPrConvApplesoft, pPS->fFviewPage.fConvApplesoft != 0); - fPreferences.SetPrefBool(kPrConvInteger, pPS->fFviewPage.fConvInteger != 0); - fPreferences.SetPrefBool(kPrConvGWP, pPS->fFviewPage.fConvGWP != 0); - fPreferences.SetPrefBool(kPrConvText8, pPS->fFviewPage.fConvText8 != 0); - fPreferences.SetPrefBool(kPrConvAWP, pPS->fFviewPage.fConvAWP != 0); - fPreferences.SetPrefBool(kPrConvADB, pPS->fFviewPage.fConvADB != 0); - fPreferences.SetPrefBool(kPrConvASP, pPS->fFviewPage.fConvASP != 0); - fPreferences.SetPrefBool(kPrConvSCAssem, pPS->fFviewPage.fConvSCAssem != 0); - fPreferences.SetPrefBool(kPrConvDisasm, pPS->fFviewPage.fConvDisasm != 0); - fPreferences.SetPrefBool(kPrConvHiRes, pPS->fFviewPage.fConvHiRes != 0); - fPreferences.SetPrefBool(kPrConvDHR, pPS->fFviewPage.fConvDHR != 0); - fPreferences.SetPrefBool(kPrConvSHR, pPS->fFviewPage.fConvSHR != 0); - fPreferences.SetPrefBool(kPrConvPrintShop, pPS->fFviewPage.fConvPrintShop != 0); - fPreferences.SetPrefBool(kPrConvMacPaint, pPS->fFviewPage.fConvMacPaint != 0); - fPreferences.SetPrefBool(kPrConvProDOSFolder, pPS->fFviewPage.fConvProDOSFolder != 0); - fPreferences.SetPrefBool(kPrConvResources, pPS->fFviewPage.fConvResources != 0); - - fPreferences.SetPrefString(kPrTempPath, pPS->fFilesPage.fTempPath); - WMSG1("--- Temp path now '%s'\n", fPreferences.GetPrefString(kPrTempPath)); - fPreferences.SetPrefString(kPrExtViewerExts, pPS->fFilesPage.fExtViewerExts); - - -// if ((pPS->fGeneralPage.fShowToolbarText != 0) != fPreferences.GetShowToolbarText()) { -// fPreferences.SetShowToolbarText(pPS->fGeneralPage.fShowToolbarText != 0); -// //SetToolbarTextMode(); -// ResizeClientArea(); -// } - - /* allow open archive to track changes to preferences */ - if (fpOpenArchive != nil) - fpOpenArchive->PreferencesChanged(); - - if (mustReload) { - WMSG0("Preferences apply requesting GA/CL reload\n"); - if (fpOpenArchive != nil) - fpOpenArchive->Reload(); - if (fpContentList != nil) - fpContentList->Reload(); - } - - /* export to registry */ - fPreferences.SaveToRegistry(); - - //Invalidate(); -} - -/* - * Handle IDM_EDIT_FIND. - */ -void -MainWindow::OnEditFind(void) -{ - DWORD flags = 0; - - if (fpFindDialog != nil) - return; - - if (fFindDown) - flags |= FR_DOWN; - if (fFindMatchCase) - flags |= FR_MATCHCASE; - if (fFindMatchWholeWord) - flags |= FR_WHOLEWORD; - - fpFindDialog = new CFindReplaceDialog; - - fpFindDialog->Create(TRUE, // "find" only - fFindLastStr, // default string to search for - NULL, // default string to replace - flags, // flags - this); // parent -} -void -MainWindow::OnUpdateEditFind(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != nil); -} - -/* - * Handle activity in the modeless "find" dialog. - */ -LRESULT -MainWindow::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) -{ - assert(fpFindDialog != nil); - - fFindDown = (fpFindDialog->SearchDown() != 0); - fFindMatchCase = (fpFindDialog->MatchCase() != 0); - fFindMatchWholeWord = (fpFindDialog->MatchWholeWord() != 0); - - if (fpFindDialog->IsTerminating()) { - fpFindDialog = nil; - return 0; - } - - if (fpFindDialog->FindNext()) { - fFindLastStr = fpFindDialog->GetFindString(); - fpContentList->FindNext(fFindLastStr, fFindDown, fFindMatchCase, - fFindMatchWholeWord); - } else { - WMSG0("Unexpected find dialog activity\n"); - } - - return 0; -} - - -/* - * Handle IDM_SORT_*. - * - * The "sort" enu item should really only be active if we have a file open. - */ -void -MainWindow::OnEditSort(UINT id) -{ - WMSG1("EDIT SORT %d\n", id); - - ASSERT(id >= IDM_SORT_PATHNAME && id <= IDM_SORT_ORIGINAL); - fPreferences.GetColumnLayout()->SetSortColumn(id - IDM_SORT_PATHNAME); - fPreferences.GetColumnLayout()->SetAscending(true); - if (fpContentList != nil) - fpContentList->NewSortOrder(); -} -void -MainWindow::OnUpdateEditSort(CCmdUI* pCmdUI) -{ - unsigned int column = fPreferences.GetColumnLayout()->GetSortColumn(); - - pCmdUI->SetCheck(pCmdUI->m_nID - IDM_SORT_PATHNAME == column); -} - -/* - * Open the help file. - */ -void -MainWindow::OnHelpContents(void) -{ - WinHelp(0, HELP_FINDER); -} - -/* - * Go to the faddenSoft web site. - */ -void -MainWindow::OnHelpWebSite(void) -{ - int err; - - err = (int) ::ShellExecute(m_hWnd, _T("open"), kWebSiteURL, NULL, NULL, - SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - if (err == ERROR_FILE_NOT_FOUND) { - msg = "Windows call failed: web browser not found. (Sometimes" - " it mistakenly reports this when IE is not the default" - " browser.)"; - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - msg.Format("Unable to launch web browser (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } - } -} - -/* - * Show ordering info (ka-ching!). - */ -void -MainWindow::OnHelpOrdering(void) -{ - WinHelp(HELP_TOPIC_ORDERING_INFO, HELP_CONTEXT); -} - -/* - * Pop up the About box. - */ -void -MainWindow::OnHelpAbout(void) -{ - int result; - - AboutDialog dlg(this); - - result = dlg.DoModal(); - WMSG1("HelpAbout returned %d\n", result); - - /* - * User could've changed registration. If we're showing the registered - * user name in the title bar, update it. - */ - if (fpOpenArchive == nil) - SetCPTitle(); -} - -/* - * Create a new SHK archive, using a "save as" dialog to select the name. - */ -void -MainWindow::OnFileNewArchive(void) -{ - CString filename, saveFolder, errStr; - GenericArchive* pOpenArchive; - CString errMsg; - - CFileDialog dlg(FALSE, _T("shk"), NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "ShrinkIt Archives (*.shk)|*.shk||", this); - - dlg.m_ofn.lpstrTitle = "New Archive"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - filename = dlg.GetPathName(); - WMSG1("NEW FILE '%s'\n", filename); - - /* remove file if it already exists */ - errMsg = RemoveFile(filename); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - pOpenArchive = new NufxArchive; - errStr = pOpenArchive->New(filename, nil); - if (!errStr.IsEmpty()) { - CString failed; - failed.LoadString(IDS_FAILED); - MessageBox(errStr, failed, MB_ICONERROR); - - delete pOpenArchive; - } else { - SwitchContentList(pOpenArchive); - fOpenArchivePathName = dlg.GetPathName(); - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } - -bail: - WMSG0("--- OnFileNewArchive done\n"); -} - - -/* - * Handle request to open an archive or disk image. - */ -void -MainWindow::OnFileOpen(void) -{ - CString openFilters; - CString saveFolder; - - /* set up filters; the order is significant */ - openFilters = kOpenNuFX; - openFilters += kOpenBinaryII; - openFilters += kOpenACU; - openFilters += kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "shk", NULL, - OFN_FILEMUSTEXIST, openFilters, this); - - dlg.m_ofn.nFilterIndex = fPreferences.GetPrefLong(kPrLastOpenFilterIndex); - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - fPreferences.SetPrefLong(kPrLastOpenFilterIndex, dlg.m_ofn.nFilterIndex); - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - DoOpenArchive(dlg.GetPathName(), dlg.GetFileExt(), - dlg.m_ofn.nFilterIndex, dlg.GetReadOnlyPref() != 0); - -bail: - WMSG0("--- OnFileOpen done\n"); -} - -/* - * Handle request to open a raw disk volume. - */ -void -MainWindow::OnFileOpenVolume(void) -{ - WMSG0("--- OnFileOpenVolume\n"); - - int result; - - OpenVolumeDialog dlg(this); - - result = dlg.DoModal(); - if (result != IDOK) - goto bail; - - //DiskImg::SetAllowWritePhys0(fPreferences.GetPrefBool(kPrOpenVolumePhys0)); - DoOpenVolume(dlg.fChosenDrive, dlg.fReadOnly != 0); - -bail: - return; -} -void -MainWindow::OnUpdateFileOpenVolume(CCmdUI* pCmdUI) -{ - // don't really need this function - pCmdUI->Enable(TRUE); -} - -/* - * Open an archive. - */ -void -MainWindow::DoOpenArchive(const char* pathName, const char* ext, - int filterIndex, bool readOnly) -{ - if (LoadArchive(pathName, ext, filterIndex, readOnly, false) == 0) { - /* success, update title bar */ - fOpenArchivePathName = pathName; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } else { - /* some failures will close an open archive */ - //if (fpOpenArchive == nil) - // SetCPTitle(); - } -} - -/* - * Save any pending changes. - * - * This may be called directly from tools, so don't assume that the - * conditions checked for in OnUpdateFileSave hold here. - */ -void -MainWindow::OnFileReopen(void) -{ - ReopenArchive(); -} -void -MainWindow::OnUpdateFileReopen(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != nil); -} - - -/* - * Save any pending changes. - * - * This may be called directly from tools, so don't assume that the - * conditions checked for in OnUpdateFileSave hold here. - */ -void -MainWindow::OnFileSave(void) -{ - CString errMsg; - - if (fpOpenArchive == nil) - return; - - { - CWaitCursor waitc; - errMsg = fpOpenArchive->Flush(); - } - if (!errMsg.IsEmpty()) - ShowFailureMsg(this, errMsg, IDS_FAILED); - - // update the title bar - DoIdle(); -} -void -MainWindow::OnUpdateFileSave(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != nil && fpOpenArchive->IsModified()); -} - -/* - * Close current archive or disk image. - */ -void -MainWindow::OnFileClose(void) -{ - CloseArchive(); - //SetCPTitle(); - WMSG0("--- OnFileClose done\n"); -} -void -MainWindow::OnUpdateFileClose(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != nil); -} - - -/* - * Show detailed information on the current archive. - */ -void -MainWindow::OnFileArchiveInfo(void) -{ - ArchiveInfoDialog* pDlg = nil; - ASSERT(fpOpenArchive != nil); - - switch (fpOpenArchive->GetArchiveKind()) { - case GenericArchive::kArchiveNuFX: - pDlg = new NufxArchiveInfoDialog((NufxArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveDiskImage: - pDlg = new DiskArchiveInfoDialog((DiskArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveBNY: - pDlg = new BnyArchiveInfoDialog((BnyArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveACU: - pDlg = new AcuArchiveInfoDialog((AcuArchive*) fpOpenArchive, this); - break; - default: - WMSG1("Unexpected archive type %d\n", fpOpenArchive->GetArchiveKind()); - ASSERT(false); - return; - }; - - pDlg->DoModal(); - - delete pDlg; -} -void -MainWindow::OnUpdateFileArchiveInfo(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != nil); -} - -/* - * Print the contents of the current archive. - */ -void -MainWindow::OnFilePrint(void) -{ - PrintListing(fpContentList); -} -void -MainWindow::OnUpdateFilePrint(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != nil && fpContentList->GetItemCount() > 0); -} - -/* - * Print a ContentList. - */ -void -MainWindow::PrintListing(const ContentList* pContentList) -{ - CPrintDialog dlg(FALSE); // use CPrintDialogEx for Win2K? CPageSetUpDialog? - PrintContentList pcl; - CDC dc; - int itemCount, numPages; - - itemCount = pContentList->GetItemCount(); - numPages = (itemCount + (pcl.GetLinesPerPage()-1)) / pcl.GetLinesPerPage(); - - dlg.m_pd.nFromPage = dlg.m_pd.nMinPage = 1; - dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = numPages; - - dlg.m_pd.hDevMode = fhDevMode; - dlg.m_pd.hDevNames = fhDevNames; - dlg.m_pd.Flags |= PD_USEDEVMODECOPIESANDCOLLATE; - dlg.m_pd.Flags &= ~(PD_NOPAGENUMS); - if (dlg.DoModal() != IDOK) - return; - if (dc.Attach(dlg.GetPrinterDC()) != TRUE) { - CString msg; - msg.LoadString(IDS_PRINTER_NOT_USABLE); - ShowFailureMsg(this, msg, IDS_FAILED); - return; - } - - pcl.Setup(&dc, this); - if (dlg.m_pd.Flags & PD_PAGENUMS) - pcl.Print(pContentList, dlg.m_pd.nFromPage, dlg.m_pd.nToPage); - else - pcl.Print(pContentList); - - fhDevMode = dlg.m_pd.hDevMode; - fhDevNames = dlg.m_pd.hDevNames; -} - - -/* - * Handle Exit item by sending a close request. - */ -void -MainWindow::OnFileExit(void) -{ - SendMessage(WM_CLOSE, 0, 0); -} - - -/* - * Select everything in the content list. - */ -void -MainWindow::OnEditSelectAll(void) -{ - ASSERT(fpContentList != nil); - fpContentList->SelectAll(); -} -void -MainWindow::OnUpdateEditSelectAll(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != nil); -} - -/* - * Invert the content list selection. - */ -void -MainWindow::OnEditInvertSelection(void) -{ - ASSERT(fpContentList != nil); - fpContentList->InvertSelection(); -} -void -MainWindow::OnUpdateEditInvertSelection(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != nil); -} - - -/* - * Get the one selected item from the current display. Primarily useful - * for the double-click handler, but also used for "action" menu items - * that insist on operating on a single menu item (edit prefs, create subdir). - * - * Returns nil if the item couldn't be found or if more than one item was - * selected. - */ -GenericEntry* -MainWindow::GetSelectedItem(ContentList* pContentList) -{ - if (pContentList->GetSelectedCount() != 1) - return nil; - - POSITION posn; - posn = pContentList->GetFirstSelectedItemPosition(); - if (posn == nil) { - ASSERT(false); - return nil; - } - int num = pContentList->GetNextSelectedItem(/*ref*/ posn); - GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(num); - if (pEntry == nil) { - WMSG1(" Glitch: couldn't find entry %d\n", num); - ASSERT(false); - } - - return pEntry; -} - -/* - * Handle a double-click. - * - * Individual items get special treatment, multiple items just get handed off - * to the file viewer. - */ -void -MainWindow::HandleDoubleClick(void) -{ - bool handled = false; - - ASSERT(fpContentList != nil); - if (fpContentList->GetSelectedCount() == 0) { - /* nothing selected, they double-clicked outside first column */ - WMSG0("Double-click but nothing selected\n"); - return; - } - if (fpContentList->GetSelectedCount() != 1) { - /* multiple items, just bring up viewer */ - HandleView(); - return; - } - - /* - * Find the GenericEntry that corresponds to this item. - */ - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == nil) - return; - - WMSG1(" Double-click GOT '%s'\n", pEntry->GetPathName()); - const char* ext; - long fileType, auxType; - - ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); - fileType = pEntry->GetFileType(); - auxType = pEntry->GetAuxType(); - - /* // unit tests for MatchSemicolonList - MatchSemicolonList("gif; jpeg; jpg", "jpeg"); - MatchSemicolonList("gif; jpeg; jpg", "jpg"); - MatchSemicolonList("gif; jpeg; jpg", "gif"); - MatchSemicolonList("gif;jpeg;jpg", "gif;"); - MatchSemicolonList("gif; jpeg; jpg", "jpe"); - MatchSemicolonList("gif; jpeg; jpg", "jpegx"); - MatchSemicolonList("gif; jpeg; jpg", "jp"); - MatchSemicolonList("gif; jpeg; jpg", "jpgx"); - MatchSemicolonList("gif; jpeg; jpg", "if"); - MatchSemicolonList("gif; jpeg; jpg", "gifs"); - MatchSemicolonList("gif, jpeg; jpg", "jpeg"); - MatchSemicolonList("", "jpeg"); - MatchSemicolonList(";", "jpeg"); - MatchSemicolonList("gif, jpeg; jpg", ""); - */ - - /* - * Figure out what to do with it. - */ - CString extViewerExts; - extViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); - if (ext != nil && MatchSemicolonList(extViewerExts, ext+1)) { - WMSG1(" Launching external viewer for '%s'\n", ext); - TmpExtractForExternal(pEntry); - handled = true; - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) { - if ((ext != nil && ( - stricmp(ext, ".shk") == 0 || - stricmp(ext, ".sdk") == 0 || - stricmp(ext, ".bxy") == 0 )) || - (fileType == 0xe0 && auxType == 0x8002)) - { - WMSG0(" Guessing NuFX\n"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeNuFX); - handled = true; - } else - if ((ext != nil && ( - stricmp(ext, ".bny") == 0 || - stricmp(ext, ".bqy") == 0 )) || - (fileType == 0xe0 && auxType == 0x8000)) - { - WMSG0(" Guessing Binary II\n"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeBinaryII); - handled = true; - } else - if ((ext != nil && ( - stricmp(ext, ".acu") == 0 )) || - (fileType == 0xe0 && auxType == 0x8001)) - { - WMSG0(" Guessing ACU\n"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeACU); - handled = true; - } else - if (fileType == 0x64496d67 && auxType == 0x64437079 && - pEntry->GetUncompressedLen() == 819284) - { - /* type is dImg, creator is dCpy, length is 800K + DC stuff */ - WMSG0(" Looks like a disk image\n"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage); - handled = true; - } - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { - WMSG0(" Opening archived disk image\n"); - TmpExtractAndOpen(pEntry, GenericEntry::kDiskImageThread, kModeDiskImage); - handled = true; - } - - if (!handled) { - // standard viewer - HandleView(); - } - - /* set "/t" temp flag and delete afterward, warning user (?) */ -} - -/* - * Extract a record to the temp folder and open it with a new instance of - * CiderPress. We might want to extract disk images as 2MG files to take - * the mystery out of opening them, but since they're coming out of a - * ShrinkIt archive they're pretty un-mysterious anyway. - * - * We tell the new instance to open it read-only, and flag it for - * deletion on exit. - * - * Returns 0 on success, nonzero error status on failure. - */ -int -MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, - const char* modeStr) -{ - CString dispName; - bool mustDelete = false; - - /* - * Get the name to display in the title bar. Double quotes will - * screw it up, so we have to replace them. (We could escape them, - * but then we'd also have to escape the escape char.) - */ - dispName = pEntry->GetFileName(); - dispName.Replace('"', '_'); - - char nameBuf[MAX_PATH]; - UINT unique; - unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - "CPfile", 0, nameBuf); - if (unique == 0) { - DWORD dwerr = ::GetLastError(); - WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", - fPreferences.GetPrefString(kPrTempPath), dwerr); - return dwerr; - } - mustDelete = true; - - /* - * Open the temp file and extract the data into it. - */ - CString errMsg; - int result; - FILE* fp; - - fp = fopen(nameBuf, "wb"); - if (fp != nil) { - WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); - result = pEntry->ExtractThreadToFile(threadKind, fp, - GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, - &errMsg); - fclose(fp); - if (result == IDOK) { - /* success */ - CString parameters; - - parameters.Format("-mode %s -dispname \"%s\" -temparc \"%s\"", - modeStr, dispName, nameBuf); - int err; - - err = (int) ::ShellExecute(m_hWnd, _T("open"), - gMyApp.GetExeFileName(), parameters, NULL, - SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - msg.Format("Unable to launch CiderPress (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - /* during dev, "missing DLL" causes false-positive success */ - WMSG0("Successfully launched CiderPress\n"); - mustDelete = false; // up to newly-launched app - } - } else { - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - } else { - CString msg; - msg.Format("Unable to open temp file '%s'.", nameBuf); - ::ShowFailureMsg(this, msg, IDS_FAILED); - } - - if (mustDelete) { - WMSG1("Deleting '%s'\n", nameBuf); - unlink(nameBuf); - } - - return 0; -} - -/* - * Extract a record to the temp folder and open it with an external viewer. - * The file must be created with the correct extension so ShellExecute - * does the right thing. - * - * The files will be added to the "delete on exit" list, so that they will - * be cleaned up when CiderPress exits (assuming the external viewer no longer - * has them open). - * - * The GetTempFileName function creates a uniquely-named temp file. We - * create a file that has that name plus an extension. To ensure that we - * don't try to use the same temp filename twice, we have to hold off on - * deleting the unused .tmp files until we're ready to delete the - * corresponding .gif (or whatever) files. Thus, each invocation of this - * function creates two files and two entries in the delete-on-exit set. - * - * Returns 0 on success, nonzero error status on failure. - */ -int -MainWindow::TmpExtractForExternal(GenericEntry* pEntry) -{ - const char* ext; - - ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); - - char nameBuf[MAX_PATH]; - UINT unique; - unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - "CPfile", 0, nameBuf); - if (unique == 0) { - DWORD dwerr = ::GetLastError(); - WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", - fPreferences.GetPrefString(kPrTempPath), dwerr); - return dwerr; - } - fDeleteList.Add(nameBuf); // file is created by GetTempFileName - - strcat(nameBuf, ext); - - /* - * Open the temp file and extract the data into it. - */ - CString errMsg; - int result; - FILE* fp; - - fp = fopen(nameBuf, "wb"); - if (fp != nil) { - fDeleteList.Add(nameBuf); // second file created by fopen - WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); - result = pEntry->ExtractThreadToFile(GenericEntry::kDataThread, fp, - GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, - &errMsg); - fclose(fp); - if (result == IDOK) { - /* success */ - int err; - - err = (int) ::ShellExecute(m_hWnd, _T("open"), nameBuf, NULL, - NULL, SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - msg.Format("Unable to launch external viewer (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - WMSG0("Successfully launched external viewer\n"); - } - } else { - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - } else { - CString msg; - msg.Format("Unable to open temp file '%s'.", nameBuf); - ShowFailureMsg(this, msg, IDS_FAILED); - } - - return 0; -} - -#if 0 -/* - * Handle a "default action" selection from the right-click menu. The - * action only applies to the record that was clicked on, so we need to - * retrieve that from the control. - */ -void -MainWindow::OnRtClkDefault(void) -{ - int idx; - - ASSERT(fpContentList != nil); - - idx = fpContentList->GetRightClickItem(); - ASSERT(idx != -1); - WMSG1("OnRtClkDefault %d\n", idx); - - fpContentList->ClearRightClickItem(); -} -#endif - - -/* - * =================================== - * Progress meter - * =================================== - */ - -/* - * There are two different mechanisms for reporting progress: ActionProgress - * dialogs (for adding/extracting files) and a small box in the lower - * right-hand corner (for opening archives). These functions will set - * the progress in the active action progress dialog if it exists, or - * will set the percentage in the window frame if not. - */ - -void -MainWindow::SetProgressBegin(void) -{ - if (fpActionProgress != nil) - fpActionProgress->SetProgress(0); - else - fStatusBar.SetPaneText(kProgressPane, "--%"); - //WMSG0(" Complete: BEGIN\n"); - - /* redraw stuff with the changes */ - (void) PeekAndPump(); -} - -int -MainWindow::SetProgressUpdate(int percent, const char* oldName, - const char* newName) -{ - int status = IDOK; - - if (fpActionProgress != nil) { - status = fpActionProgress->SetProgress(percent); - if (oldName != nil) - fpActionProgress->SetArcName(oldName); - if (newName != nil) - fpActionProgress->SetFileName(newName); - } else { - char buf[8]; - sprintf(buf, "%d%%", percent); - fStatusBar.SetPaneText(kProgressPane, buf); - //WMSG1(" Complete: %s\n", buf); - } - - if (!PeekAndPump()) { - WMSG0("SetProgressUpdate: shutdown?!\n"); - } - - //EventPause(10); // DEBUG DEBUG - return status; -} - -void -MainWindow::SetProgressEnd(void) -{ - if (fpActionProgress != nil) - fpActionProgress->SetProgress(100); - else - fStatusBar.SetPaneText(kProgressPane, ""); -// EventPause(100); // DEBUG DEBUG - //WMSG0(" Complete: END\n"); -} - - -/* - * Set a number in the "progress counter". Useful for loading large archives - * where we're not sure how much stuff is left, so showing a percentage is - * hard. - * - * Pass in -1 to erase the counter. - * - * Returns "true" if we'd like things to continue. - */ -bool -MainWindow::SetProgressCounter(const char* str, long val) -{ - /* if the main window is enabled, user could activate menus */ - ASSERT(!IsWindowEnabled()); - - if (fpProgressCounter != nil) { - //WMSG2("SetProgressCounter '%s' %d\n", str, val); - CString msg; - - if (str != nil) - fpProgressCounter->SetCounterFormat(str); - fpProgressCounter->SetCount((int) val); - } else { - if (val < 0) { - fStatusBar.SetPaneText(kProgressPane, ""); - } else { - CString tmpStr; - tmpStr.Format("%ld", val); - fStatusBar.SetPaneText(kProgressPane, tmpStr); - } - } - - if (!PeekAndPump()) { - WMSG0("SetProgressCounter: shutdown?!\n"); - } - //EventPause(10); // DEBUG DEBUG - - if (fpProgressCounter != nil) - return !fpProgressCounter->GetCancel(); - else - return true; -} - - -/* - * Allow events to flow through the message queue whenever the - * progress meter gets updated. This will allow us to redraw with - * reasonable frequency. - * - * Calling this can result in other code being called, such as Windows - * message handlers, which can lead to reentrancy problems. Make sure - * you're adequately semaphored before calling here. - * - * Returns TRUE if all is well, FALSE if we're trying to quit. - */ -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; -} - -/* - * Go to sleep for a little bit, waking up 100x per second to check - * the idle loop. - */ -void -MainWindow::EventPause(int duration) -{ - int count = duration / 10; - - for (int i = 0; i < count; i++) { - PeekAndPump(); - ::Sleep(10); - } -} - -/* - * Printer abort procedure; allows us to abort a print job. The DC - * SetAbortProc() function calls here periodically. The return value from - * this function determine whether or not printing halts. - * - * This checks a global "print cancel" variable, which is set by our print - * cancel button dialog. - * - * If this returns TRUE, printing continues; FALSE, and printing aborts. - */ -/*static*/ BOOL CALLBACK -MainWindow::PrintAbortProc(HDC hDC, int nCode) -{ - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - - pMain->PeekAndPump(); - if (pMain->GetAbortPrinting()) { - WMSG0("PrintAbortProc returning FALSE (abort printing)\n"); - return FALSE; - } - WMSG0(" PrintAbortProc returning TRUE (continue printing)\n"); - return TRUE; -} - -/* - * =================================== - * Support functions - * =================================== - */ - -/* - * Draw what looks like an empty client area. - */ -void -MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect) -{ - CBrush brush; - brush.CreateSolidBrush(::GetSysColor(COLOR_APPWORKSPACE)); // dk gray - CBrush* pOldBrush = pDC->SelectObject(&brush); - pDC->FillRect(&clientRect, &brush); - pDC->SelectObject(pOldBrush); - - CPen penWH(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)); // white - CPen penLG(PS_SOLID, 1, ::GetSysColor(COLOR_3DLIGHT)); // lt gray - CPen penDG(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); // dk gray - CPen penBL(PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW)); // near-black - CPen* pOldPen = pDC->SelectObject(&penWH); - //pDC->SelectObject(&penWH); - pDC->MoveTo(clientRect.right-1, clientRect.top); - pDC->LineTo(clientRect.right-1, clientRect.bottom-1); - pDC->LineTo(clientRect.left-1, clientRect.bottom-1); - pDC->SelectObject(&penBL); - pDC->MoveTo(clientRect.right-3, clientRect.top+1); - pDC->LineTo(clientRect.left+1, clientRect.top+1); - pDC->LineTo(clientRect.left+1, clientRect.bottom-2); - pDC->SelectObject(&penLG); - pDC->MoveTo(clientRect.right-2, clientRect.top+1); - pDC->LineTo(clientRect.right-2, clientRect.bottom-2); - pDC->LineTo(clientRect.left, clientRect.bottom-2); - pDC->SelectObject(&penDG); - pDC->MoveTo(clientRect.right-2, clientRect.top); - pDC->LineTo(clientRect.left, clientRect.top); - pDC->LineTo(clientRect.left, clientRect.bottom-1); - - pDC->SelectObject(pOldPen); -} - -/* - * Load an archive, using the appropriate GenericArchive subclass. If - * "createFile" is "true", a new archive file will be created (and must - * not already exist!). - * - * "filename" is the full path to the file, "extension" is the - * filetype component of the name (without the leading '.'), "filterIndex" - * is the offset into the set of filename filters used in the standard - * file dialog, "readOnly" reflects the state of the stdfile dialog - * checkbox, and "createFile" is set to true by the "New Archive" command. - * - * Returns 0 on success, nonzero on failure. - */ -int -MainWindow::LoadArchive(const char* fileName, const char* extension, - int filterIndex, bool readOnly, bool createFile) -{ - GenericArchive::OpenResult openResult; - int result = -1; - GenericArchive* pOpenArchive = nil; - int origFilterIndex = filterIndex; - CString errStr, appName; - - appName.LoadString(IDS_MB_APP_NAME); - - WMSG3("LoadArchive: '%s' ro=%d idx=%d\n", fileName, readOnly, filterIndex); - - /* close any existing archive to avoid weirdness from re-open */ - CloseArchive(); - - /* - * If they used the "All Files (*.*)" filter, we have to guess based - * on the file type. - * - * IDEA: change the current "filterIndex ==" stuff to a type-specific - * model, then do type-scanning here. Code later on takes the type - * and opens it. That way we can do the trivial "it must be" handling - * up here, and maybe do a little "open it up and see" stuff as well. - * In general, though, if we don't recognize the extension, it's - * probably a disk image. - */ - if (filterIndex == kFilterIndexGeneric) { - int i; - - for (i = 0; i < NELEM(gExtensionToIndex); i++) { - if (strcasecmp(extension, gExtensionToIndex[i].extension) == 0) { - filterIndex = gExtensionToIndex[i].idx; - break; - } - } - - if (i == NELEM(gExtensionToIndex)) - filterIndex = kFilterIndexDiskImage; - } - -try_again: - if (filterIndex == kFilterIndexBinaryII) { - /* try Binary II and nothing else */ - ASSERT(!createFile); - WMSG0(" Trying Binary II\n"); - pOpenArchive = new BnyArchive; - openResult = pOpenArchive->Open(fileName, readOnly, &errStr); - if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - result = -1; - goto bail; - } - } else - if (filterIndex == kFilterIndexACU) { - /* try ACU and nothing else */ - ASSERT(!createFile); - WMSG0(" Trying ACU\n"); - pOpenArchive = new AcuArchive; - openResult = pOpenArchive->Open(fileName, readOnly, &errStr); - if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - result = -1; - goto bail; - } - } else - if (filterIndex == kFilterIndexDiskImage) { - /* try various disk image formats */ - ASSERT(!createFile); - WMSG0(" Trying disk images\n"); - - pOpenArchive = new DiskArchive; - openResult = pOpenArchive->Open(fileName, readOnly, &errStr); - if (openResult == GenericArchive::kResultCancel) { - result = -1; - goto bail; - } else if (openResult == GenericArchive::kResultFileArchive) { - delete pOpenArchive; - pOpenArchive = nil; - - if (strcasecmp(extension, "zip") == 0) { - errStr = "ZIP archives with multiple files are not supported."; - MessageBox(errStr, appName, MB_OK|MB_ICONINFORMATION); - result = -1; - goto bail; - } else { - /* assume some variation of a ShrinkIt archive */ - // msg.LoadString(IDS_OPEN_AS_NUFX); <-- with MB_OKCANCEL - filterIndex = kFilterIndexNuFX; - goto try_again; - } - - } else if (openResult != GenericArchive::kResultSuccess) { - if (filterIndex != origFilterIndex) { - /* - * Kluge: assume we guessed disk image and were wrong. - */ - errStr = "File doesn't appear to be a valid archive" - " or disk image."; - } - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - result = -1; - goto bail; - } - } else - if (filterIndex == kFilterIndexNuFX) { - /* try NuFX (including its embedded-in-BNY form) */ - WMSG0(" Trying NuFX\n"); - - pOpenArchive = new NufxArchive; - openResult = pOpenArchive->Open(fileName, readOnly, &errStr); - if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - result = -1; - goto bail; - } - - } else { - ASSERT(FALSE); - result = -1; - goto bail; - } - - SwitchContentList(pOpenArchive); - - pOpenArchive = nil; - result = 0; - -bail: - if (pOpenArchive != nil) { - ASSERT(result != 0); - delete pOpenArchive; - } - return result; -} - -/* - * Open a raw disk volume. Useful for ProDOS-formatted 1.44MB floppy disks - * and CFFA flash cards. - * - * Assume it's a disk image -- it'd be a weird place for a ShrinkIt archive. - * CFFA cards can actually hold multiple volumes, but that's all taken care - * of inside the diskimg DLL. - * - * Returns 0 on success, nonzero on failure. - */ -int -MainWindow::DoOpenVolume(CString drive, bool readOnly) -{ - int result = -1; - - ASSERT(drive.GetLength() > 0); - - CString errStr; - //char filename[4] = "_:\\"; - //filename[0] = driveLetter; - - WMSG2("FileOpenVolume '%s' %d\n", (const char*)drive, readOnly); - - /* close existing archive */ - CloseArchive(); - - GenericArchive* pOpenArchive = nil; - pOpenArchive = new DiskArchive; - { - CWaitCursor waitc; - GenericArchive::OpenResult openResult; - - openResult = pOpenArchive->Open(drive, readOnly, &errStr); - if (openResult == GenericArchive::kResultCancel) { - // this bubbles out of the format confirmation dialog - goto bail; - } else if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - } - - // success! - SwitchContentList(pOpenArchive); - pOpenArchive = nil; - fOpenArchivePathName = drive; - result = 0; - - fOpenArchivePathName = drive; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - -bail: - if (pOpenArchive != nil) { - ASSERT(result != 0); - delete pOpenArchive; - } - return result; -} - - -/* - * Close and re-open the current archive. - */ -void -MainWindow::ReopenArchive(void) -{ - if (fpOpenArchive == nil) { - ASSERT(false); - return; - } - - /* clear the flag, regardless of success or failure */ - fNeedReopen = false; - - GenericArchive* pOpenArchive = nil; - CString pathName = fpOpenArchive->GetPathName(); - bool readOnly = fpOpenArchive->IsReadOnly(); - GenericArchive::ArchiveKind archiveKind = fpOpenArchive->GetArchiveKind(); - GenericArchive::OpenResult openResult; - CString errStr; - - /* if the open fails we *don't* want to leave the previous content up */ - WMSG3("Reopening '%s' ro=%d kind=%d\n", pathName, readOnly, archiveKind); - CloseArchive(); - - switch (archiveKind) { - case GenericArchive::kArchiveDiskImage: - pOpenArchive = new DiskArchive; - break; - case GenericArchive::kArchiveNuFX: - pOpenArchive = new NufxArchive; - break; - case GenericArchive::kArchiveBNY: - pOpenArchive = new BnyArchive; - break; - default: - ASSERT(false); - return; - } - - openResult = pOpenArchive->Open(pathName, readOnly, &errStr); - if (openResult == GenericArchive::kResultCancel) { - // this bubbles out of the format confirmation dialog - goto bail; - } else if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - - WMSG0(" Reopen was successful\n"); - SwitchContentList(pOpenArchive); - pOpenArchive = nil; - SetCPTitle(pathName, fpOpenArchive); - -bail: - delete pOpenArchive; -} - -/* - * Determine whether "path" matches the pathname of the currently open archive. - */ -bool -MainWindow::IsOpenPathName(const char* path) -{ - if (fpOpenArchive == nil) - return false; - - if (stricmp(path, fpOpenArchive->GetPathName()) == 0) - return true; - - return false; -} - - -/* - * Switch the content list to a new archive, closing the previous if one - * was already open. - */ -void -MainWindow::SwitchContentList(GenericArchive* pOpenArchive) -{ - assert(pOpenArchive != nil); - - /* - * We've got an archive opened successfully. If we already had one - * open, shut it. (This assumes that closing an archive is a simple - * matter of closing files and freeing storage. If we needed to do - * something that might fail, like flush changes, we should've done - * that before getting this far to avoid confusion.) - */ - if (fpOpenArchive != nil) - CloseArchive(); - - ASSERT(fpOpenArchive == nil); - ASSERT(fpContentList == nil); - - /* - * Without this we get an assertion failure in CImageList::Attach if we - * call here from ReopenArchive. I think Windows needs to do some - * cleanup, though I don't understand how the reopen case differs from - * the usual case. Maybe there's more stuff pending in the "reopen" - * case? In any event, this seems to work, which is all you can hope - * for from MFC. It does, however, make the screen flash, which it - * didn't do before. - * - * UPDATE: this tripped once while I was debugging, even with this. The - * PeekAndPump function does force the idle loop to run, so I'm not sure - * why it failed, unless the debugger somehow affected the idle - * processing. Yuck. - * - * The screen flash bugged me so I took it back out. And the assert - * didn't hit. I really, really love Windows. - */ - //PeekAndPump(); - - - fpContentList = new ContentList(pOpenArchive, - fPreferences.GetColumnLayout()); - - CRect sizeRect; - GetClientRect(&sizeRect); - fpContentList->Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL, - sizeRect, this, IDC_CONTENT_LIST); - - fpOpenArchive = pOpenArchive; -} - - -/* - * Close the existing archive file, but don't try to shut down the child - * windows. This should really only be used from the destructor. - */ -void -MainWindow::CloseArchiveWOControls(void) -{ - if (fpOpenArchive != nil) { - //fpOpenArchive->Close(); - WMSG0("Deleting OpenArchive\n"); - delete fpOpenArchive; - fpOpenArchive = nil; - } -} - -/* - * Close the existing archive file, and throw out the control we're - * using to display it. - */ -void -MainWindow::CloseArchive(void) -{ - CWaitCursor waitc; // closing large compressed archive can be slow - - // destroy the ContentList - if (fpContentList != nil) { - WMSG0("Destroying ContentList\n"); - fpContentList->DestroyWindow(); // auto-cleanup invokes "delete" - fpContentList = nil; - } - - // destroy the GenericArchive - CloseArchiveWOControls(); - - // reset the title bar - SetCPTitle(); -} - - -/* - * Set the title bar on the main window. - * - * "pathname" is often different from pOpenArchive->GetPathName(), especially - * when we were launched from another instance of CiderPress and handed a - * temp file whose name we're trying to conceal. - */ -void -MainWindow::SetCPTitle(const char* pathname, GenericArchive* pOpenArchive) -{ - ASSERT(pathname != nil); - CString title; - CString archiveDescription; - CString appName; - - appName.LoadString(IDS_MB_APP_NAME); - - pOpenArchive->GetDescription(&archiveDescription); - title.Format(_T("%s - %s (%s)"), appName, pathname, archiveDescription); - - if (fpOpenArchive->IsReadOnly()) { - CString readOnly; - readOnly.LoadString(IDS_READONLY); - title += _T(" "); - title += readOnly; - } - - SetWindowText(title); -} - -/* - * Set the title bar to something boring when nothing is open. - */ -void -MainWindow::SetCPTitle(void) -{ - CString appName, regName, title; - CString user, company, reg, versions, expire; - -#if 0 - if (gMyApp.fRegistry.GetRegistration(&user, &company, ®, &versions, - &expire) == 0) - { - if (reg.IsEmpty()) { - regName += _T(" (unregistered)"); - } else { - regName += _T(" (registered to "); - regName += user; - regName += _T(")"); - // include company? - } - } -#endif - - appName.LoadString(IDS_MB_APP_NAME); - title = appName + regName; - SetWindowText(title); -} - -/* - * Come up with a title to put at the top of a printout. This is essentially - * the same as the window title, but without some flags (e.g. "read-only"). - */ -CString -MainWindow::GetPrintTitle(void) -{ - CString title; - CString archiveDescription; - CString appName; - - if (fpOpenArchive == nil) { - ASSERT(false); - return title; - } - - appName.LoadString(IDS_MB_APP_NAME); - - fpOpenArchive->GetDescription(&archiveDescription); - title.Format(_T("%s - %s (%s)"), - appName, fOpenArchivePathName, archiveDescription); - - return title; -} - - -/* - * After successful completion of a command, make a happy noise (but only - * if we're configured to do so). - */ -void -MainWindow::SuccessBeep(void) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { - WMSG0("\n"); - ::MessageBeep(MB_OK); - } -} - -/* - * If something fails, make noise if we're configured for loudness. - */ -void -MainWindow::FailureBeep(void) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { - WMSG0("\n"); - ::MessageBeep(MB_ICONEXCLAMATION); // maybe MB_ICONHAND? - } -} - -/* - * Remove a file. Returns a helpful error string on failure. - * - * The absence of the file is not considered an error. - */ -CString -MainWindow::RemoveFile(const char* fileName) -{ - CString errMsg; - - int cc; - cc = unlink(fileName); - if (cc < 0 && errno != ENOENT) { - int err = errno; - WMSG2("Failed removing file '%s', errno=%d\n", fileName, err); - errMsg.Format("Unable to remove '%s': %s.", - fileName, strerror(err)); - if (err == EACCES) - errMsg += "\n\n(Make sure the file isn't open.)"; - } - - return errMsg; -} - - -/* - * Configure a ReformatHolder based on the current preferences. - */ -/*static*/ void -MainWindow::ConfigureReformatFromPreferences(ReformatHolder* pReformat) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - pReformat->SetReformatAllowed(ReformatHolder::kReformatRaw, true); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHexDump, true); - - pReformat->SetReformatAllowed(ReformatHolder::kReformatTextEOL_HA, - pPreferences->GetPrefBool(kPrConvTextEOL_HA)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatResourceFork, - pPreferences->GetPrefBool(kPrConvResources)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatProDOSDirectory, - pPreferences->GetPrefBool(kPrConvProDOSFolder)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalText, - pPreferences->GetPrefBool(kPrConvPascalText)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalCode, - pPreferences->GetPrefBool(kPrConvPascalCode)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatCPMText, - pPreferences->GetPrefBool(kPrConvCPMText)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft, - pPreferences->GetPrefBool(kPrConvApplesoft)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft_Hilite, - pPreferences->GetPrefBool(kPrConvApplesoft)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger, - pPreferences->GetPrefBool(kPrConvInteger)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger_Hilite, - pPreferences->GetPrefBool(kPrConvInteger)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSCAssem, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMerlin, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA2, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA3, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA4, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor8, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmMerlin8, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Long, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Short, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmOrcam16, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWGS_WP, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatTeach, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatGWP, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMagicWindow, - pPreferences->GetPrefBool(kPrConvText8)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, - pPreferences->GetPrefBool(kPrConvAWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, - pPreferences->GetPrefBool(kPrConvAWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatADB, - pPreferences->GetPrefBool(kPrConvADB)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatASP, - pPreferences->GetPrefBool(kPrConvASP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes, - pPreferences->GetPrefBool(kPrConvHiRes)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes_BW, - pPreferences->GetPrefBool(kPrConvHiRes)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Latched, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_BW, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Plain140, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Window, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_PIC, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_JEQ, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Paintworks, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Packed, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_APF, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3200, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3201, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG256, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG3200, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPrintShop, - pPreferences->GetPrefBool(kPrConvPrintShop)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMacPaint, - pPreferences->GetPrefBool(kPrConvMacPaint)); - - pReformat->SetOption(ReformatHolder::kOptHiliteHexDump, - pPreferences->GetPrefBool(kPrHighlightHexDump)); - pReformat->SetOption(ReformatHolder::kOptHiliteBASIC, - pPreferences->GetPrefBool(kPrHighlightBASIC)); - pReformat->SetOption(ReformatHolder::kOptHiResBW, - pPreferences->GetPrefBool(kPrConvHiResBlackWhite)); - pReformat->SetOption(ReformatHolder::kOptDHRAlgorithm, - pPreferences->GetPrefLong(kPrConvDHRAlgorithm)); - pReformat->SetOption(ReformatHolder::kOptRelaxGfxTypeCheck, - pPreferences->GetPrefBool(kPrRelaxGfxTypeCheck)); - pReformat->SetOption(ReformatHolder::kOptOneByteBrkCop, - pPreferences->GetPrefBool(kPrDisasmOneByteBrkCop)); -} - -/* - * Convert a DiskImg format spec into a ReformatHolder SourceFormat. - */ -/*static*/ ReformatHolder::SourceFormat -MainWindow::ReformatterSourceFormat(DiskImg::FSFormat format) -{ - if (DiskImg::UsesDOSFileStructure(format)) - return ReformatHolder::kSourceFormatDOS; - else if (format == DiskImg::kFormatCPM) - return ReformatHolder::kSourceFormatCPM; - else - return ReformatHolder::kSourceFormatGeneric; -} +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Main window management. + */ +#include "stdafx.h" +#include "Main.h" +#include "MyApp.h" +#include "AboutDialog.h" +#include "NufxArchive.h" +#include "DiskArchive.h" +#include "BNYArchive.h" +#include "ACUArchive.h" +#include "ArchiveInfoDialog.h" +#include "PrefsDialog.h" +#include "EnterRegDialog.h" +#include "OpenVolumeDialog.h" +#include "Print.h" +#include "HelpTopics.h" +#include "../util/UtilLib.h" +#include "resource.h" + +/* use MFC's fancy version of new for debugging */ +//#define new DEBUG_NEW + +static const char* kWebSiteURL = "http://www.faddensoft.com/"; + +/* + * Filters for the "open file" command. In some cases a file may be opened + * in more than one format, so it's necessary to keep track of what the + * file filter was set to when the file was opened. + */ +const char MainWindow::kOpenNuFX[] = + "ShrinkIt Archives (.shk .sdk .bxy .sea .bse)|*.shk;*.sdk;*.bxy;*.sea;*.bse|"; +const char MainWindow::kOpenBinaryII[] = + "Binary II Archives (.bny .bqy .bxy)|*.bny;*.bqy;*.bxy|"; +const char MainWindow::kOpenACU[] = + "ACU Archives (.acu)|*.acu|"; +const char MainWindow::kOpenDiskImage[] = + "Disk Images (.shk .sdk .dsk .po .do .d13 .2mg .img .nib .nb2 .raw .hdv .dc .dc6 .ddd .app .fdi .iso .gz .zip)|" + "*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|"; +const char MainWindow::kOpenAll[] = + "All Files (*.*)|*.*|"; +const char MainWindow::kOpenEnd[] = + "|"; + +static const struct { + //const char* extension; + char extension[4]; + FilterIndex idx; +} gExtensionToIndex[] = { + { "shk", kFilterIndexNuFX }, + { "bxy", kFilterIndexNuFX }, + { "bse", kFilterIndexNuFX }, + { "sea", kFilterIndexNuFX }, + { "bny", kFilterIndexBinaryII }, + { "bqy", kFilterIndexBinaryII }, + { "acu", kFilterIndexACU }, + { "dsk", kFilterIndexDiskImage }, + { "po", kFilterIndexDiskImage }, + { "do", kFilterIndexDiskImage }, + { "d13", kFilterIndexDiskImage }, + { "2mg", kFilterIndexDiskImage }, + { "img", kFilterIndexDiskImage }, + { "sdk", kFilterIndexDiskImage }, + { "raw", kFilterIndexDiskImage }, + { "ddd", kFilterIndexDiskImage }, + { "app", kFilterIndexDiskImage }, + { "fdi", kFilterIndexDiskImage }, + { "iso", kFilterIndexDiskImage }, + { "gz", kFilterIndexDiskImage }, // assume disk image inside + { "zip", kFilterIndexDiskImage }, // assume disk image inside +}; + +const char* MainWindow::kModeNuFX = _T("nufx"); +const char* MainWindow::kModeBinaryII = _T("bin2"); +const char* MainWindow::kModeACU = _T("acu"); +const char* MainWindow::kModeDiskImage = _T("disk"); + + +/* + * =========================================================================== + * MainWindow + * =========================================================================== + */ + +static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING); + +BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) + ON_WM_CREATE() + ON_MESSAGE(WMU_LATE_INIT, OnLateInit) + //ON_MESSAGE(WMU_CLOSE_MAIN_DIALOG, OnCloseMainDialog) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_PAINT() + //ON_WM_MOUSEWHEEL() + ON_WM_SETFOCUS() + ON_WM_HELPINFO() + ON_WM_QUERYENDSESSION() + ON_WM_ENDSESSION() + ON_REGISTERED_MESSAGE(gFindReplaceID, OnFindDialogMessage) + ON_COMMAND( IDM_FILE_NEW_ARCHIVE, OnFileNewArchive) + ON_COMMAND( IDM_FILE_OPEN, OnFileOpen) + ON_COMMAND( IDM_FILE_OPEN_VOLUME, OnFileOpenVolume) + ON_UPDATE_COMMAND_UI(IDM_FILE_OPEN_VOLUME, OnUpdateFileOpenVolume) + ON_COMMAND( IDM_FILE_REOPEN, OnFileReopen) + ON_UPDATE_COMMAND_UI(IDM_FILE_REOPEN, OnUpdateFileReopen) + ON_COMMAND( IDM_FILE_SAVE, OnFileSave) + ON_UPDATE_COMMAND_UI(IDM_FILE_SAVE, OnUpdateFileSave) + ON_COMMAND( IDM_FILE_CLOSE, OnFileClose) + ON_UPDATE_COMMAND_UI(IDM_FILE_CLOSE, OnUpdateFileClose) + ON_COMMAND( IDM_FILE_ARCHIVEINFO, OnFileArchiveInfo) + ON_UPDATE_COMMAND_UI(IDM_FILE_ARCHIVEINFO, OnUpdateFileArchiveInfo) + ON_COMMAND( IDM_FILE_PRINT, OnFilePrint) + ON_UPDATE_COMMAND_UI(IDM_FILE_PRINT, OnUpdateFilePrint) + ON_COMMAND( IDM_FILE_EXIT, OnFileExit) + ON_COMMAND( IDM_EDIT_COPY, OnEditCopy) + ON_UPDATE_COMMAND_UI(IDM_EDIT_COPY, OnUpdateEditCopy) + ON_COMMAND( IDM_EDIT_PASTE, OnEditPaste) + ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE, OnUpdateEditPaste) + ON_COMMAND( IDM_EDIT_PASTE_SPECIAL, OnEditPasteSpecial) + ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE_SPECIAL, OnUpdateEditPasteSpecial) + ON_COMMAND( IDM_EDIT_FIND, OnEditFind) + ON_UPDATE_COMMAND_UI(IDM_EDIT_FIND, OnUpdateEditFind) + ON_COMMAND( IDM_EDIT_SELECT_ALL, OnEditSelectAll) + ON_UPDATE_COMMAND_UI(IDM_EDIT_SELECT_ALL, OnUpdateEditSelectAll) + ON_COMMAND( IDM_EDIT_INVERT_SELECTION, OnEditInvertSelection) + ON_UPDATE_COMMAND_UI(IDM_EDIT_INVERT_SELECTION, OnUpdateEditInvertSelection) + ON_COMMAND( IDM_EDIT_PREFERENCES, OnEditPreferences) + ON_COMMAND_RANGE( IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnEditSort) + ON_UPDATE_COMMAND_UI_RANGE(IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnUpdateEditSort) + ON_COMMAND( IDM_ACTIONS_VIEW, OnActionsView) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_VIEW, OnUpdateActionsView) + ON_COMMAND( IDM_ACTIONS_ADD_FILES, OnActionsAddFiles) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_FILES, OnUpdateActionsAddFiles) + ON_COMMAND( IDM_ACTIONS_ADD_DISKS, OnActionsAddDisks) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_DISKS, OnUpdateActionsAddDisks) + ON_COMMAND( IDM_ACTIONS_CREATE_SUBDIR, OnActionsCreateSubdir) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CREATE_SUBDIR, OnUpdateActionsCreateSubdir) + ON_COMMAND( IDM_ACTIONS_EXTRACT, OnActionsExtract) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EXTRACT, OnUpdateActionsExtract) + ON_COMMAND( IDM_ACTIONS_TEST, OnActionsTest) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_TEST, OnUpdateActionsTest) + ON_COMMAND( IDM_ACTIONS_DELETE, OnActionsDelete) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_DELETE, OnUpdateActionsDelete) + ON_COMMAND( IDM_ACTIONS_RENAME, OnActionsRename) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME, OnUpdateActionsRename) + ON_COMMAND( IDM_ACTIONS_RECOMPRESS, OnActionsRecompress) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RECOMPRESS, OnUpdateActionsRecompress) + ON_COMMAND( IDM_ACTIONS_OPENASDISK, OnActionsOpenAsDisk) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_OPENASDISK, OnUpdateActionsOpenAsDisk) + ON_COMMAND( IDM_ACTIONS_EDIT_COMMENT, OnActionsEditComment) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_COMMENT, OnUpdateActionsEditComment) + ON_COMMAND( IDM_ACTIONS_EDIT_PROPS, OnActionsEditProps) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_PROPS, OnUpdateActionsEditProps) + ON_COMMAND( IDM_ACTIONS_RENAME_VOLUME, OnActionsRenameVolume) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME_VOLUME, OnUpdateActionsRenameVolume) + ON_COMMAND( IDM_ACTIONS_CONV_DISK, OnActionsConvDisk) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_DISK, OnUpdateActionsConvDisk) + ON_COMMAND( IDM_ACTIONS_CONV_FILE, OnActionsConvFile) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FILE, OnUpdateActionsConvFile) + ON_COMMAND( IDM_ACTIONS_CONV_TOWAV, OnActionsConvToWav) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_TOWAV, OnUpdateActionsConvToWav) + ON_COMMAND( IDM_ACTIONS_CONV_FROMWAV, OnActionsConvFromWav) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FROMWAV, OnUpdateActionsConvFromWav) + ON_COMMAND( IDM_ACTIONS_IMPORT_BAS, OnActionsImportBAS) + ON_UPDATE_COMMAND_UI(IDM_ACTIONS_IMPORT_BAS, OnUpdateActionsImportBAS) + ON_COMMAND( IDM_TOOLS_DISKEDIT, OnToolsDiskEdit) + ON_COMMAND( IDM_TOOLS_IMAGECREATOR, OnToolsDiskImageCreator) + ON_COMMAND( IDM_TOOLS_DISKCONV, OnToolsDiskConv) + ON_COMMAND( IDM_TOOLS_BULKDISKCONV, OnToolsBulkDiskConv) + ON_COMMAND( IDM_TOOLS_SST_MERGE, OnToolsSSTMerge) + ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_VOLUME, OnToolsVolumeCopierVolume) + ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_FILE, OnToolsVolumeCopierFile) + ON_COMMAND( IDM_TOOLS_EOLSCANNER, OnToolsEOLScanner) + ON_COMMAND( IDM_TOOLS_TWOIMGPROPS, OnToolsTwoImgProps) + ON_COMMAND( IDM_HELP_CONTENTS, OnHelpContents) + ON_COMMAND( IDM_HELP_WEBSITE, OnHelpWebSite) + ON_COMMAND( IDM_HELP_ORDERING, OnHelpOrdering) + ON_COMMAND( IDM_HELP_ABOUT, OnHelpAbout) +// ON_COMMAND( IDM_RTCLK_DEFAULT, OnRtClkDefault) + + /* this is required to allow "Help" button to work in PropertySheets (!) */ +// ON_COMMAND(ID_HELP, OnHelp) + ON_COMMAND(ID_HELP_FINDER, CFrameWnd::OnHelpFinder) + ON_COMMAND(ID_HELP, CFrameWnd::OnHelp) + ON_COMMAND(ID_CONTEXT_HELP, CFrameWnd::OnContextHelp) + ON_COMMAND(ID_DEFAULT_HELP, CFrameWnd::OnHelpFinder) +END_MESSAGE_MAP() + + +/* + * MainWindow constructor. Creates the main window and sets + * its properties. + */ +MainWindow::MainWindow() +{ + static const char* kAppName = _T("CiderPress"); + + fpContentList = nil; + fpOpenArchive = nil; + //fpSelSet = nil; + fpActionProgress = nil; + fpProgressCounter = nil; + fpFindDialog = nil; + + fFindDown = true; + fFindMatchCase = false; + fFindMatchWholeWord = false; + + fAbortPrinting = false; + fhDevMode = nil; + fhDevNames = nil; + fNeedReopen = false; + + CString wndClass = AfxRegisterWndClass( + CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, + gMyApp.LoadStandardCursor(IDC_ARROW), + NULL /*(HBRUSH) (COLOR_WINDOW + 1)*/, + gMyApp.LoadIcon(IDR_MAINFRAME) ); + + Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/, + rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME)); + + LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME)); + + // initialize some OLE garbage + AfxOleInit(); + + // required by MFC if Rich Edit controls are used + AfxInitRichEdit(); + + // required?? + //AfxEnableControlContainer(); + + SetCPTitle(); + + int cc = PostMessage(WMU_LATE_INIT, 0, 0); + ASSERT(cc != 0); +} + +/* + * MainWindow destructor. Close the archive if one is open, but don't try + * to shut down any controls in child windows. By this point, Windows has + * already snuffed them. + */ +MainWindow::~MainWindow() +{ + WMSG0("~MainWindow\n"); + + //WMSG0("MainWindow destructor\n"); + CloseArchiveWOControls(); + + int cc; + cc = ::WinHelp(m_hWnd, ::AfxGetApp()->m_pszHelpFilePath, HELP_QUIT, 0); + WMSG1("Turning off WinHelp returned %d\n", cc); + + // free stuff used by print dialog + ::GlobalFree(fhDevMode); + ::GlobalFree(fhDevNames); + + fPreferences.SaveToRegistry(); + WMSG0("MainWindow destructor complete\n"); +} + + +/* + * Override the pre-create function to tweak the window style. + */ +BOOL +MainWindow::PreCreateWindow(CREATESTRUCT& cs) +{ + BOOL res = CFrameWnd::PreCreateWindow(cs); + + cs.dwExStyle &= ~(WS_EX_CLIENTEDGE); + + return res; +} + +/* + * Override GetClientRect so we can factor in the status and tool bars. + */ +void +MainWindow::GetClientRect(LPRECT lpRect) const +{ + CRect sizeRect; + int toolBarHeight, statusBarHeight; + + fToolBar.GetWindowRect(&sizeRect); + toolBarHeight = sizeRect.bottom - sizeRect.top; + fStatusBar.GetWindowRect(&sizeRect); + statusBarHeight = sizeRect.bottom - sizeRect.top; + + //WMSG2("HEIGHTS = %d/%d\n", toolBarHeight, statusBarHeight); + CFrameWnd::GetClientRect(lpRect); + lpRect->top += toolBarHeight; + lpRect->bottom -= statusBarHeight; +} + + +/* + * Do some idle processing. + */ +void +MainWindow::DoIdle(void) +{ + /* + * Make sure that the filename field in the content list is always + * visible, since that what the user clicks on to select things. Would + * be nice to have a way to prevent it, but for now we'll just shove + * things back where they're supposed to be. + */ + if (fpContentList != nil) { + /* get the current column 0 width, with current user adjustments */ + fpContentList->ExportColumnWidths(); + int width = fPreferences.GetColumnLayout()->GetColumnWidth(0); + + if (width >= 0 && width < ColumnLayout::kMinCol0Width) { + /* column is too small, but don't change it until user lets mouse up */ + if (::GetAsyncKeyState(VK_LBUTTON) >= 0) { + WMSG0("Resetting column 0 width\n"); + fPreferences.GetColumnLayout()->SetColumnWidth(0, + ColumnLayout::kMinCol0Width); + fpContentList->NewColumnWidths(); + } + } + } + + /* + * Put an asterisk at the end of the title if we have an open archive + * and it has pending modifications. Remove it if nothing is pending. + */ + if (fpOpenArchive != nil) { + CString title; + int len; + + GetWindowText(/*ref*/ title); + len = title.GetLength(); + if (len > 0 && title.GetAt(len-1) == '*') { + if (!fpOpenArchive->IsModified()) { + /* remove the asterisk and the preceeding space */ + title.Delete(len-2, 2); + SetWindowText(title); + } + } else { + if (fpOpenArchive->IsModified()) { + /* add an asterisk */ + title += " *"; + SetWindowText(title); + } + } + } +} + + +/* + * Handle command-line arguments. + * + * Usage: + * CiderPress [[-temparc] [-mode {nufx,bin2,disk}] [-dispname name] filename] + */ +void +MainWindow::ProcessCommandLine(void) +{ + /* + * Get the command line and break it down into an argument vector. + */ + const char* cmdLine = ::GetCommandLine(); + if (cmdLine == nil || strlen(cmdLine) == 0) + return; + + char* mangle = strdup(cmdLine); + if (mangle == nil) + return; + + WMSG1("Mangling '%s'\n", mangle); + char* argv[8]; + int argc = 8; + VectorizeString(mangle, argv, &argc); + + WMSG0("Args:\n"); + for (int i = 0; i < argc; i++) { + WMSG2(" %d '%s'\n", i, argv[i]); + } + + /* + * Figure out what the arguments are. + */ + const char* filename = nil; + const char* dispName = nil; + int filterIndex = kFilterIndexGeneric; + bool temp = false; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + if (strcasecmp(argv[i], "-mode") == 0) { + if (i == argc-1) { + WMSG0("WARNING: -mode specified without mode\n"); + } else + i++; + if (strcasecmp(argv[i], kModeNuFX) == 0) + filterIndex = kFilterIndexNuFX; + else if (strcasecmp(argv[i], kModeBinaryII) == 0) + filterIndex = kFilterIndexBinaryII; + else if (strcasecmp(argv[i], kModeACU) == 0) + filterIndex = kFilterIndexACU; + else if (strcasecmp(argv[i], kModeDiskImage) == 0) + filterIndex = kFilterIndexDiskImage; + else { + WMSG1("WARNING: unrecognized mode '%s'\n", argv[i]); + } + } else if (strcasecmp(argv[i], "-dispname") == 0) { + if (i == argc-1) { + WMSG0("WARNING: -dispname specified without name\n"); + } else + i++; + dispName = argv[i]; + } else if (strcasecmp(argv[i], "-temparc") == 0) { + temp = true; + } else if (strcasecmp(argv[i], "-install") == 0) { + // see MyApp::InitInstance + WMSG0("Got '-install' flag, doing nothing\n"); + } else if (strcasecmp(argv[i], "-uninstall") == 0) { + // see MyApp::InitInstance + WMSG0("Got '-uninstall' flag, doing nothing\n"); + } else { + WMSG1("WARNING: unrecognized flag '%s'\n", argv[i]); + } + } else { + /* must be the filename */ + if (i != argc-1) { + WMSG1("WARNING: ignoring extra arguments (e.g. '%s')\n", + argv[i+1]); + } + filename = argv[i]; + break; + } + } + if (argc != 1 && filename == nil) { + WMSG0("WARNING: args specified but no filename found\n"); + } + + WMSG0("Argument handling:\n"); + WMSG3(" index=%d temp=%d filename='%s'\n", + filterIndex, temp, filename == nil ? "(nil)" : filename); + + if (filename != nil) { + PathName path(filename); + CString ext = path.GetExtension(); + + // drop the leading '.' from the extension + if (ext.Left(1) == ".") + ext.Delete(0, 1); + + /* load the archive, mandating read-only if it's a temporary file */ + if (LoadArchive(filename, ext, filterIndex, temp, false) == 0) { + /* success, update title bar */ + if (temp) + fOpenArchivePathName = path.GetFileName(); + else + fOpenArchivePathName = filename; + if (dispName != nil) + fOpenArchivePathName = dispName; + SetCPTitle(fOpenArchivePathName, fpOpenArchive); + } + + /* if it's a temporary file, arrange to have it deleted before exit */ + if (temp) { + int len = strlen(filename); + + if (len > 4 && strcasecmp(filename + (len-4), ".tmp") == 0) { + fDeleteList.Add(filename); + } else { + WMSG1("NOT adding '%s' to DeleteList -- does not end in '.tmp'\n", + filename); + } + } + } + + free(mangle); +} + + +/* + * =================================== + * Command handlers + * =================================== + */ + +const int kProgressPane = 1; + +/* + * OnCreate handler. Used to add a toolbar and status bar. + */ +int +MainWindow::OnCreate(LPCREATESTRUCT lpcs) +{ + WMSG0("Now in OnCreate!\n"); + if (CFrameWnd::OnCreate(lpcs) == -1) + return -1; + + /* + * Create the tool bar. + */ +#if 0 + static UINT buttonList[] = { + IDM_FILE_OPEN, + IDM_FILE_NEW_ARCHIVE, + // spacer + IDM_FILE_PRINT, + }; +#endif + fToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | + CBRS_TOOLTIPS | CBRS_FLYBY); + fToolBar.LoadToolBar(IDR_TOOLBAR1); + + /* + * Create the status bar. + */ + static UINT indicators[] = { ID_SEPARATOR, ID_INDICATOR_COMPLETE }; + fStatusBar.Create(this); + fStatusBar.SetIndicators(indicators, NELEM(indicators)); + //fStatusBar.SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0); + + fStatusBar.SetPaneText(kProgressPane, ""); + + return 0; +} + + +/* + * Catch a message sent to inspire us to perform one-time initializations of + * preferences and libraries. + * + * We're doing this the long way around because we want to be able to + * put up a dialog box if the version is bad. If we tried to handle this + * in the constructor we'd be acting before the window was fully created. + */ +LONG +MainWindow::OnLateInit(UINT, LONG) +{ + CString result; + CString appName; + CString niftyListFile; + + appName.LoadString(IDS_MB_APP_NAME); + + WMSG0("----- late init begins -----\n"); + + /* + * Handle all other messages. This gives the framework a chance to dim + * all of the toolbar buttons. This is especially useful when opening + * a file from the command line that doesn't exist, causing an error + * dialog and blocking main window messages. + */ + PeekAndPump(); + + /* + * Initialize libraries. This includes a version check. + */ + result = NufxArchive::AppInit(); + if (!result.IsEmpty()) + goto fail; + result = DiskArchive::AppInit(); + if (!result.IsEmpty()) + goto fail; + result = BnyArchive::AppInit(); + if (!result.IsEmpty()) + goto fail; + + niftyListFile = gMyApp.GetExeBaseName(); + niftyListFile += "NList.Data"; + if (!NiftyList::AppInit(niftyListFile)) { + CString file2 = niftyListFile + ".TXT"; + if (!NiftyList::AppInit(file2)) { + CString msg; + msg.Format(IDS_NLIST_DATA_FAILED, niftyListFile, file2); + MessageBox(msg, appName, MB_OK); + } + } + + /* + * Read preferences from registry. + */ + fPreferences.LoadFromRegistry(); + + /* + * Check to see if we're registered; if we're not, and we've expired, it's + * time to bail out. + */ + MyRegistry::RegStatus regStatus; + //regStatus = gMyApp.fRegistry.CheckRegistration(&result); + regStatus = MyRegistry::kRegValid; + WMSG1("CheckRegistration returned %d\n", regStatus); + switch (regStatus) { + case MyRegistry::kRegNotSet: + case MyRegistry::kRegValid: + ASSERT(result.IsEmpty()); + break; + case MyRegistry::kRegExpired: + case MyRegistry::kRegInvalid: + MessageBox(result, appName, MB_OK|MB_ICONINFORMATION); + WMSG0("FORCING REG\n"); +#if 0 + if (EnterRegDialog::GetRegInfo(this) != 0) { + result = ""; + goto fail; + } +#endif + SetCPTitle(); // update title bar with new reg info + break; + case MyRegistry::kRegFailed: + ASSERT(!result.IsEmpty()); + goto fail; + default: + ASSERT(false); + CString confused; + confused.Format("Registration check failed. %s", (LPCTSTR) result); + result = confused; + goto fail; + } + + /* + * Process command-line options, possibly loading an archive. + */ + ProcessCommandLine(); + + return 0; + +fail: + if (!result.IsEmpty()) + ShowFailureMsg(this, result, IDS_FAILED); + int cc = PostMessage(WM_CLOSE, 0, 0); + ASSERT(cc != 0); + + return 0; +} + + +/* + * The system wants to know if we're okay with shutting down. + * + * Return TRUE if it's okay to shut down, FALSE otherwise. + */ +BOOL +MainWindow::OnQueryEndSession(void) +{ + WMSG0("Got QueryEndSession\n"); + return TRUE; +} + +/* + * Notification of shutdown (or not). + */ +void +MainWindow::OnEndSession(BOOL bEnding) +{ + WMSG1("Got EndSession (bEnding=%d)\n", bEnding); + + if (bEnding) { + CloseArchiveWOControls(); + + fPreferences.SaveToRegistry(); + } +} + +/* + * The main window is resizing. We don't automatically redraw on resize, + * so we will need to update the client region. If it's filled with a + * control, the control's resize & redraw function will take care of it. + * If not, we need to explicitly invalidate the client region so the + * window will repaint itself. + */ +void +MainWindow::OnSize(UINT nType, int cx, int cy) +{ + CFrameWnd::OnSize(nType, cx, cy); + ResizeClientArea(); +} +void +MainWindow::ResizeClientArea(void) +{ + CRect sizeRect; + + GetClientRect(&sizeRect); + if (fpContentList != NULL) + fpContentList->MoveWindow(sizeRect); + else + Invalidate(false); +} + +/* + * Restrict the minimum window size to something reasonable. + */ +void +MainWindow::OnGetMinMaxInfo(MINMAXINFO* pMMI) +{ + pMMI->ptMinTrackSize.x = 256; + pMMI->ptMinTrackSize.y = 192; +} + +/* + * Repaint the main window. + */ +void +MainWindow::OnPaint(void) +{ + CPaintDC dc(this); + CRect clientRect; + + GetClientRect(&clientRect); + + /* + * If there's no control in the window, fill in the client area with + * what looks like an empty MDI client rect. + */ + if (fpContentList == nil) { + DrawEmptyClientArea(&dc, clientRect); + } + +#if 0 + CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // red pen, 1 pixel wide + CPen* pOldPen = dc.SelectObject(&pen); + + dc.MoveTo(clientRect.left, clientRect.top); + dc.LineTo(clientRect.right-1, clientRect.top); + dc.LineTo(clientRect.right, clientRect.bottom); + dc.LineTo(clientRect.left, clientRect.bottom-1); + dc.LineTo(clientRect.left, clientRect.top); + + dc.SelectObject(pOldPen); +#endif +} + +#if 0 +afx_msg BOOL +MainWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +{ + WMSG0("MOUSE WHEEL\n"); + return FALSE; + + WPARAM wparam; + LPARAM lparam; + + wparam = nFlags | (zDelta << 16); + lparam = pt.x | (pt.y << 16); + if (fpContentList != nil) + fpContentList->SendMessage(WM_MOUSEWHEEL, wparam, lparam); + return CWnd::OnMouseWheel(nFlags, zDelta, pt); +// return TRUE; +} +#endif + +/* + * Make sure open controls keep the input focus. + */ +void +MainWindow::OnSetFocus(CWnd* /*pOldWnd*/) +{ + if (fpContentList != nil) { + WMSG0("Returning focus to ContentList\n"); + fpContentList->SetFocus(); + } +} + +/* + * User hit F1. We don't currently have context-sensitive help on the main page. + */ +BOOL +MainWindow::OnHelpInfo(HELPINFO* /*lpHelpInfo*/) +{ + //WinHelp(0, HELP_FINDER); + WinHelp(HELP_TOPIC_WELCOME, HELP_CONTEXT); + return TRUE; // dunno what this means +} + +#if 0 +/* + * Catch-all Help handler, necessary to allow CPropertySheet to display a + * "Help" button. (WTF?) + */ +LONG +MainWindow::OnHelp(UINT wParam, LONG lParam) +{ + HELPINFO* lpHelpInfo = (HELPINFO*) lParam; + + DWORD context = lpHelpInfo->iCtrlId; + WMSG1("MainWindow OnHelp (context=%d)\n", context); + WinHelp(context, HELP_CONTEXTPOPUP); + + return TRUE; // yes, we handled it +} +#endif + +/* + * Handle Edit->Preferences by popping up a property sheet. + */ +void +MainWindow::OnEditPreferences(void) +{ + PrefsSheet ps; + ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); + + /* pull any user header tweaks out of list so we can configure prefs */ + if (fpContentList != nil) + fpContentList->ExportColumnWidths(); + + /* set up PrefsGeneralPage */ + for (int i = 0; i < kNumVisibleColumns; i++) { + ps.fGeneralPage.fColumn[i] = (pColLayout->GetColumnWidth(i) != 0); + } + ps.fGeneralPage.fMimicShrinkIt = fPreferences.GetPrefBool(kPrMimicShrinkIt); + ps.fGeneralPage.fBadMacSHK = fPreferences.GetPrefBool(kPrBadMacSHK); + ps.fGeneralPage.fReduceSHKErrorChecks = fPreferences.GetPrefBool(kPrReduceSHKErrorChecks); + ps.fGeneralPage.fCoerceDOSFilenames = fPreferences.GetPrefBool(kPrCoerceDOSFilenames); + ps.fGeneralPage.fSpacesToUnder = fPreferences.GetPrefBool(kPrSpacesToUnder); + ps.fGeneralPage.fPasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths); + ps.fGeneralPage.fBeepOnSuccess = fPreferences.GetPrefBool(kPrBeepOnSuccess); + + /* set up PrefsDiskImagePage */ + ps.fDiskImagePage.fQueryImageFormat = fPreferences.GetPrefBool(kPrQueryImageFormat); + ps.fDiskImagePage.fOpenVolumeRO = fPreferences.GetPrefBool(kPrOpenVolumeRO); + ps.fDiskImagePage.fOpenVolumePhys0 = fPreferences.GetPrefBool(kPrOpenVolumePhys0); + ps.fDiskImagePage.fProDOSAllowLower = fPreferences.GetPrefBool(kPrProDOSAllowLower); + ps.fDiskImagePage.fProDOSUseSparse = fPreferences.GetPrefBool(kPrProDOSUseSparse); + + /* set up PrefsCompressionPage */ + ps.fCompressionPage.fCompressType = fPreferences.GetPrefLong(kPrCompressionType); + + /* set up PrefsFviewPage */ + ps.fFviewPage.fMaxViewFileSizeKB = + (fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024; + ps.fFviewPage.fNoWrapText = fPreferences.GetPrefBool(kPrNoWrapText); + + ps.fFviewPage.fHighlightHexDump = fPreferences.GetPrefBool(kPrHighlightHexDump); + ps.fFviewPage.fHighlightBASIC = fPreferences.GetPrefBool(kPrHighlightBASIC); + ps.fFviewPage.fConvDisasmOneByteBrkCop = fPreferences.GetPrefBool(kPrDisasmOneByteBrkCop); + ps.fFviewPage.fConvHiResBlackWhite = fPreferences.GetPrefBool(kPrConvHiResBlackWhite); + ps.fFviewPage.fConvDHRAlgorithm = fPreferences.GetPrefLong(kPrConvDHRAlgorithm); + ps.fFviewPage.fRelaxGfxTypeCheck = fPreferences.GetPrefBool(kPrRelaxGfxTypeCheck); +// ps.fFviewPage.fEOLConvRaw = fPreferences.GetPrefBool(kPrEOLConvRaw); +// ps.fFviewPage.fConvHighASCII = fPreferences.GetPrefBool(kPrConvHighASCII); + ps.fFviewPage.fConvTextEOL_HA = fPreferences.GetPrefBool(kPrConvTextEOL_HA); + ps.fFviewPage.fConvCPMText = fPreferences.GetPrefBool(kPrConvCPMText); + ps.fFviewPage.fConvPascalText = fPreferences.GetPrefBool(kPrConvPascalText); + ps.fFviewPage.fConvPascalCode = fPreferences.GetPrefBool(kPrConvPascalCode); + ps.fFviewPage.fConvApplesoft = fPreferences.GetPrefBool(kPrConvApplesoft); + ps.fFviewPage.fConvInteger = fPreferences.GetPrefBool(kPrConvInteger); + ps.fFviewPage.fConvBusiness = fPreferences.GetPrefBool(kPrConvBusiness); + ps.fFviewPage.fConvGWP = fPreferences.GetPrefBool(kPrConvGWP); + ps.fFviewPage.fConvText8 = fPreferences.GetPrefBool(kPrConvText8); + ps.fFviewPage.fConvAWP = fPreferences.GetPrefBool(kPrConvAWP); + ps.fFviewPage.fConvADB = fPreferences.GetPrefBool(kPrConvADB); + ps.fFviewPage.fConvASP = fPreferences.GetPrefBool(kPrConvASP); + ps.fFviewPage.fConvSCAssem = fPreferences.GetPrefBool(kPrConvSCAssem); + ps.fFviewPage.fConvDisasm = fPreferences.GetPrefBool(kPrConvDisasm); + ps.fFviewPage.fConvHiRes = fPreferences.GetPrefBool(kPrConvHiRes); + ps.fFviewPage.fConvDHR = fPreferences.GetPrefBool(kPrConvDHR); + ps.fFviewPage.fConvSHR = fPreferences.GetPrefBool(kPrConvSHR); + ps.fFviewPage.fConvPrintShop = fPreferences.GetPrefBool(kPrConvPrintShop); + ps.fFviewPage.fConvMacPaint = fPreferences.GetPrefBool(kPrConvMacPaint); + ps.fFviewPage.fConvProDOSFolder = fPreferences.GetPrefBool(kPrConvProDOSFolder); + ps.fFviewPage.fConvResources = fPreferences.GetPrefBool(kPrConvResources); + + /* set up PrefsFilesPage */ + ps.fFilesPage.fTempPath = fPreferences.GetPrefString(kPrTempPath); + ps.fFilesPage.fExtViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); + + if (ps.DoModal() == IDOK) + ApplyNow(&ps); +} + +/* + * Apply a change from the preferences sheet. + */ +void +MainWindow::ApplyNow(PrefsSheet* pPS) +{ + bool mustReload = false; + + //WMSG0("APPLY CHANGES\n"); + + ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); + + if (pPS->fGeneralPage.fDefaultsPushed) { + /* reset all sizes to defaults, then factor in checkboxes */ + WMSG0(" Resetting all widths to defaults\n"); + + /* copy defaults over */ + for (int i = 0; i < kNumVisibleColumns; i++) + pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); + } + + /* handle column checkboxes */ + for (int i = 0; i < kNumVisibleColumns; i++) { + if (pColLayout->GetColumnWidth(i) == 0 && + pPS->fGeneralPage.fColumn[i]) + { + /* restore column */ + WMSG1(" Column %d restored\n", i); + pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); + } else if (pColLayout->GetColumnWidth(i) != 0 && + !pPS->fGeneralPage.fColumn[i]) + { + /* disable column */ + WMSG1(" Column %d hidden\n", i); + pColLayout->SetColumnWidth(i, 0); + } + } + if (fpContentList != nil) + fpContentList->NewColumnWidths(); + fPreferences.SetPrefBool(kPrMimicShrinkIt, + pPS->fGeneralPage.fMimicShrinkIt != 0); + fPreferences.SetPrefBool(kPrBadMacSHK, pPS->fGeneralPage.fBadMacSHK != 0); + fPreferences.SetPrefBool(kPrReduceSHKErrorChecks, + pPS->fGeneralPage.fReduceSHKErrorChecks != 0); + if (fPreferences.GetPrefBool(kPrCoerceDOSFilenames)!= + (pPS->fGeneralPage.fCoerceDOSFilenames != 0)) + { + WMSG1("DOS filename coercion pref now %d\n", + pPS->fGeneralPage.fCoerceDOSFilenames); + fPreferences.SetPrefBool(kPrCoerceDOSFilenames, + pPS->fGeneralPage.fCoerceDOSFilenames != 0); + mustReload = true; + } + if (fPreferences.GetPrefBool(kPrSpacesToUnder) != + (pPS->fGeneralPage.fSpacesToUnder != 0)) + { + WMSG1("Spaces-to-underscores now %d\n", pPS->fGeneralPage.fSpacesToUnder); + fPreferences.SetPrefBool(kPrSpacesToUnder, pPS->fGeneralPage.fSpacesToUnder != 0); + mustReload = true; + } + fPreferences.SetPrefBool(kPrPasteJunkPaths, pPS->fGeneralPage.fPasteJunkPaths != 0); + fPreferences.SetPrefBool(kPrBeepOnSuccess, pPS->fGeneralPage.fBeepOnSuccess != 0); + + if (pPS->fGeneralPage.fOurAssociations != nil) { + WMSG0("NEW ASSOCIATIONS!\n"); + + for (int assoc = 0; assoc < gMyApp.fRegistry.GetNumFileAssocs(); assoc++) + { + gMyApp.fRegistry.SetFileAssoc(assoc, + pPS->fGeneralPage.fOurAssociations[assoc]); + } + + /* delete them so, if they hit "apply" again, we only update once */ + delete[] pPS->fGeneralPage.fOurAssociations; + pPS->fGeneralPage.fOurAssociations = nil; + } + + fPreferences.SetPrefBool(kPrQueryImageFormat, pPS->fDiskImagePage.fQueryImageFormat != 0); + fPreferences.SetPrefBool(kPrOpenVolumeRO, pPS->fDiskImagePage.fOpenVolumeRO != 0); + fPreferences.SetPrefBool(kPrOpenVolumePhys0, pPS->fDiskImagePage.fOpenVolumePhys0 != 0); + fPreferences.SetPrefBool(kPrProDOSAllowLower, pPS->fDiskImagePage.fProDOSAllowLower != 0); + fPreferences.SetPrefBool(kPrProDOSUseSparse, pPS->fDiskImagePage.fProDOSUseSparse != 0); + + fPreferences.SetPrefLong(kPrCompressionType, pPS->fCompressionPage.fCompressType); + + fPreferences.SetPrefLong(kPrMaxViewFileSize, pPS->fFviewPage.fMaxViewFileSizeKB * 1024); + fPreferences.SetPrefBool(kPrNoWrapText, pPS->fFviewPage.fNoWrapText != 0); + + fPreferences.SetPrefBool(kPrHighlightHexDump, pPS->fFviewPage.fHighlightHexDump != 0); + fPreferences.SetPrefBool(kPrHighlightBASIC, pPS->fFviewPage.fHighlightBASIC != 0); + fPreferences.SetPrefBool(kPrDisasmOneByteBrkCop, pPS->fFviewPage.fConvDisasmOneByteBrkCop != 0); + fPreferences.SetPrefBool(kPrConvHiResBlackWhite, pPS->fFviewPage.fConvHiResBlackWhite != 0); + fPreferences.SetPrefLong(kPrConvDHRAlgorithm, pPS->fFviewPage.fConvDHRAlgorithm); + fPreferences.SetPrefBool(kPrRelaxGfxTypeCheck, pPS->fFviewPage.fRelaxGfxTypeCheck != 0); +// fPreferences.SetPrefBool(kPrEOLConvRaw, pPS->fFviewPage.fEOLConvRaw != 0); +// fPreferences.SetPrefBool(kPrConvHighASCII, pPS->fFviewPage.fConvHighASCII != 0); + fPreferences.SetPrefBool(kPrConvTextEOL_HA, pPS->fFviewPage.fConvTextEOL_HA != 0); + fPreferences.SetPrefBool(kPrConvCPMText, pPS->fFviewPage.fConvCPMText != 0); + fPreferences.SetPrefBool(kPrConvPascalText, pPS->fFviewPage.fConvPascalText != 0); + fPreferences.SetPrefBool(kPrConvPascalCode, pPS->fFviewPage.fConvPascalCode != 0); + fPreferences.SetPrefBool(kPrConvApplesoft, pPS->fFviewPage.fConvApplesoft != 0); + fPreferences.SetPrefBool(kPrConvInteger, pPS->fFviewPage.fConvInteger != 0); + fPreferences.SetPrefBool(kPrConvBusiness, pPS->fFviewPage.fConvBusiness != 0); + fPreferences.SetPrefBool(kPrConvGWP, pPS->fFviewPage.fConvGWP != 0); + fPreferences.SetPrefBool(kPrConvText8, pPS->fFviewPage.fConvText8 != 0); + fPreferences.SetPrefBool(kPrConvAWP, pPS->fFviewPage.fConvAWP != 0); + fPreferences.SetPrefBool(kPrConvADB, pPS->fFviewPage.fConvADB != 0); + fPreferences.SetPrefBool(kPrConvASP, pPS->fFviewPage.fConvASP != 0); + fPreferences.SetPrefBool(kPrConvSCAssem, pPS->fFviewPage.fConvSCAssem != 0); + fPreferences.SetPrefBool(kPrConvDisasm, pPS->fFviewPage.fConvDisasm != 0); + fPreferences.SetPrefBool(kPrConvHiRes, pPS->fFviewPage.fConvHiRes != 0); + fPreferences.SetPrefBool(kPrConvDHR, pPS->fFviewPage.fConvDHR != 0); + fPreferences.SetPrefBool(kPrConvSHR, pPS->fFviewPage.fConvSHR != 0); + fPreferences.SetPrefBool(kPrConvPrintShop, pPS->fFviewPage.fConvPrintShop != 0); + fPreferences.SetPrefBool(kPrConvMacPaint, pPS->fFviewPage.fConvMacPaint != 0); + fPreferences.SetPrefBool(kPrConvProDOSFolder, pPS->fFviewPage.fConvProDOSFolder != 0); + fPreferences.SetPrefBool(kPrConvResources, pPS->fFviewPage.fConvResources != 0); + + fPreferences.SetPrefString(kPrTempPath, pPS->fFilesPage.fTempPath); + WMSG1("--- Temp path now '%s'\n", fPreferences.GetPrefString(kPrTempPath)); + fPreferences.SetPrefString(kPrExtViewerExts, pPS->fFilesPage.fExtViewerExts); + + +// if ((pPS->fGeneralPage.fShowToolbarText != 0) != fPreferences.GetShowToolbarText()) { +// fPreferences.SetShowToolbarText(pPS->fGeneralPage.fShowToolbarText != 0); +// //SetToolbarTextMode(); +// ResizeClientArea(); +// } + + /* allow open archive to track changes to preferences */ + if (fpOpenArchive != nil) + fpOpenArchive->PreferencesChanged(); + + if (mustReload) { + WMSG0("Preferences apply requesting GA/CL reload\n"); + if (fpOpenArchive != nil) + fpOpenArchive->Reload(); + if (fpContentList != nil) + fpContentList->Reload(); + } + + /* export to registry */ + fPreferences.SaveToRegistry(); + + //Invalidate(); +} + +/* + * Handle IDM_EDIT_FIND. + */ +void +MainWindow::OnEditFind(void) +{ + DWORD flags = 0; + + if (fpFindDialog != nil) + return; + + if (fFindDown) + flags |= FR_DOWN; + if (fFindMatchCase) + flags |= FR_MATCHCASE; + if (fFindMatchWholeWord) + flags |= FR_WHOLEWORD; + + fpFindDialog = new CFindReplaceDialog; + + fpFindDialog->Create(TRUE, // "find" only + fFindLastStr, // default string to search for + NULL, // default string to replace + flags, // flags + this); // parent +} +void +MainWindow::OnUpdateEditFind(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpOpenArchive != nil); +} + +/* + * Handle activity in the modeless "find" dialog. + */ +LRESULT +MainWindow::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) +{ + assert(fpFindDialog != nil); + + fFindDown = (fpFindDialog->SearchDown() != 0); + fFindMatchCase = (fpFindDialog->MatchCase() != 0); + fFindMatchWholeWord = (fpFindDialog->MatchWholeWord() != 0); + + if (fpFindDialog->IsTerminating()) { + fpFindDialog = nil; + return 0; + } + + if (fpFindDialog->FindNext()) { + fFindLastStr = fpFindDialog->GetFindString(); + fpContentList->FindNext(fFindLastStr, fFindDown, fFindMatchCase, + fFindMatchWholeWord); + } else { + WMSG0("Unexpected find dialog activity\n"); + } + + return 0; +} + + +/* + * Handle IDM_SORT_*. + * + * The "sort" enu item should really only be active if we have a file open. + */ +void +MainWindow::OnEditSort(UINT id) +{ + WMSG1("EDIT SORT %d\n", id); + + ASSERT(id >= IDM_SORT_PATHNAME && id <= IDM_SORT_ORIGINAL); + fPreferences.GetColumnLayout()->SetSortColumn(id - IDM_SORT_PATHNAME); + fPreferences.GetColumnLayout()->SetAscending(true); + if (fpContentList != nil) + fpContentList->NewSortOrder(); +} +void +MainWindow::OnUpdateEditSort(CCmdUI* pCmdUI) +{ + unsigned int column = fPreferences.GetColumnLayout()->GetSortColumn(); + + pCmdUI->SetCheck(pCmdUI->m_nID - IDM_SORT_PATHNAME == column); +} + +/* + * Open the help file. + */ +void +MainWindow::OnHelpContents(void) +{ + WinHelp(0, HELP_FINDER); +} + +/* + * Go to the faddenSoft web site. + */ +void +MainWindow::OnHelpWebSite(void) +{ + int err; + + err = (int) ::ShellExecute(m_hWnd, _T("open"), kWebSiteURL, NULL, NULL, + SW_SHOWNORMAL); + if (err <= 32) { + CString msg; + if (err == ERROR_FILE_NOT_FOUND) { + msg = "Windows call failed: web browser not found. (Sometimes" + " it mistakenly reports this when IE is not the default" + " browser.)"; + ShowFailureMsg(this, msg, IDS_FAILED); + } else { + msg.Format("Unable to launch web browser (err=%d).", err); + ShowFailureMsg(this, msg, IDS_FAILED); + } + } +} + +/* + * Show ordering info (ka-ching!). + */ +void +MainWindow::OnHelpOrdering(void) +{ + WinHelp(HELP_TOPIC_ORDERING_INFO, HELP_CONTEXT); +} + +/* + * Pop up the About box. + */ +void +MainWindow::OnHelpAbout(void) +{ + int result; + + AboutDialog dlg(this); + + result = dlg.DoModal(); + WMSG1("HelpAbout returned %d\n", result); + + /* + * User could've changed registration. If we're showing the registered + * user name in the title bar, update it. + */ + if (fpOpenArchive == nil) + SetCPTitle(); +} + +/* + * Create a new SHK archive, using a "save as" dialog to select the name. + */ +void +MainWindow::OnFileNewArchive(void) +{ + CString filename, saveFolder, errStr; + GenericArchive* pOpenArchive; + CString errMsg; + + CFileDialog dlg(FALSE, _T("shk"), NULL, + OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, + "ShrinkIt Archives (*.shk)|*.shk||", this); + + dlg.m_ofn.lpstrTitle = "New Archive"; + dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); + + if (dlg.DoModal() != IDOK) + goto bail; + + saveFolder = dlg.m_ofn.lpstrFile; + saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); + fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); + + filename = dlg.GetPathName(); + WMSG1("NEW FILE '%s'\n", filename); + + /* remove file if it already exists */ + errMsg = RemoveFile(filename); + if (!errMsg.IsEmpty()) { + ShowFailureMsg(this, errMsg, IDS_FAILED); + goto bail; + } + + pOpenArchive = new NufxArchive; + errStr = pOpenArchive->New(filename, nil); + if (!errStr.IsEmpty()) { + CString failed; + failed.LoadString(IDS_FAILED); + MessageBox(errStr, failed, MB_ICONERROR); + + delete pOpenArchive; + } else { + SwitchContentList(pOpenArchive); + fOpenArchivePathName = dlg.GetPathName(); + SetCPTitle(fOpenArchivePathName, fpOpenArchive); + } + +bail: + WMSG0("--- OnFileNewArchive done\n"); +} + + +/* + * Handle request to open an archive or disk image. + */ +void +MainWindow::OnFileOpen(void) +{ + CString openFilters; + CString saveFolder; + + /* set up filters; the order is significant */ + openFilters = kOpenNuFX; + openFilters += kOpenBinaryII; + openFilters += kOpenACU; + openFilters += kOpenDiskImage; + openFilters += kOpenAll; + openFilters += kOpenEnd; + CFileDialog dlg(TRUE, "shk", NULL, + OFN_FILEMUSTEXIST, openFilters, this); + + dlg.m_ofn.nFilterIndex = fPreferences.GetPrefLong(kPrLastOpenFilterIndex); + dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); + + if (dlg.DoModal() != IDOK) + goto bail; + + fPreferences.SetPrefLong(kPrLastOpenFilterIndex, dlg.m_ofn.nFilterIndex); + saveFolder = dlg.m_ofn.lpstrFile; + saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); + fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); + + DoOpenArchive(dlg.GetPathName(), dlg.GetFileExt(), + dlg.m_ofn.nFilterIndex, dlg.GetReadOnlyPref() != 0); + +bail: + WMSG0("--- OnFileOpen done\n"); +} + +/* + * Handle request to open a raw disk volume. + */ +void +MainWindow::OnFileOpenVolume(void) +{ + WMSG0("--- OnFileOpenVolume\n"); + + int result; + + OpenVolumeDialog dlg(this); + + result = dlg.DoModal(); + if (result != IDOK) + goto bail; + + //DiskImg::SetAllowWritePhys0(fPreferences.GetPrefBool(kPrOpenVolumePhys0)); + DoOpenVolume(dlg.fChosenDrive, dlg.fReadOnly != 0); + +bail: + return; +} +void +MainWindow::OnUpdateFileOpenVolume(CCmdUI* pCmdUI) +{ + // don't really need this function + pCmdUI->Enable(TRUE); +} + +/* + * Open an archive. + */ +void +MainWindow::DoOpenArchive(const char* pathName, const char* ext, + int filterIndex, bool readOnly) +{ + if (LoadArchive(pathName, ext, filterIndex, readOnly, false) == 0) { + /* success, update title bar */ + fOpenArchivePathName = pathName; + SetCPTitle(fOpenArchivePathName, fpOpenArchive); + } else { + /* some failures will close an open archive */ + //if (fpOpenArchive == nil) + // SetCPTitle(); + } +} + +/* + * Save any pending changes. + * + * This may be called directly from tools, so don't assume that the + * conditions checked for in OnUpdateFileSave hold here. + */ +void +MainWindow::OnFileReopen(void) +{ + ReopenArchive(); +} +void +MainWindow::OnUpdateFileReopen(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpOpenArchive != nil); +} + + +/* + * Save any pending changes. + * + * This may be called directly from tools, so don't assume that the + * conditions checked for in OnUpdateFileSave hold here. + */ +void +MainWindow::OnFileSave(void) +{ + CString errMsg; + + if (fpOpenArchive == nil) + return; + + { + CWaitCursor waitc; + errMsg = fpOpenArchive->Flush(); + } + if (!errMsg.IsEmpty()) + ShowFailureMsg(this, errMsg, IDS_FAILED); + + // update the title bar + DoIdle(); +} +void +MainWindow::OnUpdateFileSave(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpOpenArchive != nil && fpOpenArchive->IsModified()); +} + +/* + * Close current archive or disk image. + */ +void +MainWindow::OnFileClose(void) +{ + CloseArchive(); + //SetCPTitle(); + WMSG0("--- OnFileClose done\n"); +} +void +MainWindow::OnUpdateFileClose(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpOpenArchive != nil); +} + + +/* + * Show detailed information on the current archive. + */ +void +MainWindow::OnFileArchiveInfo(void) +{ + ArchiveInfoDialog* pDlg = nil; + ASSERT(fpOpenArchive != nil); + + switch (fpOpenArchive->GetArchiveKind()) { + case GenericArchive::kArchiveNuFX: + pDlg = new NufxArchiveInfoDialog((NufxArchive*) fpOpenArchive, this); + break; + case GenericArchive::kArchiveDiskImage: + pDlg = new DiskArchiveInfoDialog((DiskArchive*) fpOpenArchive, this); + break; + case GenericArchive::kArchiveBNY: + pDlg = new BnyArchiveInfoDialog((BnyArchive*) fpOpenArchive, this); + break; + case GenericArchive::kArchiveACU: + pDlg = new AcuArchiveInfoDialog((AcuArchive*) fpOpenArchive, this); + break; + default: + WMSG1("Unexpected archive type %d\n", fpOpenArchive->GetArchiveKind()); + ASSERT(false); + return; + }; + + pDlg->DoModal(); + + delete pDlg; +} +void +MainWindow::OnUpdateFileArchiveInfo(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpContentList != nil); +} + +/* + * Print the contents of the current archive. + */ +void +MainWindow::OnFilePrint(void) +{ + PrintListing(fpContentList); +} +void +MainWindow::OnUpdateFilePrint(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpContentList != nil && fpContentList->GetItemCount() > 0); +} + +/* + * Print a ContentList. + */ +void +MainWindow::PrintListing(const ContentList* pContentList) +{ + CPrintDialog dlg(FALSE); // use CPrintDialogEx for Win2K? CPageSetUpDialog? + PrintContentList pcl; + CDC dc; + int itemCount, numPages; + + itemCount = pContentList->GetItemCount(); + numPages = (itemCount + (pcl.GetLinesPerPage()-1)) / pcl.GetLinesPerPage(); + + dlg.m_pd.nFromPage = dlg.m_pd.nMinPage = 1; + dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = numPages; + + dlg.m_pd.hDevMode = fhDevMode; + dlg.m_pd.hDevNames = fhDevNames; + dlg.m_pd.Flags |= PD_USEDEVMODECOPIESANDCOLLATE; + dlg.m_pd.Flags &= ~(PD_NOPAGENUMS); + if (dlg.DoModal() != IDOK) + return; + if (dc.Attach(dlg.GetPrinterDC()) != TRUE) { + CString msg; + msg.LoadString(IDS_PRINTER_NOT_USABLE); + ShowFailureMsg(this, msg, IDS_FAILED); + return; + } + + pcl.Setup(&dc, this); + if (dlg.m_pd.Flags & PD_PAGENUMS) + pcl.Print(pContentList, dlg.m_pd.nFromPage, dlg.m_pd.nToPage); + else + pcl.Print(pContentList); + + fhDevMode = dlg.m_pd.hDevMode; + fhDevNames = dlg.m_pd.hDevNames; +} + + +/* + * Handle Exit item by sending a close request. + */ +void +MainWindow::OnFileExit(void) +{ + SendMessage(WM_CLOSE, 0, 0); +} + + +/* + * Select everything in the content list. + */ +void +MainWindow::OnEditSelectAll(void) +{ + ASSERT(fpContentList != nil); + fpContentList->SelectAll(); +} +void +MainWindow::OnUpdateEditSelectAll(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpContentList != nil); +} + +/* + * Invert the content list selection. + */ +void +MainWindow::OnEditInvertSelection(void) +{ + ASSERT(fpContentList != nil); + fpContentList->InvertSelection(); +} +void +MainWindow::OnUpdateEditInvertSelection(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(fpContentList != nil); +} + + +/* + * Get the one selected item from the current display. Primarily useful + * for the double-click handler, but also used for "action" menu items + * that insist on operating on a single menu item (edit prefs, create subdir). + * + * Returns nil if the item couldn't be found or if more than one item was + * selected. + */ +GenericEntry* +MainWindow::GetSelectedItem(ContentList* pContentList) +{ + if (pContentList->GetSelectedCount() != 1) + return nil; + + POSITION posn; + posn = pContentList->GetFirstSelectedItemPosition(); + if (posn == nil) { + ASSERT(false); + return nil; + } + int num = pContentList->GetNextSelectedItem(/*ref*/ posn); + GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(num); + if (pEntry == nil) { + WMSG1(" Glitch: couldn't find entry %d\n", num); + ASSERT(false); + } + + return pEntry; +} + +/* + * Handle a double-click. + * + * Individual items get special treatment, multiple items just get handed off + * to the file viewer. + */ +void +MainWindow::HandleDoubleClick(void) +{ + bool handled = false; + + ASSERT(fpContentList != nil); + if (fpContentList->GetSelectedCount() == 0) { + /* nothing selected, they double-clicked outside first column */ + WMSG0("Double-click but nothing selected\n"); + return; + } + if (fpContentList->GetSelectedCount() != 1) { + /* multiple items, just bring up viewer */ + HandleView(); + return; + } + + /* + * Find the GenericEntry that corresponds to this item. + */ + GenericEntry* pEntry = GetSelectedItem(fpContentList); + if (pEntry == nil) + return; + + WMSG1(" Double-click GOT '%s'\n", pEntry->GetPathName()); + const char* ext; + long fileType, auxType; + + ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); + fileType = pEntry->GetFileType(); + auxType = pEntry->GetAuxType(); + + /* // unit tests for MatchSemicolonList + MatchSemicolonList("gif; jpeg; jpg", "jpeg"); + MatchSemicolonList("gif; jpeg; jpg", "jpg"); + MatchSemicolonList("gif; jpeg; jpg", "gif"); + MatchSemicolonList("gif;jpeg;jpg", "gif;"); + MatchSemicolonList("gif; jpeg; jpg", "jpe"); + MatchSemicolonList("gif; jpeg; jpg", "jpegx"); + MatchSemicolonList("gif; jpeg; jpg", "jp"); + MatchSemicolonList("gif; jpeg; jpg", "jpgx"); + MatchSemicolonList("gif; jpeg; jpg", "if"); + MatchSemicolonList("gif; jpeg; jpg", "gifs"); + MatchSemicolonList("gif, jpeg; jpg", "jpeg"); + MatchSemicolonList("", "jpeg"); + MatchSemicolonList(";", "jpeg"); + MatchSemicolonList("gif, jpeg; jpg", ""); + */ + + /* + * Figure out what to do with it. + */ + CString extViewerExts; + extViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); + if (ext != nil && MatchSemicolonList(extViewerExts, ext+1)) { + WMSG1(" Launching external viewer for '%s'\n", ext); + TmpExtractForExternal(pEntry); + handled = true; + } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) { + if ((ext != nil && ( + stricmp(ext, ".shk") == 0 || + stricmp(ext, ".sdk") == 0 || + stricmp(ext, ".bxy") == 0 )) || + (fileType == 0xe0 && auxType == 0x8002)) + { + WMSG0(" Guessing NuFX\n"); + TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeNuFX); + handled = true; + } else + if ((ext != nil && ( + stricmp(ext, ".bny") == 0 || + stricmp(ext, ".bqy") == 0 )) || + (fileType == 0xe0 && auxType == 0x8000)) + { + WMSG0(" Guessing Binary II\n"); + TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeBinaryII); + handled = true; + } else + if ((ext != nil && ( + stricmp(ext, ".acu") == 0 )) || + (fileType == 0xe0 && auxType == 0x8001)) + { + WMSG0(" Guessing ACU\n"); + TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeACU); + handled = true; + } else + if (fileType == 0x64496d67 && auxType == 0x64437079 && + pEntry->GetUncompressedLen() == 819284) + { + /* type is dImg, creator is dCpy, length is 800K + DC stuff */ + WMSG0(" Looks like a disk image\n"); + TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage); + handled = true; + } + } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { + WMSG0(" Opening archived disk image\n"); + TmpExtractAndOpen(pEntry, GenericEntry::kDiskImageThread, kModeDiskImage); + handled = true; + } + + if (!handled) { + // standard viewer + HandleView(); + } + + /* set "/t" temp flag and delete afterward, warning user (?) */ +} + +/* + * Extract a record to the temp folder and open it with a new instance of + * CiderPress. We might want to extract disk images as 2MG files to take + * the mystery out of opening them, but since they're coming out of a + * ShrinkIt archive they're pretty un-mysterious anyway. + * + * We tell the new instance to open it read-only, and flag it for + * deletion on exit. + * + * Returns 0 on success, nonzero error status on failure. + */ +int +MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, + const char* modeStr) +{ + CString dispName; + bool mustDelete = false; + + /* + * Get the name to display in the title bar. Double quotes will + * screw it up, so we have to replace them. (We could escape them, + * but then we'd also have to escape the escape char.) + */ + dispName = pEntry->GetFileName(); + dispName.Replace('"', '_'); + + char nameBuf[MAX_PATH]; + UINT unique; + unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), + "CPfile", 0, nameBuf); + if (unique == 0) { + DWORD dwerr = ::GetLastError(); + WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", + fPreferences.GetPrefString(kPrTempPath), dwerr); + return dwerr; + } + mustDelete = true; + + /* + * Open the temp file and extract the data into it. + */ + CString errMsg; + int result; + FILE* fp; + + fp = fopen(nameBuf, "wb"); + if (fp != nil) { + WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); + result = pEntry->ExtractThreadToFile(threadKind, fp, + GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, + &errMsg); + fclose(fp); + if (result == IDOK) { + /* success */ + CString parameters; + + parameters.Format("-mode %s -dispname \"%s\" -temparc \"%s\"", + modeStr, dispName, nameBuf); + int err; + + err = (int) ::ShellExecute(m_hWnd, _T("open"), + gMyApp.GetExeFileName(), parameters, NULL, + SW_SHOWNORMAL); + if (err <= 32) { + CString msg; + msg.Format("Unable to launch CiderPress (err=%d).", err); + ShowFailureMsg(this, msg, IDS_FAILED); + } else { + /* during dev, "missing DLL" causes false-positive success */ + WMSG0("Successfully launched CiderPress\n"); + mustDelete = false; // up to newly-launched app + } + } else { + ShowFailureMsg(this, errMsg, IDS_FAILED); + } + } else { + CString msg; + msg.Format("Unable to open temp file '%s'.", nameBuf); + ::ShowFailureMsg(this, msg, IDS_FAILED); + } + + if (mustDelete) { + WMSG1("Deleting '%s'\n", nameBuf); + unlink(nameBuf); + } + + return 0; +} + +/* + * Extract a record to the temp folder and open it with an external viewer. + * The file must be created with the correct extension so ShellExecute + * does the right thing. + * + * The files will be added to the "delete on exit" list, so that they will + * be cleaned up when CiderPress exits (assuming the external viewer no longer + * has them open). + * + * The GetTempFileName function creates a uniquely-named temp file. We + * create a file that has that name plus an extension. To ensure that we + * don't try to use the same temp filename twice, we have to hold off on + * deleting the unused .tmp files until we're ready to delete the + * corresponding .gif (or whatever) files. Thus, each invocation of this + * function creates two files and two entries in the delete-on-exit set. + * + * Returns 0 on success, nonzero error status on failure. + */ +int +MainWindow::TmpExtractForExternal(GenericEntry* pEntry) +{ + const char* ext; + + ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); + + char nameBuf[MAX_PATH]; + UINT unique; + unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), + "CPfile", 0, nameBuf); + if (unique == 0) { + DWORD dwerr = ::GetLastError(); + WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", + fPreferences.GetPrefString(kPrTempPath), dwerr); + return dwerr; + } + fDeleteList.Add(nameBuf); // file is created by GetTempFileName + + strcat(nameBuf, ext); + + /* + * Open the temp file and extract the data into it. + */ + CString errMsg; + int result; + FILE* fp; + + fp = fopen(nameBuf, "wb"); + if (fp != nil) { + fDeleteList.Add(nameBuf); // second file created by fopen + WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); + result = pEntry->ExtractThreadToFile(GenericEntry::kDataThread, fp, + GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, + &errMsg); + fclose(fp); + if (result == IDOK) { + /* success */ + int err; + + err = (int) ::ShellExecute(m_hWnd, _T("open"), nameBuf, NULL, + NULL, SW_SHOWNORMAL); + if (err <= 32) { + CString msg; + msg.Format("Unable to launch external viewer (err=%d).", err); + ShowFailureMsg(this, msg, IDS_FAILED); + } else { + WMSG0("Successfully launched external viewer\n"); + } + } else { + ShowFailureMsg(this, errMsg, IDS_FAILED); + } + } else { + CString msg; + msg.Format("Unable to open temp file '%s'.", nameBuf); + ShowFailureMsg(this, msg, IDS_FAILED); + } + + return 0; +} + +#if 0 +/* + * Handle a "default action" selection from the right-click menu. The + * action only applies to the record that was clicked on, so we need to + * retrieve that from the control. + */ +void +MainWindow::OnRtClkDefault(void) +{ + int idx; + + ASSERT(fpContentList != nil); + + idx = fpContentList->GetRightClickItem(); + ASSERT(idx != -1); + WMSG1("OnRtClkDefault %d\n", idx); + + fpContentList->ClearRightClickItem(); +} +#endif + + +/* + * =================================== + * Progress meter + * =================================== + */ + +/* + * There are two different mechanisms for reporting progress: ActionProgress + * dialogs (for adding/extracting files) and a small box in the lower + * right-hand corner (for opening archives). These functions will set + * the progress in the active action progress dialog if it exists, or + * will set the percentage in the window frame if not. + */ + +void +MainWindow::SetProgressBegin(void) +{ + if (fpActionProgress != nil) + fpActionProgress->SetProgress(0); + else + fStatusBar.SetPaneText(kProgressPane, "--%"); + //WMSG0(" Complete: BEGIN\n"); + + /* redraw stuff with the changes */ + (void) PeekAndPump(); +} + +int +MainWindow::SetProgressUpdate(int percent, const char* oldName, + const char* newName) +{ + int status = IDOK; + + if (fpActionProgress != nil) { + status = fpActionProgress->SetProgress(percent); + if (oldName != nil) + fpActionProgress->SetArcName(oldName); + if (newName != nil) + fpActionProgress->SetFileName(newName); + } else { + char buf[8]; + sprintf(buf, "%d%%", percent); + fStatusBar.SetPaneText(kProgressPane, buf); + //WMSG1(" Complete: %s\n", buf); + } + + if (!PeekAndPump()) { + WMSG0("SetProgressUpdate: shutdown?!\n"); + } + + //EventPause(10); // DEBUG DEBUG + return status; +} + +void +MainWindow::SetProgressEnd(void) +{ + if (fpActionProgress != nil) + fpActionProgress->SetProgress(100); + else + fStatusBar.SetPaneText(kProgressPane, ""); +// EventPause(100); // DEBUG DEBUG + //WMSG0(" Complete: END\n"); +} + + +/* + * Set a number in the "progress counter". Useful for loading large archives + * where we're not sure how much stuff is left, so showing a percentage is + * hard. + * + * Pass in -1 to erase the counter. + * + * Returns "true" if we'd like things to continue. + */ +bool +MainWindow::SetProgressCounter(const char* str, long val) +{ + /* if the main window is enabled, user could activate menus */ + ASSERT(!IsWindowEnabled()); + + if (fpProgressCounter != nil) { + //WMSG2("SetProgressCounter '%s' %d\n", str, val); + CString msg; + + if (str != nil) + fpProgressCounter->SetCounterFormat(str); + fpProgressCounter->SetCount((int) val); + } else { + if (val < 0) { + fStatusBar.SetPaneText(kProgressPane, ""); + } else { + CString tmpStr; + tmpStr.Format("%ld", val); + fStatusBar.SetPaneText(kProgressPane, tmpStr); + } + } + + if (!PeekAndPump()) { + WMSG0("SetProgressCounter: shutdown?!\n"); + } + //EventPause(10); // DEBUG DEBUG + + if (fpProgressCounter != nil) + return !fpProgressCounter->GetCancel(); + else + return true; +} + + +/* + * Allow events to flow through the message queue whenever the + * progress meter gets updated. This will allow us to redraw with + * reasonable frequency. + * + * Calling this can result in other code being called, such as Windows + * message handlers, which can lead to reentrancy problems. Make sure + * you're adequately semaphored before calling here. + * + * Returns TRUE if all is well, FALSE if we're trying to quit. + */ +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; +} + +/* + * Go to sleep for a little bit, waking up 100x per second to check + * the idle loop. + */ +void +MainWindow::EventPause(int duration) +{ + int count = duration / 10; + + for (int i = 0; i < count; i++) { + PeekAndPump(); + ::Sleep(10); + } +} + +/* + * Printer abort procedure; allows us to abort a print job. The DC + * SetAbortProc() function calls here periodically. The return value from + * this function determine whether or not printing halts. + * + * This checks a global "print cancel" variable, which is set by our print + * cancel button dialog. + * + * If this returns TRUE, printing continues; FALSE, and printing aborts. + */ +/*static*/ BOOL CALLBACK +MainWindow::PrintAbortProc(HDC hDC, int nCode) +{ + MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); + + pMain->PeekAndPump(); + if (pMain->GetAbortPrinting()) { + WMSG0("PrintAbortProc returning FALSE (abort printing)\n"); + return FALSE; + } + WMSG0(" PrintAbortProc returning TRUE (continue printing)\n"); + return TRUE; +} + +/* + * =================================== + * Support functions + * =================================== + */ + +/* + * Draw what looks like an empty client area. + */ +void +MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect) +{ + CBrush brush; + brush.CreateSolidBrush(::GetSysColor(COLOR_APPWORKSPACE)); // dk gray + CBrush* pOldBrush = pDC->SelectObject(&brush); + pDC->FillRect(&clientRect, &brush); + pDC->SelectObject(pOldBrush); + + CPen penWH(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)); // white + CPen penLG(PS_SOLID, 1, ::GetSysColor(COLOR_3DLIGHT)); // lt gray + CPen penDG(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); // dk gray + CPen penBL(PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW)); // near-black + CPen* pOldPen = pDC->SelectObject(&penWH); + //pDC->SelectObject(&penWH); + pDC->MoveTo(clientRect.right-1, clientRect.top); + pDC->LineTo(clientRect.right-1, clientRect.bottom-1); + pDC->LineTo(clientRect.left-1, clientRect.bottom-1); + pDC->SelectObject(&penBL); + pDC->MoveTo(clientRect.right-3, clientRect.top+1); + pDC->LineTo(clientRect.left+1, clientRect.top+1); + pDC->LineTo(clientRect.left+1, clientRect.bottom-2); + pDC->SelectObject(&penLG); + pDC->MoveTo(clientRect.right-2, clientRect.top+1); + pDC->LineTo(clientRect.right-2, clientRect.bottom-2); + pDC->LineTo(clientRect.left, clientRect.bottom-2); + pDC->SelectObject(&penDG); + pDC->MoveTo(clientRect.right-2, clientRect.top); + pDC->LineTo(clientRect.left, clientRect.top); + pDC->LineTo(clientRect.left, clientRect.bottom-1); + + pDC->SelectObject(pOldPen); +} + +/* + * Load an archive, using the appropriate GenericArchive subclass. If + * "createFile" is "true", a new archive file will be created (and must + * not already exist!). + * + * "filename" is the full path to the file, "extension" is the + * filetype component of the name (without the leading '.'), "filterIndex" + * is the offset into the set of filename filters used in the standard + * file dialog, "readOnly" reflects the state of the stdfile dialog + * checkbox, and "createFile" is set to true by the "New Archive" command. + * + * Returns 0 on success, nonzero on failure. + */ +int +MainWindow::LoadArchive(const char* fileName, const char* extension, + int filterIndex, bool readOnly, bool createFile) +{ + GenericArchive::OpenResult openResult; + int result = -1; + GenericArchive* pOpenArchive = nil; + int origFilterIndex = filterIndex; + CString errStr, appName; + + appName.LoadString(IDS_MB_APP_NAME); + + WMSG3("LoadArchive: '%s' ro=%d idx=%d\n", fileName, readOnly, filterIndex); + + /* close any existing archive to avoid weirdness from re-open */ + CloseArchive(); + + /* + * If they used the "All Files (*.*)" filter, we have to guess based + * on the file type. + * + * IDEA: change the current "filterIndex ==" stuff to a type-specific + * model, then do type-scanning here. Code later on takes the type + * and opens it. That way we can do the trivial "it must be" handling + * up here, and maybe do a little "open it up and see" stuff as well. + * In general, though, if we don't recognize the extension, it's + * probably a disk image. + */ + if (filterIndex == kFilterIndexGeneric) { + int i; + + for (i = 0; i < NELEM(gExtensionToIndex); i++) { + if (strcasecmp(extension, gExtensionToIndex[i].extension) == 0) { + filterIndex = gExtensionToIndex[i].idx; + break; + } + } + + if (i == NELEM(gExtensionToIndex)) + filterIndex = kFilterIndexDiskImage; + } + +try_again: + if (filterIndex == kFilterIndexBinaryII) { + /* try Binary II and nothing else */ + ASSERT(!createFile); + WMSG0(" Trying Binary II\n"); + pOpenArchive = new BnyArchive; + openResult = pOpenArchive->Open(fileName, readOnly, &errStr); + if (openResult != GenericArchive::kResultSuccess) { + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + result = -1; + goto bail; + } + } else + if (filterIndex == kFilterIndexACU) { + /* try ACU and nothing else */ + ASSERT(!createFile); + WMSG0(" Trying ACU\n"); + pOpenArchive = new AcuArchive; + openResult = pOpenArchive->Open(fileName, readOnly, &errStr); + if (openResult != GenericArchive::kResultSuccess) { + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + result = -1; + goto bail; + } + } else + if (filterIndex == kFilterIndexDiskImage) { + /* try various disk image formats */ + ASSERT(!createFile); + WMSG0(" Trying disk images\n"); + + pOpenArchive = new DiskArchive; + openResult = pOpenArchive->Open(fileName, readOnly, &errStr); + if (openResult == GenericArchive::kResultCancel) { + result = -1; + goto bail; + } else if (openResult == GenericArchive::kResultFileArchive) { + delete pOpenArchive; + pOpenArchive = nil; + + if (strcasecmp(extension, "zip") == 0) { + errStr = "ZIP archives with multiple files are not supported."; + MessageBox(errStr, appName, MB_OK|MB_ICONINFORMATION); + result = -1; + goto bail; + } else { + /* assume some variation of a ShrinkIt archive */ + // msg.LoadString(IDS_OPEN_AS_NUFX); <-- with MB_OKCANCEL + filterIndex = kFilterIndexNuFX; + goto try_again; + } + + } else if (openResult != GenericArchive::kResultSuccess) { + if (filterIndex != origFilterIndex) { + /* + * Kluge: assume we guessed disk image and were wrong. + */ + errStr = "File doesn't appear to be a valid archive" + " or disk image."; + } + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + result = -1; + goto bail; + } + } else + if (filterIndex == kFilterIndexNuFX) { + /* try NuFX (including its embedded-in-BNY form) */ + WMSG0(" Trying NuFX\n"); + + pOpenArchive = new NufxArchive; + openResult = pOpenArchive->Open(fileName, readOnly, &errStr); + if (openResult != GenericArchive::kResultSuccess) { + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + result = -1; + goto bail; + } + + } else { + ASSERT(FALSE); + result = -1; + goto bail; + } + + SwitchContentList(pOpenArchive); + + pOpenArchive = nil; + result = 0; + +bail: + if (pOpenArchive != nil) { + ASSERT(result != 0); + delete pOpenArchive; + } + return result; +} + +/* + * Open a raw disk volume. Useful for ProDOS-formatted 1.44MB floppy disks + * and CFFA flash cards. + * + * Assume it's a disk image -- it'd be a weird place for a ShrinkIt archive. + * CFFA cards can actually hold multiple volumes, but that's all taken care + * of inside the diskimg DLL. + * + * Returns 0 on success, nonzero on failure. + */ +int +MainWindow::DoOpenVolume(CString drive, bool readOnly) +{ + int result = -1; + + ASSERT(drive.GetLength() > 0); + + CString errStr; + //char filename[4] = "_:\\"; + //filename[0] = driveLetter; + + WMSG2("FileOpenVolume '%s' %d\n", (const char*)drive, readOnly); + + /* close existing archive */ + CloseArchive(); + + GenericArchive* pOpenArchive = nil; + pOpenArchive = new DiskArchive; + { + CWaitCursor waitc; + GenericArchive::OpenResult openResult; + + openResult = pOpenArchive->Open(drive, readOnly, &errStr); + if (openResult == GenericArchive::kResultCancel) { + // this bubbles out of the format confirmation dialog + goto bail; + } else if (openResult != GenericArchive::kResultSuccess) { + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + goto bail; + } + } + + // success! + SwitchContentList(pOpenArchive); + pOpenArchive = nil; + fOpenArchivePathName = drive; + result = 0; + + fOpenArchivePathName = drive; + SetCPTitle(fOpenArchivePathName, fpOpenArchive); + +bail: + if (pOpenArchive != nil) { + ASSERT(result != 0); + delete pOpenArchive; + } + return result; +} + + +/* + * Close and re-open the current archive. + */ +void +MainWindow::ReopenArchive(void) +{ + if (fpOpenArchive == nil) { + ASSERT(false); + return; + } + + /* clear the flag, regardless of success or failure */ + fNeedReopen = false; + + GenericArchive* pOpenArchive = nil; + CString pathName = fpOpenArchive->GetPathName(); + bool readOnly = fpOpenArchive->IsReadOnly(); + GenericArchive::ArchiveKind archiveKind = fpOpenArchive->GetArchiveKind(); + GenericArchive::OpenResult openResult; + CString errStr; + + /* if the open fails we *don't* want to leave the previous content up */ + WMSG3("Reopening '%s' ro=%d kind=%d\n", pathName, readOnly, archiveKind); + CloseArchive(); + + switch (archiveKind) { + case GenericArchive::kArchiveDiskImage: + pOpenArchive = new DiskArchive; + break; + case GenericArchive::kArchiveNuFX: + pOpenArchive = new NufxArchive; + break; + case GenericArchive::kArchiveBNY: + pOpenArchive = new BnyArchive; + break; + default: + ASSERT(false); + return; + } + + openResult = pOpenArchive->Open(pathName, readOnly, &errStr); + if (openResult == GenericArchive::kResultCancel) { + // this bubbles out of the format confirmation dialog + goto bail; + } else if (openResult != GenericArchive::kResultSuccess) { + if (!errStr.IsEmpty()) + ShowFailureMsg(this, errStr, IDS_FAILED); + goto bail; + } + + WMSG0(" Reopen was successful\n"); + SwitchContentList(pOpenArchive); + pOpenArchive = nil; + SetCPTitle(pathName, fpOpenArchive); + +bail: + delete pOpenArchive; +} + +/* + * Determine whether "path" matches the pathname of the currently open archive. + */ +bool +MainWindow::IsOpenPathName(const char* path) +{ + if (fpOpenArchive == nil) + return false; + + if (stricmp(path, fpOpenArchive->GetPathName()) == 0) + return true; + + return false; +} + + +/* + * Switch the content list to a new archive, closing the previous if one + * was already open. + */ +void +MainWindow::SwitchContentList(GenericArchive* pOpenArchive) +{ + assert(pOpenArchive != nil); + + /* + * We've got an archive opened successfully. If we already had one + * open, shut it. (This assumes that closing an archive is a simple + * matter of closing files and freeing storage. If we needed to do + * something that might fail, like flush changes, we should've done + * that before getting this far to avoid confusion.) + */ + if (fpOpenArchive != nil) + CloseArchive(); + + ASSERT(fpOpenArchive == nil); + ASSERT(fpContentList == nil); + + /* + * Without this we get an assertion failure in CImageList::Attach if we + * call here from ReopenArchive. I think Windows needs to do some + * cleanup, though I don't understand how the reopen case differs from + * the usual case. Maybe there's more stuff pending in the "reopen" + * case? In any event, this seems to work, which is all you can hope + * for from MFC. It does, however, make the screen flash, which it + * didn't do before. + * + * UPDATE: this tripped once while I was debugging, even with this. The + * PeekAndPump function does force the idle loop to run, so I'm not sure + * why it failed, unless the debugger somehow affected the idle + * processing. Yuck. + * + * The screen flash bugged me so I took it back out. And the assert + * didn't hit. I really, really love Windows. + */ + //PeekAndPump(); + + + fpContentList = new ContentList(pOpenArchive, + fPreferences.GetColumnLayout()); + + CRect sizeRect; + GetClientRect(&sizeRect); + fpContentList->Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL, + sizeRect, this, IDC_CONTENT_LIST); + + fpOpenArchive = pOpenArchive; +} + + +/* + * Close the existing archive file, but don't try to shut down the child + * windows. This should really only be used from the destructor. + */ +void +MainWindow::CloseArchiveWOControls(void) +{ + if (fpOpenArchive != nil) { + //fpOpenArchive->Close(); + WMSG0("Deleting OpenArchive\n"); + delete fpOpenArchive; + fpOpenArchive = nil; + } +} + +/* + * Close the existing archive file, and throw out the control we're + * using to display it. + */ +void +MainWindow::CloseArchive(void) +{ + CWaitCursor waitc; // closing large compressed archive can be slow + + // destroy the ContentList + if (fpContentList != nil) { + WMSG0("Destroying ContentList\n"); + fpContentList->DestroyWindow(); // auto-cleanup invokes "delete" + fpContentList = nil; + } + + // destroy the GenericArchive + CloseArchiveWOControls(); + + // reset the title bar + SetCPTitle(); +} + + +/* + * Set the title bar on the main window. + * + * "pathname" is often different from pOpenArchive->GetPathName(), especially + * when we were launched from another instance of CiderPress and handed a + * temp file whose name we're trying to conceal. + */ +void +MainWindow::SetCPTitle(const char* pathname, GenericArchive* pOpenArchive) +{ + ASSERT(pathname != nil); + CString title; + CString archiveDescription; + CString appName; + + appName.LoadString(IDS_MB_APP_NAME); + + pOpenArchive->GetDescription(&archiveDescription); + title.Format(_T("%s - %s (%s)"), appName, pathname, archiveDescription); + + if (fpOpenArchive->IsReadOnly()) { + CString readOnly; + readOnly.LoadString(IDS_READONLY); + title += _T(" "); + title += readOnly; + } + + SetWindowText(title); +} + +/* + * Set the title bar to something boring when nothing is open. + */ +void +MainWindow::SetCPTitle(void) +{ + CString appName, regName, title; + CString user, company, reg, versions, expire; + +#if 0 + if (gMyApp.fRegistry.GetRegistration(&user, &company, ®, &versions, + &expire) == 0) + { + if (reg.IsEmpty()) { + regName += _T(" (unregistered)"); + } else { + regName += _T(" (registered to "); + regName += user; + regName += _T(")"); + // include company? + } + } +#endif + + appName.LoadString(IDS_MB_APP_NAME); + title = appName + regName; + SetWindowText(title); +} + +/* + * Come up with a title to put at the top of a printout. This is essentially + * the same as the window title, but without some flags (e.g. "read-only"). + */ +CString +MainWindow::GetPrintTitle(void) +{ + CString title; + CString archiveDescription; + CString appName; + + if (fpOpenArchive == nil) { + ASSERT(false); + return title; + } + + appName.LoadString(IDS_MB_APP_NAME); + + fpOpenArchive->GetDescription(&archiveDescription); + title.Format(_T("%s - %s (%s)"), + appName, fOpenArchivePathName, archiveDescription); + + return title; +} + + +/* + * After successful completion of a command, make a happy noise (but only + * if we're configured to do so). + */ +void +MainWindow::SuccessBeep(void) +{ + const Preferences* pPreferences = GET_PREFERENCES(); + + if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { + WMSG0("\n"); + ::MessageBeep(MB_OK); + } +} + +/* + * If something fails, make noise if we're configured for loudness. + */ +void +MainWindow::FailureBeep(void) +{ + const Preferences* pPreferences = GET_PREFERENCES(); + + if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { + WMSG0("\n"); + ::MessageBeep(MB_ICONEXCLAMATION); // maybe MB_ICONHAND? + } +} + +/* + * Remove a file. Returns a helpful error string on failure. + * + * The absence of the file is not considered an error. + */ +CString +MainWindow::RemoveFile(const char* fileName) +{ + CString errMsg; + + int cc; + cc = unlink(fileName); + if (cc < 0 && errno != ENOENT) { + int err = errno; + WMSG2("Failed removing file '%s', errno=%d\n", fileName, err); + errMsg.Format("Unable to remove '%s': %s.", + fileName, strerror(err)); + if (err == EACCES) + errMsg += "\n\n(Make sure the file isn't open.)"; + } + + return errMsg; +} + + +/* + * Configure a ReformatHolder based on the current preferences. + */ +/*static*/ void +MainWindow::ConfigureReformatFromPreferences(ReformatHolder* pReformat) +{ + const Preferences* pPreferences = GET_PREFERENCES(); + + pReformat->SetReformatAllowed(ReformatHolder::kReformatRaw, true); + pReformat->SetReformatAllowed(ReformatHolder::kReformatHexDump, true); + + pReformat->SetReformatAllowed(ReformatHolder::kReformatTextEOL_HA, + pPreferences->GetPrefBool(kPrConvTextEOL_HA)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatResourceFork, + pPreferences->GetPrefBool(kPrConvResources)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatProDOSDirectory, + pPreferences->GetPrefBool(kPrConvProDOSFolder)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalText, + pPreferences->GetPrefBool(kPrConvPascalText)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalCode, + pPreferences->GetPrefBool(kPrConvPascalCode)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatCPMText, + pPreferences->GetPrefBool(kPrConvCPMText)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft, + pPreferences->GetPrefBool(kPrConvApplesoft)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft_Hilite, + pPreferences->GetPrefBool(kPrConvApplesoft)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger, + pPreferences->GetPrefBool(kPrConvInteger)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger_Hilite, + pPreferences->GetPrefBool(kPrConvInteger)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness, + pPreferences->GetPrefBool(kPrConvBusiness)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness_Hilite, + pPreferences->GetPrefBool(kPrConvBusiness)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSCAssem, + pPreferences->GetPrefBool(kPrConvSCAssem)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMerlin, + pPreferences->GetPrefBool(kPrConvSCAssem)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA2, + pPreferences->GetPrefBool(kPrConvSCAssem)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA3, + pPreferences->GetPrefBool(kPrConvSCAssem)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA4, + pPreferences->GetPrefBool(kPrConvSCAssem)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor8, + pPreferences->GetPrefBool(kPrConvDisasm)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmMerlin8, + pPreferences->GetPrefBool(kPrConvDisasm)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Long, + pPreferences->GetPrefBool(kPrConvDisasm)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Short, + pPreferences->GetPrefBool(kPrConvDisasm)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmOrcam16, + pPreferences->GetPrefBool(kPrConvDisasm)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatAWGS_WP, + pPreferences->GetPrefBool(kPrConvGWP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatTeach, + pPreferences->GetPrefBool(kPrConvGWP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatGWP, + pPreferences->GetPrefBool(kPrConvGWP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMagicWindow, + pPreferences->GetPrefBool(kPrConvText8)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, + pPreferences->GetPrefBool(kPrConvAWP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, + pPreferences->GetPrefBool(kPrConvAWP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatADB, + pPreferences->GetPrefBool(kPrConvADB)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatASP, + pPreferences->GetPrefBool(kPrConvASP)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes, + pPreferences->GetPrefBool(kPrConvHiRes)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes_BW, + pPreferences->GetPrefBool(kPrConvHiRes)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Latched, + pPreferences->GetPrefBool(kPrConvDHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_BW, + pPreferences->GetPrefBool(kPrConvDHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Plain140, + pPreferences->GetPrefBool(kPrConvDHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Window, + pPreferences->GetPrefBool(kPrConvDHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_PIC, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_JEQ, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Paintworks, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Packed, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_APF, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3200, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3201, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG256, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG3200, + pPreferences->GetPrefBool(kPrConvSHR)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatPrintShop, + pPreferences->GetPrefBool(kPrConvPrintShop)); + pReformat->SetReformatAllowed(ReformatHolder::kReformatMacPaint, + pPreferences->GetPrefBool(kPrConvMacPaint)); + + pReformat->SetOption(ReformatHolder::kOptHiliteHexDump, + pPreferences->GetPrefBool(kPrHighlightHexDump)); + pReformat->SetOption(ReformatHolder::kOptHiliteBASIC, + pPreferences->GetPrefBool(kPrHighlightBASIC)); + pReformat->SetOption(ReformatHolder::kOptHiResBW, + pPreferences->GetPrefBool(kPrConvHiResBlackWhite)); + pReformat->SetOption(ReformatHolder::kOptDHRAlgorithm, + pPreferences->GetPrefLong(kPrConvDHRAlgorithm)); + pReformat->SetOption(ReformatHolder::kOptRelaxGfxTypeCheck, + pPreferences->GetPrefBool(kPrRelaxGfxTypeCheck)); + pReformat->SetOption(ReformatHolder::kOptOneByteBrkCop, + pPreferences->GetPrefBool(kPrDisasmOneByteBrkCop)); +} + +/* + * Convert a DiskImg format spec into a ReformatHolder SourceFormat. + */ +/*static*/ ReformatHolder::SourceFormat +MainWindow::ReformatterSourceFormat(DiskImg::FSFormat format) +{ + if (DiskImg::UsesDOSFileStructure(format)) + return ReformatHolder::kSourceFormatDOS; + else if (format == DiskImg::kFormatCPM) + return ReformatHolder::kSourceFormatCPM; + else + return ReformatHolder::kSourceFormatGeneric; +} diff --git a/app/Preferences.cpp b/app/Preferences.cpp index 42e958b..900a068 100644 --- a/app/Preferences.cpp +++ b/app/Preferences.cpp @@ -1,618 +1,620 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Save and restore preferences from the config file. - */ -#include "stdafx.h" -#include "Preferences.h" -#include "NufxArchive.h" -#include "MyApp.h" -#include "../util/UtilLib.h" - -static const char* kDefaultTempPath = "."; - -/* registry section for columns */ -static const char* kColumnSect = _T("columns"); -/* registry section for file add options */ -static const char* kAddSect = _T("add"); -/* registry section for extraction options */ -static const char* kExtractSect = _T("extract"); -/* registry section for view options */ -static const char* kViewSect = _T("view"); -/* registry section for logical/physical volume operations */ -static const char* kVolumeSect = _T("volume"); -/* registry section for file-to-disk options */ -//static const char* kConvDiskSect = _T("conv-disk"); -/* registry section for disk-to-file options */ -static const char* kConvFileSect = _T("conv-file"); -/* registry section for folders */ -static const char* kFolderSect = _T("folders"); -/* registry section for preferences on property pages */ -static const char* kPrefsSect = _T("prefs"); -/* registry section for miscellaneous settings */ -static const char* kMiscSect = _T("misc"); - - -/* - * Map PrefNum to type and registry string. - * - * To make life easier, we require that the PrefNum (first entry) match the - * offset in the table. That way instead of searching for a match we can just - * index into the table. - */ -const Preferences::PrefMap Preferences::fPrefMaps[kPrefNumLastEntry] = { - /**/ { kPrefNumUnknown, kPTNone, nil, nil }, - - { kPrAddIncludeSubFolders, kBool, kAddSect, _T("include-sub-folders") }, - { kPrAddStripFolderNames, kBool, kAddSect, _T("strip-folder-names") }, - { kPrAddOverwriteExisting, kBool, kAddSect, _T("overwrite-existing") }, - { kPrAddTypePreservation, kLong, kAddSect, _T("type-preservation") }, - { kPrAddConvEOL, kLong, kAddSect, _T("conv-eol") }, - -// { kPrExtractPath, kString, kExtractSect, _T("path") }, - { kPrExtractConvEOL, kLong, kExtractSect, _T("conv-eol") }, - { kPrExtractConvHighASCII, kBool, kExtractSect, _T("conv-high-ascii") }, - { kPrExtractIncludeData, kBool, kExtractSect, _T("include-data") }, - { kPrExtractIncludeRsrc, kBool, kExtractSect, _T("include-rsrc") }, - { kPrExtractIncludeDisk, kBool, kExtractSect, _T("include-disk") }, - { kPrExtractEnableReformat, kBool, kExtractSect, _T("enable-reformat") }, - { kPrExtractDiskTo2MG, kBool, kExtractSect, _T("disk-to-2mg") }, - { kPrExtractAddTypePreservation, kBool, kExtractSect, _T("add-type-preservation") }, - { kPrExtractAddExtension, kBool, kExtractSect, _T("add-extension") }, - { kPrExtractStripFolderNames, kBool, kExtractSect, _T("strip-folder-names") }, - { kPrExtractOverwriteExisting, kBool, kExtractSect, _T("overwrite-existing") }, - -// { kPrViewIncludeDataForks, kBool, kViewSect, _T("include-data-forks") }, -// { kPrViewIncludeRsrcForks, kBool, kViewSect, _T("include-rsrc-forks") }, -// { kPrViewIncludeDiskImages, kBool, kViewSect, _T("include-disk-images") }, -// { kPrViewIncludeComments, kBool, kViewSect, _T("include-comments") }, - - { kPrConvFileEmptyFolders, kBool, kConvFileSect, _T("preserve-empty-folders") }, - - { kPrOpenArchiveFolder, kString, kFolderSect, _T("open-archive") }, - { kPrConvertArchiveFolder, kString, kFolderSect, _T("convert-archive") }, - { kPrAddFileFolder, kString, kFolderSect, _T("add-file") }, - { kPrExtractFileFolder, kString, kFolderSect, _T("extract-file") }, - - { kPrVolumeFilter, kLong, kVolumeSect, _T("open-filter") }, - //{ kPrVolumeReadOnly, kBool, kVolumeSect, _T("read-only") }, - - { kPrCassetteAlgorithm, kLong, kVolumeSect, _T("cassette-algorithm") }, - { kPrOpenWAVFolder, kString, kFolderSect, _T("open-wav") }, - - { kPrMimicShrinkIt, kBool, kPrefsSect, _T("mimic-shrinkit") }, - { kPrBadMacSHK, kBool, kPrefsSect, _T("bad-mac-shk") }, - { kPrReduceSHKErrorChecks, kBool, kPrefsSect, _T("reduce-shk-error-checks") }, - { kPrCoerceDOSFilenames, kBool, kPrefsSect, _T("coerce-dos-filenames") }, - { kPrSpacesToUnder, kBool, kPrefsSect, _T("spaces-to-under") }, - { kPrPasteJunkPaths, kBool, kPrefsSect, _T("paste-junk-paths") }, - { kPrBeepOnSuccess, kBool, kPrefsSect, _T("beep-on-success") }, - - { kPrQueryImageFormat, kBool, kPrefsSect, _T("query-image-format") }, - { kPrOpenVolumeRO, kBool, kPrefsSect, _T("open-volume-ro") }, - { kPrOpenVolumePhys0, kBool, kPrefsSect, _T("open-volume-phys0") }, - { kPrProDOSAllowLower, kBool, kPrefsSect, _T("prodos-allow-lower") }, - { kPrProDOSUseSparse, kBool, kPrefsSect, _T("prodos-use-sparse") }, - - { kPrCompressionType, kLong, kPrefsSect, _T("compression-type") }, - - { kPrMaxViewFileSize, kLong, kPrefsSect, _T("max-view-file-size") }, - { kPrNoWrapText, kBool, kPrefsSect, _T("no-wrap-text") }, - - { kPrHighlightHexDump, kBool, kPrefsSect, _T("highlight-hex-dump") }, - { kPrHighlightBASIC, kBool, kPrefsSect, _T("highlight-basic") }, - { kPrConvHiResBlackWhite, kBool, kPrefsSect, _T("conv-hi-res-black-white") }, - { kPrConvDHRAlgorithm, kLong, kPrefsSect, _T("dhr-algorithm") }, - { kPrRelaxGfxTypeCheck, kBool, kPrefsSect, _T("relax-gfx-type-check") }, - { kPrDisasmOneByteBrkCop, kBool, kPrefsSect, _T("disasm-onebytebrkcop") }, - //{ kPrEOLConvRaw, kBool, kPrefsSect, _T("eol-conv-raw") }, - { kPrConvTextEOL_HA, kBool, kPrefsSect, _T("conv-eol-ha") }, - { kPrConvPascalText, kBool, kPrefsSect, _T("conv-pascal-text") }, - { kPrConvPascalCode, kBool, kPrefsSect, _T("conv-pascal-code") }, - { kPrConvCPMText, kBool, kPrefsSect, _T("conv-cpm-text") }, - { kPrConvApplesoft, kBool, kPrefsSect, _T("conv-applesoft") }, - { kPrConvInteger, kBool, kPrefsSect, _T("conv-integer") }, - { kPrConvGWP, kBool, kPrefsSect, _T("conv-gwp") }, - { kPrConvText8, kBool, kPrefsSect, _T("conv-text8") }, - { kPrConvAWP, kBool, kPrefsSect, _T("conv-awp") }, - { kPrConvADB, kBool, kPrefsSect, _T("conv-adb") }, - { kPrConvASP, kBool, kPrefsSect, _T("conv-asp") }, - { kPrConvSCAssem, kBool, kPrefsSect, _T("conv-scassem") }, - { kPrConvDisasm, kBool, kPrefsSect, _T("conv-disasm") }, - { kPrConvHiRes, kBool, kPrefsSect, _T("conv-hi-res") }, - { kPrConvDHR, kBool, kPrefsSect, _T("conv-dhr") }, - { kPrConvSHR, kBool, kPrefsSect, _T("conv-shr") }, - { kPrConvPrintShop, kBool, kPrefsSect, _T("conv-print-shop") }, - { kPrConvMacPaint, kBool, kPrefsSect, _T("conv-mac-paint") }, - { kPrConvProDOSFolder, kBool, kPrefsSect, _T("conv-prodos-folder") }, - { kPrConvResources, kBool, kPrefsSect, _T("conv-resources") }, - - { kPrTempPath, kString, kPrefsSect, _T("temp-path") }, - { kPrExtViewerExts, kString, kPrefsSect, _T("extviewer-exts") }, - - { kPrLastOpenFilterIndex, kLong, kMiscSect, _T("open-filter-index") }, - - /**/ { kPrefNumLastRegistry, kPTNone, nil, nil }, - - { kPrViewTextTypeFace, kString, nil, nil }, - { kPrViewTextPointSize, kLong, nil, nil }, - { kPrFileViewerWidth, kLong, nil, nil }, - { kPrFileViewerHeight, kLong, nil, nil }, - { kPrDiskImageCreateFormat, kLong, nil, nil }, -}; - -/* - * Constructor. There should be only one Preferences object in the - * application, so this should only be run once. - */ -Preferences::Preferences(void) -{ - WMSG0("Initializing Preferences\n"); - - ScanPrefMaps(); // sanity-check the table - memset(fValues, 0, sizeof(fValues)); - - SetPrefBool(kPrAddIncludeSubFolders, true); - SetPrefBool(kPrAddStripFolderNames, false); - SetPrefBool(kPrAddOverwriteExisting, false); - SetPrefLong(kPrAddTypePreservation, 1); // kPreserveTypes - SetPrefLong(kPrAddConvEOL, 1); // kConvEOLType - - InitFolders(); // set default add/extract folders; overriden by reg - SetPrefLong(kPrExtractConvEOL, 0); // kConvEOLNone - SetPrefBool(kPrExtractConvHighASCII, true); - SetPrefBool(kPrExtractIncludeData, true); - SetPrefBool(kPrExtractIncludeRsrc, false); - SetPrefBool(kPrExtractIncludeDisk, true); - SetPrefBool(kPrExtractEnableReformat, false); - SetPrefBool(kPrExtractDiskTo2MG, false); - SetPrefBool(kPrExtractAddTypePreservation, true); - SetPrefBool(kPrExtractAddExtension, false); - SetPrefBool(kPrExtractStripFolderNames, false); - SetPrefBool(kPrExtractOverwriteExisting, false); - -// SetPrefBool(kPrViewIncludeDataForks, true); -// SetPrefBool(kPrViewIncludeRsrcForks, false); -// SetPrefBool(kPrViewIncludeDiskImages, false); -// SetPrefBool(kPrViewIncludeComments, false); - - SetPrefBool(kPrConvFileEmptyFolders, true); - - // string kPrOpenArchiveFolder - // string kPrAddFileFolder - // string kPrExtractFileFolder - - SetPrefLong(kPrVolumeFilter, 0); - //SetPrefBool(kPrVolumeReadOnly, true); - - SetPrefLong(kPrCassetteAlgorithm, 0); - // string kPrOpenWAVFolder - - SetPrefBool(kPrMimicShrinkIt, false); - SetPrefBool(kPrBadMacSHK, false); - SetPrefBool(kPrReduceSHKErrorChecks, false); - SetPrefBool(kPrCoerceDOSFilenames, false); - SetPrefBool(kPrSpacesToUnder, false); - SetPrefBool(kPrPasteJunkPaths, true); - SetPrefBool(kPrBeepOnSuccess, true); - - SetPrefBool(kPrQueryImageFormat, false); - SetPrefBool(kPrOpenVolumeRO, true); - SetPrefBool(kPrOpenVolumePhys0, false); - SetPrefBool(kPrProDOSAllowLower, false); - SetPrefBool(kPrProDOSUseSparse, true); - - SetPrefLong(kPrCompressionType, DefaultCompressionType()); - - SetPrefLong(kPrMaxViewFileSize, 1024*1024); // 1MB - SetPrefBool(kPrNoWrapText, false); - - SetPrefBool(kPrHighlightHexDump, false); - SetPrefBool(kPrHighlightBASIC, false); - SetPrefBool(kPrConvHiResBlackWhite, false); - SetPrefLong(kPrConvDHRAlgorithm, 1); // latched - SetPrefBool(kPrRelaxGfxTypeCheck, true); - SetPrefBool(kPrDisasmOneByteBrkCop, false); - //SetPrefBool(kPrEOLConvRaw, true); - SetPrefBool(kPrConvTextEOL_HA, true); - SetPrefBool(kPrConvPascalText, true); - SetPrefBool(kPrConvPascalCode, true); - SetPrefBool(kPrConvCPMText, true); - SetPrefBool(kPrConvApplesoft, true); - SetPrefBool(kPrConvInteger, true); - SetPrefBool(kPrConvGWP, true); - SetPrefBool(kPrConvText8, true); - SetPrefBool(kPrConvAWP, true); - SetPrefBool(kPrConvADB, true); - SetPrefBool(kPrConvASP, true); - SetPrefBool(kPrConvSCAssem, true); - SetPrefBool(kPrConvDisasm, true); - SetPrefBool(kPrConvHiRes, true); - SetPrefBool(kPrConvDHR, true); - SetPrefBool(kPrConvSHR, true); - SetPrefBool(kPrConvPrintShop, true); - SetPrefBool(kPrConvMacPaint, true); - SetPrefBool(kPrConvProDOSFolder, true); - SetPrefBool(kPrConvResources, true); - - InitTempPath(); // set default for kPrTempPath - SetPrefString(kPrExtViewerExts, "gif; jpg; jpeg"); - - SetPrefLong(kPrLastOpenFilterIndex, 0); - - SetPrefString(kPrViewTextTypeFace, "Courier New"); - SetPrefLong(kPrViewTextPointSize, 10); - long width = 680; /* exact width for 80-column text */ - long height = 510; /* exact height for file viewer to show IIgs graphic */ - if (GetSystemMetrics(SM_CXSCREEN) < width) - width = GetSystemMetrics(SM_CXSCREEN); - if (GetSystemMetrics(SM_CYSCREEN) < height) - height = GetSystemMetrics(SM_CYSCREEN); // may overlap system bar - //width = 640; height = 480; - SetPrefLong(kPrFileViewerWidth, width); - SetPrefLong(kPrFileViewerHeight, height); - SetPrefLong(kPrDiskImageCreateFormat, -1); -} - - -/* - * ========================================================================== - * ColumnLayout - * ========================================================================== - */ - -/* - * Restore column widths. - */ -void -ColumnLayout::LoadFromRegistry(const char* section) -{ - char numBuf[8]; - int i; - - for (i = 0; i < kNumVisibleColumns; i++) { - sprintf(numBuf, "%d", i); - - fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, - fColumnWidth[i]); - fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, - fColumnWidth[i]); - } - fSortColumn = gMyApp.GetProfileInt(section, _T("sort-column"), fSortColumn); - fAscending = (gMyApp.GetProfileInt(section, _T("ascending"), fAscending) != 0); -} - -/* - * Store column widths. - */ -void -ColumnLayout::SaveToRegistry(const char* section) -{ - char numBuf[8]; - int i; - - for (i = 0; i < kNumVisibleColumns; i++) { - sprintf(numBuf, "%d", i); - - gMyApp.WriteProfileInt(section, numBuf, fColumnWidth[i]); - } - gMyApp.WriteProfileInt(section, _T("sort-column"), fSortColumn); - gMyApp.WriteProfileInt(section, _T("ascending"), fAscending); -} - - -/* - * ========================================================================== - * Preferences - * ========================================================================== - */ - -/* - * Get a default value for the temp path. - */ -void -Preferences::InitTempPath(void) -{ - char buf[MAX_PATH]; - DWORD len; - CString tempPath; - - len = ::GetTempPath(sizeof(buf), buf); - if (len == 0) { - DWORD err = ::GetLastError(); - WMSG1("GetTempPath failed, err=%d\n", err); - tempPath = kDefaultTempPath; - } else if (len >= sizeof(buf)) { - /* sheesh! */ - WMSG1("GetTempPath wants a %d-byte buffer\n", len); - tempPath = kDefaultTempPath; - } else { - tempPath = buf; - } - - PathName path(tempPath); - WMSG1("Temp path is '%s'\n", tempPath); - path.SFNToLFN(); - tempPath = path.GetPathName(); - - WMSG1("Temp path (long form) is '%s'\n", tempPath); - - SetPrefString(kPrTempPath, tempPath); - -// ::GetFullPathName(fTempPath, sizeof(buf), buf, &foo); -// ::SetCurrentDirectory(buf); -// ::GetCurrentDirectory(sizeof(buf2), buf2); -} - -/* - * Set default values for the various folders. - */ -void -Preferences::InitFolders(void) -{ - CString path; - - if (GetMyDocuments(&path)) { - SetPrefString(kPrOpenArchiveFolder, path); - SetPrefString(kPrConvertArchiveFolder, path); - SetPrefString(kPrAddFileFolder, path); - SetPrefString(kPrExtractFileFolder, path); - SetPrefString(kPrOpenWAVFolder, path); - } else { - char buf[MAX_PATH]; - ::GetCurrentDirectory(sizeof(buf), buf); - SetPrefString(kPrOpenArchiveFolder, buf); - SetPrefString(kPrConvertArchiveFolder, buf); - SetPrefString(kPrAddFileFolder, buf); - SetPrefString(kPrExtractFileFolder, buf); - SetPrefString(kPrOpenWAVFolder, buf); - } - - WMSG1("Default folder is '%s'\n", GetPrefString(kPrExtractFileFolder)); -} - -/* - * Get the path to the "My Documents" folder. - */ -bool -Preferences::GetMyDocuments(CString* pPath) -{ - LPITEMIDLIST pidl = nil; - LPMALLOC lpMalloc = nil; - HRESULT hr; - bool result = false; - - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return nil; - - hr = SHGetSpecialFolderLocation(nil, CSIDL_PERSONAL, &pidl); - if (FAILED(hr)) { - WMSG0("WARNING: unable to get CSIDL_PERSONAL\n"); - goto bail; - } - - result = (Pidl::GetPath(pidl, pPath) != FALSE); - if (!result) { - WMSG0("WARNING: unable to convert CSIDL_PERSONAL to path\n"); - /* fall through with "result" */ - } - -bail: - lpMalloc->Free(pidl); - lpMalloc->Release(); - return result; -} - -/* - * Determine the type of compression to use as a default, based on what this - * version of NufxLib supports. - * - * Note this happens *before* the AppInit call, so we should restrict this to - * things that are version-safe for all of NufxLib v2.x. - */ -int -Preferences::DefaultCompressionType(void) -{ - if (NufxArchive::IsCompressionSupported(kNuThreadFormatLZW2)) - return kNuThreadFormatLZW2; - else - return kNuThreadFormatUncompressed; -} - -/* - * Preference getters and setters. - */ -bool -Preferences::GetPrefBool(PrefNum num) const -{ - if (!ValidateEntry(num, kBool)) - return false; - //return (bool) (fValues[num]); - return (bool) ((long) (fValues[num]) != 0); -} -void -Preferences::SetPrefBool(PrefNum num, bool val) -{ - if (!ValidateEntry(num, kBool)) - return; - fValues[num] = (void*) val; -} -long -Preferences::GetPrefLong(PrefNum num) const -{ - if (!ValidateEntry(num, kLong)) - return -1; - return (long) fValues[num]; -} -void -Preferences::SetPrefLong(PrefNum num, long val) -{ - if (!ValidateEntry(num, kLong)) - return; - fValues[num] = (void*) val; -} -const char* -Preferences::GetPrefString(PrefNum num) const -{ - if (!ValidateEntry(num, kString)) - return nil; - return (const char*) fValues[num]; -} -void -Preferences::SetPrefString(PrefNum num, const char* str) -{ - if (!ValidateEntry(num, kString)) - return; - free(fValues[num]); - if (str == nil) - fValues[num] = nil; - else { - fValues[num] = new char[strlen(str) +1]; - if (fValues[num] != nil) - strcpy((char*)fValues[num], str); - } -} - -/* - * Free storage for any string entries. - */ -void -Preferences::FreeStringValues(void) -{ - int i; - - for (i = 0; i < kPrefNumLastEntry; i++) { - if (fPrefMaps[i].type == kString) { - delete[] fValues[i]; - } - } -} - - -/* - * Do a quick scan of the PrefMaps to identify duplicate, misplaced, and - * missing entries. - */ -void -Preferences::ScanPrefMaps(void) -{ - int i, j; - - /* scan PrefNum */ - for (i = 0; i < kPrefNumLastEntry; i++) { - if (fPrefMaps[i].num != i) { - WMSG2("HEY: PrefMaps[%d] has num=%d\n", i, fPrefMaps[i].num); - ASSERT(false); - break; - } - } - - /* look for duplicate strings */ - for (i = 0; i < kPrefNumLastEntry; i++) { - for (j = i+1; j < kPrefNumLastEntry; j++) { - if (fPrefMaps[i].registryKey == nil || - fPrefMaps[j].registryKey == nil) - { - continue; - } - if (strcasecmp(fPrefMaps[i].registryKey, - fPrefMaps[j].registryKey) == 0 && - strcasecmp(fPrefMaps[i].registrySection, - fPrefMaps[j].registrySection) == 0) - { - WMSG4("HEY: PrefMaps[%d] and [%d] both have '%s'/'%s'\n", - i, j, fPrefMaps[i].registrySection, - fPrefMaps[i].registryKey); - ASSERT(false); - break; - } - } - } -} - -/* - * Load preferences from the registry. - */ -int -Preferences::LoadFromRegistry(void) -{ - CString sval; - bool bval; - long lval; - - WMSG0("Loading preferences from registry\n"); - - fColumnLayout.LoadFromRegistry(kColumnSect); - - int i; - for (i = 0; i < kPrefNumLastRegistry; i++) { - if (fPrefMaps[i].registryKey == nil) - continue; - - switch (fPrefMaps[i].type) { - case kBool: - bval = GetPrefBool(fPrefMaps[i].num); - SetPrefBool(fPrefMaps[i].num, - GetBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, bval)); - break; - case kLong: - lval = GetPrefLong(fPrefMaps[i].num); - SetPrefLong(fPrefMaps[i].num, - GetInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, lval)); - break; - case kString: - sval = GetPrefString(fPrefMaps[i].num); - SetPrefString(fPrefMaps[i].num, - GetString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, sval)); - break; - default: - WMSG2("Invalid type %d on num=%d\n", fPrefMaps[i].type, i); - ASSERT(false); - break; - } - } - - return 0; -} - -/* - * Save preferences to the registry. - */ -int -Preferences::SaveToRegistry(void) -{ - WMSG0("Saving preferences to registry\n"); - - fColumnLayout.SaveToRegistry(kColumnSect); - - int i; - for (i = 0; i < kPrefNumLastRegistry; i++) { - if (fPrefMaps[i].registryKey == nil) - continue; - - switch (fPrefMaps[i].type) { - case kBool: - WriteBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefBool(fPrefMaps[i].num)); - break; - case kLong: - WriteInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefLong(fPrefMaps[i].num)); - break; - case kString: - WriteString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefString(fPrefMaps[i].num)); - break; - default: - WMSG2("Invalid type %d on num=%d\n", fPrefMaps[i].type, i); - ASSERT(false); - break; - } - } - - return 0; -} +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Save and restore preferences from the config file. + */ +#include "stdafx.h" +#include "Preferences.h" +#include "NufxArchive.h" +#include "MyApp.h" +#include "../util/UtilLib.h" + +static const char* kDefaultTempPath = "."; + +/* registry section for columns */ +static const char* kColumnSect = _T("columns"); +/* registry section for file add options */ +static const char* kAddSect = _T("add"); +/* registry section for extraction options */ +static const char* kExtractSect = _T("extract"); +/* registry section for view options */ +static const char* kViewSect = _T("view"); +/* registry section for logical/physical volume operations */ +static const char* kVolumeSect = _T("volume"); +/* registry section for file-to-disk options */ +//static const char* kConvDiskSect = _T("conv-disk"); +/* registry section for disk-to-file options */ +static const char* kConvFileSect = _T("conv-file"); +/* registry section for folders */ +static const char* kFolderSect = _T("folders"); +/* registry section for preferences on property pages */ +static const char* kPrefsSect = _T("prefs"); +/* registry section for miscellaneous settings */ +static const char* kMiscSect = _T("misc"); + + +/* + * Map PrefNum to type and registry string. + * + * To make life easier, we require that the PrefNum (first entry) match the + * offset in the table. That way instead of searching for a match we can just + * index into the table. + */ +const Preferences::PrefMap Preferences::fPrefMaps[kPrefNumLastEntry] = { + /**/ { kPrefNumUnknown, kPTNone, nil, nil }, + + { kPrAddIncludeSubFolders, kBool, kAddSect, _T("include-sub-folders") }, + { kPrAddStripFolderNames, kBool, kAddSect, _T("strip-folder-names") }, + { kPrAddOverwriteExisting, kBool, kAddSect, _T("overwrite-existing") }, + { kPrAddTypePreservation, kLong, kAddSect, _T("type-preservation") }, + { kPrAddConvEOL, kLong, kAddSect, _T("conv-eol") }, + +// { kPrExtractPath, kString, kExtractSect, _T("path") }, + { kPrExtractConvEOL, kLong, kExtractSect, _T("conv-eol") }, + { kPrExtractConvHighASCII, kBool, kExtractSect, _T("conv-high-ascii") }, + { kPrExtractIncludeData, kBool, kExtractSect, _T("include-data") }, + { kPrExtractIncludeRsrc, kBool, kExtractSect, _T("include-rsrc") }, + { kPrExtractIncludeDisk, kBool, kExtractSect, _T("include-disk") }, + { kPrExtractEnableReformat, kBool, kExtractSect, _T("enable-reformat") }, + { kPrExtractDiskTo2MG, kBool, kExtractSect, _T("disk-to-2mg") }, + { kPrExtractAddTypePreservation, kBool, kExtractSect, _T("add-type-preservation") }, + { kPrExtractAddExtension, kBool, kExtractSect, _T("add-extension") }, + { kPrExtractStripFolderNames, kBool, kExtractSect, _T("strip-folder-names") }, + { kPrExtractOverwriteExisting, kBool, kExtractSect, _T("overwrite-existing") }, + +// { kPrViewIncludeDataForks, kBool, kViewSect, _T("include-data-forks") }, +// { kPrViewIncludeRsrcForks, kBool, kViewSect, _T("include-rsrc-forks") }, +// { kPrViewIncludeDiskImages, kBool, kViewSect, _T("include-disk-images") }, +// { kPrViewIncludeComments, kBool, kViewSect, _T("include-comments") }, + + { kPrConvFileEmptyFolders, kBool, kConvFileSect, _T("preserve-empty-folders") }, + + { kPrOpenArchiveFolder, kString, kFolderSect, _T("open-archive") }, + { kPrConvertArchiveFolder, kString, kFolderSect, _T("convert-archive") }, + { kPrAddFileFolder, kString, kFolderSect, _T("add-file") }, + { kPrExtractFileFolder, kString, kFolderSect, _T("extract-file") }, + + { kPrVolumeFilter, kLong, kVolumeSect, _T("open-filter") }, + //{ kPrVolumeReadOnly, kBool, kVolumeSect, _T("read-only") }, + + { kPrCassetteAlgorithm, kLong, kVolumeSect, _T("cassette-algorithm") }, + { kPrOpenWAVFolder, kString, kFolderSect, _T("open-wav") }, + + { kPrMimicShrinkIt, kBool, kPrefsSect, _T("mimic-shrinkit") }, + { kPrBadMacSHK, kBool, kPrefsSect, _T("bad-mac-shk") }, + { kPrReduceSHKErrorChecks, kBool, kPrefsSect, _T("reduce-shk-error-checks") }, + { kPrCoerceDOSFilenames, kBool, kPrefsSect, _T("coerce-dos-filenames") }, + { kPrSpacesToUnder, kBool, kPrefsSect, _T("spaces-to-under") }, + { kPrPasteJunkPaths, kBool, kPrefsSect, _T("paste-junk-paths") }, + { kPrBeepOnSuccess, kBool, kPrefsSect, _T("beep-on-success") }, + + { kPrQueryImageFormat, kBool, kPrefsSect, _T("query-image-format") }, + { kPrOpenVolumeRO, kBool, kPrefsSect, _T("open-volume-ro") }, + { kPrOpenVolumePhys0, kBool, kPrefsSect, _T("open-volume-phys0") }, + { kPrProDOSAllowLower, kBool, kPrefsSect, _T("prodos-allow-lower") }, + { kPrProDOSUseSparse, kBool, kPrefsSect, _T("prodos-use-sparse") }, + + { kPrCompressionType, kLong, kPrefsSect, _T("compression-type") }, + + { kPrMaxViewFileSize, kLong, kPrefsSect, _T("max-view-file-size") }, + { kPrNoWrapText, kBool, kPrefsSect, _T("no-wrap-text") }, + + { kPrHighlightHexDump, kBool, kPrefsSect, _T("highlight-hex-dump") }, + { kPrHighlightBASIC, kBool, kPrefsSect, _T("highlight-basic") }, + { kPrConvHiResBlackWhite, kBool, kPrefsSect, _T("conv-hi-res-black-white") }, + { kPrConvDHRAlgorithm, kLong, kPrefsSect, _T("dhr-algorithm") }, + { kPrRelaxGfxTypeCheck, kBool, kPrefsSect, _T("relax-gfx-type-check") }, + { kPrDisasmOneByteBrkCop, kBool, kPrefsSect, _T("disasm-onebytebrkcop") }, + //{ kPrEOLConvRaw, kBool, kPrefsSect, _T("eol-conv-raw") }, + { kPrConvTextEOL_HA, kBool, kPrefsSect, _T("conv-eol-ha") }, + { kPrConvPascalText, kBool, kPrefsSect, _T("conv-pascal-text") }, + { kPrConvPascalCode, kBool, kPrefsSect, _T("conv-pascal-code") }, + { kPrConvCPMText, kBool, kPrefsSect, _T("conv-cpm-text") }, + { kPrConvApplesoft, kBool, kPrefsSect, _T("conv-applesoft") }, + { kPrConvInteger, kBool, kPrefsSect, _T("conv-integer") }, + { kPrConvBusiness, kBool, kPrefsSect, _T("conv-business") }, + { kPrConvGWP, kBool, kPrefsSect, _T("conv-gwp") }, + { kPrConvText8, kBool, kPrefsSect, _T("conv-text8") }, + { kPrConvAWP, kBool, kPrefsSect, _T("conv-awp") }, + { kPrConvADB, kBool, kPrefsSect, _T("conv-adb") }, + { kPrConvASP, kBool, kPrefsSect, _T("conv-asp") }, + { kPrConvSCAssem, kBool, kPrefsSect, _T("conv-scassem") }, + { kPrConvDisasm, kBool, kPrefsSect, _T("conv-disasm") }, + { kPrConvHiRes, kBool, kPrefsSect, _T("conv-hi-res") }, + { kPrConvDHR, kBool, kPrefsSect, _T("conv-dhr") }, + { kPrConvSHR, kBool, kPrefsSect, _T("conv-shr") }, + { kPrConvPrintShop, kBool, kPrefsSect, _T("conv-print-shop") }, + { kPrConvMacPaint, kBool, kPrefsSect, _T("conv-mac-paint") }, + { kPrConvProDOSFolder, kBool, kPrefsSect, _T("conv-prodos-folder") }, + { kPrConvResources, kBool, kPrefsSect, _T("conv-resources") }, + + { kPrTempPath, kString, kPrefsSect, _T("temp-path") }, + { kPrExtViewerExts, kString, kPrefsSect, _T("extviewer-exts") }, + + { kPrLastOpenFilterIndex, kLong, kMiscSect, _T("open-filter-index") }, + + /**/ { kPrefNumLastRegistry, kPTNone, nil, nil }, + + { kPrViewTextTypeFace, kString, nil, nil }, + { kPrViewTextPointSize, kLong, nil, nil }, + { kPrFileViewerWidth, kLong, nil, nil }, + { kPrFileViewerHeight, kLong, nil, nil }, + { kPrDiskImageCreateFormat, kLong, nil, nil }, +}; + +/* + * Constructor. There should be only one Preferences object in the + * application, so this should only be run once. + */ +Preferences::Preferences(void) +{ + WMSG0("Initializing Preferences\n"); + + ScanPrefMaps(); // sanity-check the table + memset(fValues, 0, sizeof(fValues)); + + SetPrefBool(kPrAddIncludeSubFolders, true); + SetPrefBool(kPrAddStripFolderNames, false); + SetPrefBool(kPrAddOverwriteExisting, false); + SetPrefLong(kPrAddTypePreservation, 1); // kPreserveTypes + SetPrefLong(kPrAddConvEOL, 1); // kConvEOLType + + InitFolders(); // set default add/extract folders; overriden by reg + SetPrefLong(kPrExtractConvEOL, 0); // kConvEOLNone + SetPrefBool(kPrExtractConvHighASCII, true); + SetPrefBool(kPrExtractIncludeData, true); + SetPrefBool(kPrExtractIncludeRsrc, false); + SetPrefBool(kPrExtractIncludeDisk, true); + SetPrefBool(kPrExtractEnableReformat, false); + SetPrefBool(kPrExtractDiskTo2MG, false); + SetPrefBool(kPrExtractAddTypePreservation, true); + SetPrefBool(kPrExtractAddExtension, false); + SetPrefBool(kPrExtractStripFolderNames, false); + SetPrefBool(kPrExtractOverwriteExisting, false); + +// SetPrefBool(kPrViewIncludeDataForks, true); +// SetPrefBool(kPrViewIncludeRsrcForks, false); +// SetPrefBool(kPrViewIncludeDiskImages, false); +// SetPrefBool(kPrViewIncludeComments, false); + + SetPrefBool(kPrConvFileEmptyFolders, true); + + // string kPrOpenArchiveFolder + // string kPrAddFileFolder + // string kPrExtractFileFolder + + SetPrefLong(kPrVolumeFilter, 0); + //SetPrefBool(kPrVolumeReadOnly, true); + + SetPrefLong(kPrCassetteAlgorithm, 0); + // string kPrOpenWAVFolder + + SetPrefBool(kPrMimicShrinkIt, false); + SetPrefBool(kPrBadMacSHK, false); + SetPrefBool(kPrReduceSHKErrorChecks, false); + SetPrefBool(kPrCoerceDOSFilenames, false); + SetPrefBool(kPrSpacesToUnder, false); + SetPrefBool(kPrPasteJunkPaths, true); + SetPrefBool(kPrBeepOnSuccess, true); + + SetPrefBool(kPrQueryImageFormat, false); + SetPrefBool(kPrOpenVolumeRO, true); + SetPrefBool(kPrOpenVolumePhys0, false); + SetPrefBool(kPrProDOSAllowLower, false); + SetPrefBool(kPrProDOSUseSparse, true); + + SetPrefLong(kPrCompressionType, DefaultCompressionType()); + + SetPrefLong(kPrMaxViewFileSize, 1024*1024); // 1MB + SetPrefBool(kPrNoWrapText, false); + + SetPrefBool(kPrHighlightHexDump, false); + SetPrefBool(kPrHighlightBASIC, false); + SetPrefBool(kPrConvHiResBlackWhite, false); + SetPrefLong(kPrConvDHRAlgorithm, 1); // latched + SetPrefBool(kPrRelaxGfxTypeCheck, true); + SetPrefBool(kPrDisasmOneByteBrkCop, false); + //SetPrefBool(kPrEOLConvRaw, true); + SetPrefBool(kPrConvTextEOL_HA, true); + SetPrefBool(kPrConvPascalText, true); + SetPrefBool(kPrConvPascalCode, true); + SetPrefBool(kPrConvCPMText, true); + SetPrefBool(kPrConvApplesoft, true); + SetPrefBool(kPrConvInteger, true); + SetPrefBool(kPrConvBusiness, true); + SetPrefBool(kPrConvGWP, true); + SetPrefBool(kPrConvText8, true); + SetPrefBool(kPrConvAWP, true); + SetPrefBool(kPrConvADB, true); + SetPrefBool(kPrConvASP, true); + SetPrefBool(kPrConvSCAssem, true); + SetPrefBool(kPrConvDisasm, true); + SetPrefBool(kPrConvHiRes, true); + SetPrefBool(kPrConvDHR, true); + SetPrefBool(kPrConvSHR, true); + SetPrefBool(kPrConvPrintShop, true); + SetPrefBool(kPrConvMacPaint, true); + SetPrefBool(kPrConvProDOSFolder, true); + SetPrefBool(kPrConvResources, true); + + InitTempPath(); // set default for kPrTempPath + SetPrefString(kPrExtViewerExts, "gif; jpg; jpeg"); + + SetPrefLong(kPrLastOpenFilterIndex, 0); + + SetPrefString(kPrViewTextTypeFace, "Courier New"); + SetPrefLong(kPrViewTextPointSize, 10); + long width = 680; /* exact width for 80-column text */ + long height = 510; /* exact height for file viewer to show IIgs graphic */ + if (GetSystemMetrics(SM_CXSCREEN) < width) + width = GetSystemMetrics(SM_CXSCREEN); + if (GetSystemMetrics(SM_CYSCREEN) < height) + height = GetSystemMetrics(SM_CYSCREEN); // may overlap system bar + //width = 640; height = 480; + SetPrefLong(kPrFileViewerWidth, width); + SetPrefLong(kPrFileViewerHeight, height); + SetPrefLong(kPrDiskImageCreateFormat, -1); +} + + +/* + * ========================================================================== + * ColumnLayout + * ========================================================================== + */ + +/* + * Restore column widths. + */ +void +ColumnLayout::LoadFromRegistry(const char* section) +{ + char numBuf[8]; + int i; + + for (i = 0; i < kNumVisibleColumns; i++) { + sprintf(numBuf, "%d", i); + + fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, + fColumnWidth[i]); + fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, + fColumnWidth[i]); + } + fSortColumn = gMyApp.GetProfileInt(section, _T("sort-column"), fSortColumn); + fAscending = (gMyApp.GetProfileInt(section, _T("ascending"), fAscending) != 0); +} + +/* + * Store column widths. + */ +void +ColumnLayout::SaveToRegistry(const char* section) +{ + char numBuf[8]; + int i; + + for (i = 0; i < kNumVisibleColumns; i++) { + sprintf(numBuf, "%d", i); + + gMyApp.WriteProfileInt(section, numBuf, fColumnWidth[i]); + } + gMyApp.WriteProfileInt(section, _T("sort-column"), fSortColumn); + gMyApp.WriteProfileInt(section, _T("ascending"), fAscending); +} + + +/* + * ========================================================================== + * Preferences + * ========================================================================== + */ + +/* + * Get a default value for the temp path. + */ +void +Preferences::InitTempPath(void) +{ + char buf[MAX_PATH]; + DWORD len; + CString tempPath; + + len = ::GetTempPath(sizeof(buf), buf); + if (len == 0) { + DWORD err = ::GetLastError(); + WMSG1("GetTempPath failed, err=%d\n", err); + tempPath = kDefaultTempPath; + } else if (len >= sizeof(buf)) { + /* sheesh! */ + WMSG1("GetTempPath wants a %d-byte buffer\n", len); + tempPath = kDefaultTempPath; + } else { + tempPath = buf; + } + + PathName path(tempPath); + WMSG1("Temp path is '%s'\n", tempPath); + path.SFNToLFN(); + tempPath = path.GetPathName(); + + WMSG1("Temp path (long form) is '%s'\n", tempPath); + + SetPrefString(kPrTempPath, tempPath); + +// ::GetFullPathName(fTempPath, sizeof(buf), buf, &foo); +// ::SetCurrentDirectory(buf); +// ::GetCurrentDirectory(sizeof(buf2), buf2); +} + +/* + * Set default values for the various folders. + */ +void +Preferences::InitFolders(void) +{ + CString path; + + if (GetMyDocuments(&path)) { + SetPrefString(kPrOpenArchiveFolder, path); + SetPrefString(kPrConvertArchiveFolder, path); + SetPrefString(kPrAddFileFolder, path); + SetPrefString(kPrExtractFileFolder, path); + SetPrefString(kPrOpenWAVFolder, path); + } else { + char buf[MAX_PATH]; + ::GetCurrentDirectory(sizeof(buf), buf); + SetPrefString(kPrOpenArchiveFolder, buf); + SetPrefString(kPrConvertArchiveFolder, buf); + SetPrefString(kPrAddFileFolder, buf); + SetPrefString(kPrExtractFileFolder, buf); + SetPrefString(kPrOpenWAVFolder, buf); + } + + WMSG1("Default folder is '%s'\n", GetPrefString(kPrExtractFileFolder)); +} + +/* + * Get the path to the "My Documents" folder. + */ +bool +Preferences::GetMyDocuments(CString* pPath) +{ + LPITEMIDLIST pidl = nil; + LPMALLOC lpMalloc = nil; + HRESULT hr; + bool result = false; + + hr = ::SHGetMalloc(&lpMalloc); + if (FAILED(hr)) + return nil; + + hr = SHGetSpecialFolderLocation(nil, CSIDL_PERSONAL, &pidl); + if (FAILED(hr)) { + WMSG0("WARNING: unable to get CSIDL_PERSONAL\n"); + goto bail; + } + + result = (Pidl::GetPath(pidl, pPath) != FALSE); + if (!result) { + WMSG0("WARNING: unable to convert CSIDL_PERSONAL to path\n"); + /* fall through with "result" */ + } + +bail: + lpMalloc->Free(pidl); + lpMalloc->Release(); + return result; +} + +/* + * Determine the type of compression to use as a default, based on what this + * version of NufxLib supports. + * + * Note this happens *before* the AppInit call, so we should restrict this to + * things that are version-safe for all of NufxLib v2.x. + */ +int +Preferences::DefaultCompressionType(void) +{ + if (NufxArchive::IsCompressionSupported(kNuThreadFormatLZW2)) + return kNuThreadFormatLZW2; + else + return kNuThreadFormatUncompressed; +} + +/* + * Preference getters and setters. + */ +bool +Preferences::GetPrefBool(PrefNum num) const +{ + if (!ValidateEntry(num, kBool)) + return false; + //return (bool) (fValues[num]); + return (bool) ((long) (fValues[num]) != 0); +} +void +Preferences::SetPrefBool(PrefNum num, bool val) +{ + if (!ValidateEntry(num, kBool)) + return; + fValues[num] = (void*) val; +} +long +Preferences::GetPrefLong(PrefNum num) const +{ + if (!ValidateEntry(num, kLong)) + return -1; + return (long) fValues[num]; +} +void +Preferences::SetPrefLong(PrefNum num, long val) +{ + if (!ValidateEntry(num, kLong)) + return; + fValues[num] = (void*) val; +} +const char* +Preferences::GetPrefString(PrefNum num) const +{ + if (!ValidateEntry(num, kString)) + return nil; + return (const char*) fValues[num]; +} +void +Preferences::SetPrefString(PrefNum num, const char* str) +{ + if (!ValidateEntry(num, kString)) + return; + free(fValues[num]); + if (str == nil) + fValues[num] = nil; + else { + fValues[num] = new char[strlen(str) +1]; + if (fValues[num] != nil) + strcpy((char*)fValues[num], str); + } +} + +/* + * Free storage for any string entries. + */ +void +Preferences::FreeStringValues(void) +{ + int i; + + for (i = 0; i < kPrefNumLastEntry; i++) { + if (fPrefMaps[i].type == kString) { + delete[] fValues[i]; + } + } +} + + +/* + * Do a quick scan of the PrefMaps to identify duplicate, misplaced, and + * missing entries. + */ +void +Preferences::ScanPrefMaps(void) +{ + int i, j; + + /* scan PrefNum */ + for (i = 0; i < kPrefNumLastEntry; i++) { + if (fPrefMaps[i].num != i) { + WMSG2("HEY: PrefMaps[%d] has num=%d\n", i, fPrefMaps[i].num); + ASSERT(false); + break; + } + } + + /* look for duplicate strings */ + for (i = 0; i < kPrefNumLastEntry; i++) { + for (j = i+1; j < kPrefNumLastEntry; j++) { + if (fPrefMaps[i].registryKey == nil || + fPrefMaps[j].registryKey == nil) + { + continue; + } + if (strcasecmp(fPrefMaps[i].registryKey, + fPrefMaps[j].registryKey) == 0 && + strcasecmp(fPrefMaps[i].registrySection, + fPrefMaps[j].registrySection) == 0) + { + WMSG4("HEY: PrefMaps[%d] and [%d] both have '%s'/'%s'\n", + i, j, fPrefMaps[i].registrySection, + fPrefMaps[i].registryKey); + ASSERT(false); + break; + } + } + } +} + +/* + * Load preferences from the registry. + */ +int +Preferences::LoadFromRegistry(void) +{ + CString sval; + bool bval; + long lval; + + WMSG0("Loading preferences from registry\n"); + + fColumnLayout.LoadFromRegistry(kColumnSect); + + int i; + for (i = 0; i < kPrefNumLastRegistry; i++) { + if (fPrefMaps[i].registryKey == nil) + continue; + + switch (fPrefMaps[i].type) { + case kBool: + bval = GetPrefBool(fPrefMaps[i].num); + SetPrefBool(fPrefMaps[i].num, + GetBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, bval)); + break; + case kLong: + lval = GetPrefLong(fPrefMaps[i].num); + SetPrefLong(fPrefMaps[i].num, + GetInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, lval)); + break; + case kString: + sval = GetPrefString(fPrefMaps[i].num); + SetPrefString(fPrefMaps[i].num, + GetString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, sval)); + break; + default: + WMSG2("Invalid type %d on num=%d\n", fPrefMaps[i].type, i); + ASSERT(false); + break; + } + } + + return 0; +} + +/* + * Save preferences to the registry. + */ +int +Preferences::SaveToRegistry(void) +{ + WMSG0("Saving preferences to registry\n"); + + fColumnLayout.SaveToRegistry(kColumnSect); + + int i; + for (i = 0; i < kPrefNumLastRegistry; i++) { + if (fPrefMaps[i].registryKey == nil) + continue; + + switch (fPrefMaps[i].type) { + case kBool: + WriteBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, + GetPrefBool(fPrefMaps[i].num)); + break; + case kLong: + WriteInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, + GetPrefLong(fPrefMaps[i].num)); + break; + case kString: + WriteString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, + GetPrefString(fPrefMaps[i].num)); + break; + default: + WMSG2("Invalid type %d on num=%d\n", fPrefMaps[i].type, i); + ASSERT(false); + break; + } + } + + return 0; +} diff --git a/app/Preferences.h b/app/Preferences.h index 490ef55..360c476 100644 --- a/app/Preferences.h +++ b/app/Preferences.h @@ -1,298 +1,299 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Keep track of user preferences. - * - * How to add a new preference item: - * - Add an entry to the PrefNum enum, below. - * - Add a corresponding entry to Preferences::fPrefMaps, adding a new - * section to the registry if appropriate. - * - Add a default value to Preferences::Preferences. If not specified, - * strings will be nil and numeric values will be zero. - */ -#ifndef __PREFERENCES__ -#define __PREFERENCES__ - -#include "MyApp.h" - -class ContentList; - -/* - * Number of visible columns. (We no longer have "invisible" columns, so the - * name is somewhat misleading.) - * - * This is used widely. Update with care. - */ -const int kNumVisibleColumns = 9; - -/* - * Used to save & restore column layout and sorting preferences for - * the ContentList class. - */ -class ColumnLayout { -public: - ColumnLayout(void) { - for (int i = 0; i < kNumVisibleColumns; i++) - fColumnWidth[i] = kWidthDefaulted; - fSortColumn = kNumVisibleColumns; // means "use original order" - fAscending = true; - } - ~ColumnLayout(void) {} - - void LoadFromRegistry(const char* section); - void SaveToRegistry(const char* section); - - int GetColumnWidth(int col) const { - ASSERT(col >= 0 && col < kNumVisibleColumns); - return fColumnWidth[col]; - } - void SetColumnWidth(int col, int width) { - ASSERT(col >= 0 && col < kNumVisibleColumns); - ASSERT(width >= 0 || width == kWidthDefaulted); - fColumnWidth[col] = width; - } - - int GetSortColumn(void) const { return fSortColumn; } - void SetSortColumn(int col) { - ASSERT(col >= 0 && col <= kNumVisibleColumns); - fSortColumn = col; - } - bool GetAscending(void) const { return fAscending; } - void SetAscending(bool val) { fAscending = val; } - - /* column width value used to flag "defaulted" status */ - enum { kWidthDefaulted = -1 }; - /* minimium width of column 0 (pathname) */ - enum { kMinCol0Width = 50 }; - -private: - // Create a dummy list control to get default column widths. - void DetermineDefaultWidths(ContentList* pList); - - int fColumnWidth[kNumVisibleColumns]; - int fSortColumn; - bool fAscending; -}; - - -/* - * Preferences type enumeration. - * - * This is logically part of the Preferences object, but it's annoying to - * have to specify the scope resolution operator everywhere. - */ -typedef enum { - /**/ kPrefNumUnknown = 0, - -/* these are saved in the registry */ - - // sticky settings for add file options - kPrAddIncludeSubFolders, // bool - kPrAddStripFolderNames, // bool - kPrAddOverwriteExisting, // bool - kPrAddTypePreservation, // long - kPrAddConvEOL, // long - - // sticky settings for file extraction - //kPrExtractPath, // string - kPrExtractConvEOL, // long - kPrExtractConvHighASCII, // bool - kPrExtractIncludeData, // bool - kPrExtractIncludeRsrc, // bool - kPrExtractIncludeDisk, // bool - kPrExtractEnableReformat, // bool - kPrExtractDiskTo2MG, // bool - kPrExtractAddTypePreservation, // bool - kPrExtractAddExtension, // bool - kPrExtractStripFolderNames, // bool - kPrExtractOverwriteExisting, // bool - -// // view file options -// kPrViewIncludeDataForks, // bool -// kPrViewIncludeRsrcForks, // bool -// kPrViewIncludeDiskImages, // bool -// kPrViewIncludeComments, // bool - - // convert disk image to file archive - //kPrConvFileConvDOSText, // bool - //kPrConvFileConvPascalText, // bool - kPrConvFileEmptyFolders, // bool - - // folders for CFileDialog initialization - kPrOpenArchiveFolder, // string - kPrConvertArchiveFolder, // string - kPrAddFileFolder, // string - kPrExtractFileFolder, // string - - // logical/physical volume prefs - kPrVolumeFilter, // long - //kPrVolumeReadOnly, // bool - - // cassette import/export prefs - kPrCassetteAlgorithm, // long - kPrOpenWAVFolder, // string - - // items from the Preferences propertypages (must be saved/restored) - kPrMimicShrinkIt, // bool - kPrBadMacSHK, // bool - kPrReduceSHKErrorChecks, // bool - kPrCoerceDOSFilenames, // bool - kPrSpacesToUnder, // bool - kPrPasteJunkPaths, // bool - kPrBeepOnSuccess, // bool - - kPrQueryImageFormat, // bool - kPrOpenVolumeRO, // bool - kPrOpenVolumePhys0, // bool - kPrProDOSAllowLower, // bool - kPrProDOSUseSparse, // bool - - kPrCompressionType, // long - - kPrMaxViewFileSize, // long - kPrNoWrapText, // bool - - kPrHighlightHexDump, // bool - kPrHighlightBASIC, // bool - kPrConvHiResBlackWhite, // bool - kPrConvDHRAlgorithm, // long - kPrRelaxGfxTypeCheck, // bool - kPrDisasmOneByteBrkCop, // bool - //kPrEOLConvRaw, // bool - kPrConvTextEOL_HA, // bool - kPrConvPascalText, // bool - kPrConvPascalCode, // bool - kPrConvCPMText, // bool - kPrConvApplesoft, // bool - kPrConvInteger, // bool - kPrConvGWP, // bool - kPrConvText8, // bool - kPrConvAWP, // bool - kPrConvADB, // bool - kPrConvASP, // bool - kPrConvSCAssem, // bool - kPrConvDisasm, // bool - kPrConvHiRes, // bool - kPrConvDHR, // bool - kPrConvSHR, // bool - kPrConvPrintShop, // bool - kPrConvMacPaint, // bool - kPrConvProDOSFolder, // bool - kPrConvResources, // bool - - kPrTempPath, // string - kPrExtViewerExts, // string - - // open file dialog - kPrLastOpenFilterIndex, // long - - /**/ kPrefNumLastRegistry, -/* these are temporary settings, not saved in the registry */ - - // sticky settings for internal file viewer (ViewFilesDialog) - kPrViewTextTypeFace, // string - kPrViewTextPointSize, // long - kPrFileViewerWidth, // long - kPrFileViewerHeight, // long - - // sticky setting for disk image creator - kPrDiskImageCreateFormat, // long - - /**/ kPrefNumLastEntry -} PrefNum; - - -/* - * Container for preferences. - */ -class Preferences { -public: - Preferences(void); - ~Preferences(void) { - FreeStringValues(); - } - - // Load/save preferences from/to registry. - int LoadFromRegistry(void); - int SaveToRegistry(void); - - ColumnLayout* GetColumnLayout(void) { return &fColumnLayout; } - //bool GetShowToolbarText(void) const { return fShowToolbarText; } - //void SetShowToolbarText(bool val) { fShowToolbarText = val; } - - bool GetPrefBool(PrefNum num) const; - void SetPrefBool(PrefNum num, bool val); - long GetPrefLong(PrefNum num) const; - void SetPrefLong(PrefNum num, long val); - const char* GetPrefString(PrefNum num) const; - void SetPrefString(PrefNum num, const char* str); - - -private: - void InitTempPath(void); - void InitFolders(void); - bool GetMyDocuments(CString* pPath); - int DefaultCompressionType(void); - void FreeStringValues(void); - - /* - * Internal data structure used to manage preferences. - */ - typedef enum { kPTNone, kBool, kLong, kString } PrefType; - typedef struct PrefMap { - PrefNum num; - PrefType type; - const char* registrySection; - const char* registryKey; - } PrefMap; - static const PrefMap fPrefMaps[kPrefNumLastEntry]; - void ScanPrefMaps(void); - - // this holds the actual values - void* fValues[kPrefNumLastEntry]; - - // verify that the entry exists and has the expected type - bool ValidateEntry(PrefNum num, PrefType type) const { - if (num <= kPrefNumUnknown || num >= kPrefNumLastEntry) { - ASSERT(false); - return false; - } - if (fPrefMaps[num].type != type) { - ASSERT(false); - return false; - } - return true; - } - - // column widths for ContentList - ColumnLayout fColumnLayout; - - /* - * Registry helpers. - */ - UINT GetInt(const char* section, const char* key, int dflt) { - return gMyApp.GetProfileInt(section, key, dflt); - } - bool GetBool(const char* section, const char* key, bool dflt) { - return (gMyApp.GetProfileInt(section, key, dflt) != 0); - } - CString GetString(const char* section, const char* key, - const char* dflt) - { - return gMyApp.GetProfileString(section, key, dflt); - } - BOOL WriteInt(const char* section, const char* key, int value) { - return gMyApp.WriteProfileInt(section, key, value); - } - BOOL WriteBool(const char* section, const char* key, bool value) { - return gMyApp.WriteProfileInt(section, key, value); - } - BOOL WriteString(const char* section, const char* key, const char* value) { - return gMyApp.WriteProfileString(section, key, value); - } -}; - +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Keep track of user preferences. + * + * How to add a new preference item: + * - Add an entry to the PrefNum enum, below. + * - Add a corresponding entry to Preferences::fPrefMaps, adding a new + * section to the registry if appropriate. + * - Add a default value to Preferences::Preferences. If not specified, + * strings will be nil and numeric values will be zero. + */ +#ifndef __PREFERENCES__ +#define __PREFERENCES__ + +#include "MyApp.h" + +class ContentList; + +/* + * Number of visible columns. (We no longer have "invisible" columns, so the + * name is somewhat misleading.) + * + * This is used widely. Update with care. + */ +const int kNumVisibleColumns = 9; + +/* + * Used to save & restore column layout and sorting preferences for + * the ContentList class. + */ +class ColumnLayout { +public: + ColumnLayout(void) { + for (int i = 0; i < kNumVisibleColumns; i++) + fColumnWidth[i] = kWidthDefaulted; + fSortColumn = kNumVisibleColumns; // means "use original order" + fAscending = true; + } + ~ColumnLayout(void) {} + + void LoadFromRegistry(const char* section); + void SaveToRegistry(const char* section); + + int GetColumnWidth(int col) const { + ASSERT(col >= 0 && col < kNumVisibleColumns); + return fColumnWidth[col]; + } + void SetColumnWidth(int col, int width) { + ASSERT(col >= 0 && col < kNumVisibleColumns); + ASSERT(width >= 0 || width == kWidthDefaulted); + fColumnWidth[col] = width; + } + + int GetSortColumn(void) const { return fSortColumn; } + void SetSortColumn(int col) { + ASSERT(col >= 0 && col <= kNumVisibleColumns); + fSortColumn = col; + } + bool GetAscending(void) const { return fAscending; } + void SetAscending(bool val) { fAscending = val; } + + /* column width value used to flag "defaulted" status */ + enum { kWidthDefaulted = -1 }; + /* minimium width of column 0 (pathname) */ + enum { kMinCol0Width = 50 }; + +private: + // Create a dummy list control to get default column widths. + void DetermineDefaultWidths(ContentList* pList); + + int fColumnWidth[kNumVisibleColumns]; + int fSortColumn; + bool fAscending; +}; + + +/* + * Preferences type enumeration. + * + * This is logically part of the Preferences object, but it's annoying to + * have to specify the scope resolution operator everywhere. + */ +typedef enum { + /**/ kPrefNumUnknown = 0, + +/* these are saved in the registry */ + + // sticky settings for add file options + kPrAddIncludeSubFolders, // bool + kPrAddStripFolderNames, // bool + kPrAddOverwriteExisting, // bool + kPrAddTypePreservation, // long + kPrAddConvEOL, // long + + // sticky settings for file extraction + //kPrExtractPath, // string + kPrExtractConvEOL, // long + kPrExtractConvHighASCII, // bool + kPrExtractIncludeData, // bool + kPrExtractIncludeRsrc, // bool + kPrExtractIncludeDisk, // bool + kPrExtractEnableReformat, // bool + kPrExtractDiskTo2MG, // bool + kPrExtractAddTypePreservation, // bool + kPrExtractAddExtension, // bool + kPrExtractStripFolderNames, // bool + kPrExtractOverwriteExisting, // bool + +// // view file options +// kPrViewIncludeDataForks, // bool +// kPrViewIncludeRsrcForks, // bool +// kPrViewIncludeDiskImages, // bool +// kPrViewIncludeComments, // bool + + // convert disk image to file archive + //kPrConvFileConvDOSText, // bool + //kPrConvFileConvPascalText, // bool + kPrConvFileEmptyFolders, // bool + + // folders for CFileDialog initialization + kPrOpenArchiveFolder, // string + kPrConvertArchiveFolder, // string + kPrAddFileFolder, // string + kPrExtractFileFolder, // string + + // logical/physical volume prefs + kPrVolumeFilter, // long + //kPrVolumeReadOnly, // bool + + // cassette import/export prefs + kPrCassetteAlgorithm, // long + kPrOpenWAVFolder, // string + + // items from the Preferences propertypages (must be saved/restored) + kPrMimicShrinkIt, // bool + kPrBadMacSHK, // bool + kPrReduceSHKErrorChecks, // bool + kPrCoerceDOSFilenames, // bool + kPrSpacesToUnder, // bool + kPrPasteJunkPaths, // bool + kPrBeepOnSuccess, // bool + + kPrQueryImageFormat, // bool + kPrOpenVolumeRO, // bool + kPrOpenVolumePhys0, // bool + kPrProDOSAllowLower, // bool + kPrProDOSUseSparse, // bool + + kPrCompressionType, // long + + kPrMaxViewFileSize, // long + kPrNoWrapText, // bool + + kPrHighlightHexDump, // bool + kPrHighlightBASIC, // bool + kPrConvHiResBlackWhite, // bool + kPrConvDHRAlgorithm, // long + kPrRelaxGfxTypeCheck, // bool + kPrDisasmOneByteBrkCop, // bool + //kPrEOLConvRaw, // bool + kPrConvTextEOL_HA, // bool + kPrConvPascalText, // bool + kPrConvPascalCode, // bool + kPrConvCPMText, // bool + kPrConvApplesoft, // bool + kPrConvInteger, // bool + kPrConvBusiness, // bool + kPrConvGWP, // bool + kPrConvText8, // bool + kPrConvAWP, // bool + kPrConvADB, // bool + kPrConvASP, // bool + kPrConvSCAssem, // bool + kPrConvDisasm, // bool + kPrConvHiRes, // bool + kPrConvDHR, // bool + kPrConvSHR, // bool + kPrConvPrintShop, // bool + kPrConvMacPaint, // bool + kPrConvProDOSFolder, // bool + kPrConvResources, // bool + + kPrTempPath, // string + kPrExtViewerExts, // string + + // open file dialog + kPrLastOpenFilterIndex, // long + + /**/ kPrefNumLastRegistry, +/* these are temporary settings, not saved in the registry */ + + // sticky settings for internal file viewer (ViewFilesDialog) + kPrViewTextTypeFace, // string + kPrViewTextPointSize, // long + kPrFileViewerWidth, // long + kPrFileViewerHeight, // long + + // sticky setting for disk image creator + kPrDiskImageCreateFormat, // long + + /**/ kPrefNumLastEntry +} PrefNum; + + +/* + * Container for preferences. + */ +class Preferences { +public: + Preferences(void); + ~Preferences(void) { + FreeStringValues(); + } + + // Load/save preferences from/to registry. + int LoadFromRegistry(void); + int SaveToRegistry(void); + + ColumnLayout* GetColumnLayout(void) { return &fColumnLayout; } + //bool GetShowToolbarText(void) const { return fShowToolbarText; } + //void SetShowToolbarText(bool val) { fShowToolbarText = val; } + + bool GetPrefBool(PrefNum num) const; + void SetPrefBool(PrefNum num, bool val); + long GetPrefLong(PrefNum num) const; + void SetPrefLong(PrefNum num, long val); + const char* GetPrefString(PrefNum num) const; + void SetPrefString(PrefNum num, const char* str); + + +private: + void InitTempPath(void); + void InitFolders(void); + bool GetMyDocuments(CString* pPath); + int DefaultCompressionType(void); + void FreeStringValues(void); + + /* + * Internal data structure used to manage preferences. + */ + typedef enum { kPTNone, kBool, kLong, kString } PrefType; + typedef struct PrefMap { + PrefNum num; + PrefType type; + const char* registrySection; + const char* registryKey; + } PrefMap; + static const PrefMap fPrefMaps[kPrefNumLastEntry]; + void ScanPrefMaps(void); + + // this holds the actual values + void* fValues[kPrefNumLastEntry]; + + // verify that the entry exists and has the expected type + bool ValidateEntry(PrefNum num, PrefType type) const { + if (num <= kPrefNumUnknown || num >= kPrefNumLastEntry) { + ASSERT(false); + return false; + } + if (fPrefMaps[num].type != type) { + ASSERT(false); + return false; + } + return true; + } + + // column widths for ContentList + ColumnLayout fColumnLayout; + + /* + * Registry helpers. + */ + UINT GetInt(const char* section, const char* key, int dflt) { + return gMyApp.GetProfileInt(section, key, dflt); + } + bool GetBool(const char* section, const char* key, bool dflt) { + return (gMyApp.GetProfileInt(section, key, dflt) != 0); + } + CString GetString(const char* section, const char* key, + const char* dflt) + { + return gMyApp.GetProfileString(section, key, dflt); + } + BOOL WriteInt(const char* section, const char* key, int value) { + return gMyApp.WriteProfileInt(section, key, value); + } + BOOL WriteBool(const char* section, const char* key, bool value) { + return gMyApp.WriteProfileInt(section, key, value); + } + BOOL WriteString(const char* section, const char* key, const char* value) { + return gMyApp.WriteProfileString(section, key, value); + } +}; + #endif /*__PREFERENCES__*/ \ No newline at end of file diff --git a/app/PrefsDialog.h b/app/PrefsDialog.h index 2b3cc8e..fa91084 100644 --- a/app/PrefsDialog.h +++ b/app/PrefsDialog.h @@ -1,241 +1,242 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Classes to support the Preferences property pages. - */ -#ifndef __PREFSDIALOG__ -#define __PREFSDIALOG__ - -#include "Preferences.h" -#include "../util/UtilLib.h" -#include "resource.h" - -/* - * The "general" page, which controls how we display information to the user. - */ -class PrefsGeneralPage : public CPropertyPage -{ -public: - PrefsGeneralPage(void) : - CPropertyPage(IDD_PREF_GENERAL), - fReady(false), - fMimicShrinkIt(FALSE), - fBadMacSHK(FALSE), - fReduceSHKErrorChecks(FALSE), - fCoerceDOSFilenames(FALSE), - fSpacesToUnder(FALSE), - fDefaultsPushed(FALSE), - fOurAssociations(nil) - {} - virtual ~PrefsGeneralPage(void) { - delete[] fOurAssociations; - } - - bool fReady; - - // fields on this page - BOOL fColumn[kNumVisibleColumns]; - BOOL fMimicShrinkIt; - BOOL fBadMacSHK; - BOOL fReduceSHKErrorChecks; - BOOL fCoerceDOSFilenames; - BOOL fSpacesToUnder; - BOOL fPasteJunkPaths; - BOOL fBeepOnSuccess; - BOOL fDefaultsPushed; - - // initialized if we opened the file associations edit page - bool* fOurAssociations; - -protected: - virtual void DoDataExchange(CDataExchange* pDX); - - afx_msg void OnChange(void); - afx_msg void OnChangeRange(UINT); - afx_msg void OnDefaults(void); - afx_msg void OnAssociations(void); - afx_msg LONG OnHelp(UINT wParam, LONG lParam); - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "disk image" page, for selecting disk image preferences. - */ -class PrefsDiskImagePage : public CPropertyPage -{ -public: - PrefsDiskImagePage(void) : - CPropertyPage(IDD_PREF_DISKIMAGE), - fReady(false), - fQueryImageFormat(FALSE), - fOpenVolumeRO(FALSE), - fProDOSAllowLower(FALSE), - fProDOSUseSparse(FALSE) - {} - - bool fReady; - - BOOL fQueryImageFormat; - BOOL fOpenVolumeRO; - BOOL fOpenVolumePhys0; - BOOL fProDOSAllowLower; - BOOL fProDOSUseSparse; - -protected: - virtual BOOL OnInitDialog(void); - virtual void DoDataExchange(CDataExchange* pDX); - - afx_msg void OnChange(void); - //afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelp(UINT wParam, LONG lParam); - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "compression" page, which lets the user choose a default compression - * method. - */ -class PrefsCompressionPage : public CPropertyPage -{ -public: - PrefsCompressionPage(void) : - CPropertyPage(IDD_PREF_COMPRESSION), fReady(false) - {} - - bool fReady; - - int fCompressType; // radio button index - -protected: - virtual BOOL OnInitDialog(void); - virtual void DoDataExchange(CDataExchange* pDX); - - afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelp(UINT wParam, LONG lParam); - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); - -private: - void DisableWnd(int id); - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "fview" page, for selecting preferences for the internal file viewer. - */ -class PrefsFviewPage : public CPropertyPage -{ -public: - PrefsFviewPage(void) : - CPropertyPage(IDD_PREF_FVIEW), fReady(false) - {} - bool fReady; - - BOOL fEOLConvRaw; - BOOL fNoWrapText; - BOOL fHighlightHexDump; - BOOL fHighlightBASIC; - BOOL fConvDisasmOneByteBrkCop; - BOOL fConvHiResBlackWhite; - int fConvDHRAlgorithm; // drop list - - BOOL fConvTextEOL_HA; - BOOL fConvCPMText; - BOOL fConvPascalText; - BOOL fConvPascalCode; - BOOL fConvApplesoft; - BOOL fConvInteger; - BOOL fConvGWP; - BOOL fConvText8; - BOOL fConvAWP; - BOOL fConvADB; - BOOL fConvASP; - BOOL fConvSCAssem; - BOOL fConvDisasm; - - BOOL fConvHiRes; - BOOL fConvDHR; - BOOL fConvSHR; - BOOL fConvPrintShop; - BOOL fConvMacPaint; - BOOL fConvProDOSFolder; - BOOL fConvResources; - BOOL fRelaxGfxTypeCheck; - - UINT fMaxViewFileSizeKB; - -protected: - virtual BOOL OnInitDialog(void); - virtual void DoDataExchange(CDataExchange* pDX); - - afx_msg void OnChange(void); - afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelp(UINT wParam, LONG lParam); - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "compression" page, which lets the user choose a default compression - * method for NuFX archives. - */ -class PrefsFilesPage : public CPropertyPage -{ -public: - PrefsFilesPage(void) : - CPropertyPage(IDD_PREF_FILES), fReady(false) - {} - - bool fReady; - - CString fTempPath; - CString fExtViewerExts; - -protected: - virtual BOOL OnInitDialog(void); - virtual void DoDataExchange(CDataExchange* pDX); - - afx_msg void OnChange(void); - afx_msg void OnChooseFolder(void); - afx_msg LONG OnHelp(UINT wParam, LONG lParam); - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); - - MyBitmapButton fChooseFolderButton; - - DECLARE_MESSAGE_MAP() -}; - - -/* - * Property sheet that wraps around the preferences pages. - */ -class PrefsSheet : public CPropertySheet -{ -public: - PrefsSheet(CWnd* pParentWnd = NULL); - - PrefsGeneralPage fGeneralPage; - PrefsDiskImagePage fDiskImagePage; - PrefsCompressionPage fCompressionPage; - PrefsFviewPage fFviewPage; - PrefsFilesPage fFilesPage; - -protected: - BOOL OnNcCreate(LPCREATESTRUCT cs); - - afx_msg void OnApplyNow(); - LONG OnHelp(UINT wParam, LONG lParam); - void OnIDHelp(void); - - DECLARE_MESSAGE_MAP() -}; - +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Classes to support the Preferences property pages. + */ +#ifndef __PREFSDIALOG__ +#define __PREFSDIALOG__ + +#include "Preferences.h" +#include "../util/UtilLib.h" +#include "resource.h" + +/* + * The "general" page, which controls how we display information to the user. + */ +class PrefsGeneralPage : public CPropertyPage +{ +public: + PrefsGeneralPage(void) : + CPropertyPage(IDD_PREF_GENERAL), + fReady(false), + fMimicShrinkIt(FALSE), + fBadMacSHK(FALSE), + fReduceSHKErrorChecks(FALSE), + fCoerceDOSFilenames(FALSE), + fSpacesToUnder(FALSE), + fDefaultsPushed(FALSE), + fOurAssociations(nil) + {} + virtual ~PrefsGeneralPage(void) { + delete[] fOurAssociations; + } + + bool fReady; + + // fields on this page + BOOL fColumn[kNumVisibleColumns]; + BOOL fMimicShrinkIt; + BOOL fBadMacSHK; + BOOL fReduceSHKErrorChecks; + BOOL fCoerceDOSFilenames; + BOOL fSpacesToUnder; + BOOL fPasteJunkPaths; + BOOL fBeepOnSuccess; + BOOL fDefaultsPushed; + + // initialized if we opened the file associations edit page + bool* fOurAssociations; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); + + afx_msg void OnChange(void); + afx_msg void OnChangeRange(UINT); + afx_msg void OnDefaults(void); + afx_msg void OnAssociations(void); + afx_msg LONG OnHelp(UINT wParam, LONG lParam); + afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); + + DECLARE_MESSAGE_MAP() +}; + +/* + * The "disk image" page, for selecting disk image preferences. + */ +class PrefsDiskImagePage : public CPropertyPage +{ +public: + PrefsDiskImagePage(void) : + CPropertyPage(IDD_PREF_DISKIMAGE), + fReady(false), + fQueryImageFormat(FALSE), + fOpenVolumeRO(FALSE), + fProDOSAllowLower(FALSE), + fProDOSUseSparse(FALSE) + {} + + bool fReady; + + BOOL fQueryImageFormat; + BOOL fOpenVolumeRO; + BOOL fOpenVolumePhys0; + BOOL fProDOSAllowLower; + BOOL fProDOSUseSparse; + +protected: + virtual BOOL OnInitDialog(void); + virtual void DoDataExchange(CDataExchange* pDX); + + afx_msg void OnChange(void); + //afx_msg void OnChangeRange(UINT); + afx_msg LONG OnHelp(UINT wParam, LONG lParam); + afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); + + DECLARE_MESSAGE_MAP() +}; + +/* + * The "compression" page, which lets the user choose a default compression + * method. + */ +class PrefsCompressionPage : public CPropertyPage +{ +public: + PrefsCompressionPage(void) : + CPropertyPage(IDD_PREF_COMPRESSION), fReady(false) + {} + + bool fReady; + + int fCompressType; // radio button index + +protected: + virtual BOOL OnInitDialog(void); + virtual void DoDataExchange(CDataExchange* pDX); + + afx_msg void OnChangeRange(UINT); + afx_msg LONG OnHelp(UINT wParam, LONG lParam); + afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); + +private: + void DisableWnd(int id); + + DECLARE_MESSAGE_MAP() +}; + +/* + * The "fview" page, for selecting preferences for the internal file viewer. + */ +class PrefsFviewPage : public CPropertyPage +{ +public: + PrefsFviewPage(void) : + CPropertyPage(IDD_PREF_FVIEW), fReady(false) + {} + bool fReady; + + BOOL fEOLConvRaw; + BOOL fNoWrapText; + BOOL fHighlightHexDump; + BOOL fHighlightBASIC; + BOOL fConvDisasmOneByteBrkCop; + BOOL fConvHiResBlackWhite; + int fConvDHRAlgorithm; // drop list + + BOOL fConvTextEOL_HA; + BOOL fConvCPMText; + BOOL fConvPascalText; + BOOL fConvPascalCode; + BOOL fConvApplesoft; + BOOL fConvInteger; + BOOL fConvBusiness; + BOOL fConvGWP; + BOOL fConvText8; + BOOL fConvAWP; + BOOL fConvADB; + BOOL fConvASP; + BOOL fConvSCAssem; + BOOL fConvDisasm; + + BOOL fConvHiRes; + BOOL fConvDHR; + BOOL fConvSHR; + BOOL fConvPrintShop; + BOOL fConvMacPaint; + BOOL fConvProDOSFolder; + BOOL fConvResources; + BOOL fRelaxGfxTypeCheck; + + UINT fMaxViewFileSizeKB; + +protected: + virtual BOOL OnInitDialog(void); + virtual void DoDataExchange(CDataExchange* pDX); + + afx_msg void OnChange(void); + afx_msg void OnChangeRange(UINT); + afx_msg LONG OnHelp(UINT wParam, LONG lParam); + afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); + + DECLARE_MESSAGE_MAP() +}; + +/* + * The "compression" page, which lets the user choose a default compression + * method for NuFX archives. + */ +class PrefsFilesPage : public CPropertyPage +{ +public: + PrefsFilesPage(void) : + CPropertyPage(IDD_PREF_FILES), fReady(false) + {} + + bool fReady; + + CString fTempPath; + CString fExtViewerExts; + +protected: + virtual BOOL OnInitDialog(void); + virtual void DoDataExchange(CDataExchange* pDX); + + afx_msg void OnChange(void); + afx_msg void OnChooseFolder(void); + afx_msg LONG OnHelp(UINT wParam, LONG lParam); + afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam); + + MyBitmapButton fChooseFolderButton; + + DECLARE_MESSAGE_MAP() +}; + + +/* + * Property sheet that wraps around the preferences pages. + */ +class PrefsSheet : public CPropertySheet +{ +public: + PrefsSheet(CWnd* pParentWnd = NULL); + + PrefsGeneralPage fGeneralPage; + PrefsDiskImagePage fDiskImagePage; + PrefsCompressionPage fCompressionPage; + PrefsFviewPage fFviewPage; + PrefsFilesPage fFilesPage; + +protected: + BOOL OnNcCreate(LPCREATESTRUCT cs); + + afx_msg void OnApplyNow(); + LONG OnHelp(UINT wParam, LONG lParam); + void OnIDHelp(void); + + DECLARE_MESSAGE_MAP() +}; + #endif /*__PREFSDIALOG__*/ \ No newline at end of file diff --git a/reformat/BASIC.cpp b/reformat/BASIC.cpp index 0595c17..e3add5d 100644 --- a/reformat/BASIC.cpp +++ b/reformat/BASIC.cpp @@ -1,525 +1,752 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Convert BASIC programs. - */ -#include "StdAfx.h" -#include "BASIC.h" - - -/* our color map */ -const ReformatText::TextColor kDefaultColor = ReformatText::kColorDarkGrey; -const ReformatText::TextColor kLineNumColor = ReformatText::kColorDarkGrey; -const ReformatText::TextColor kKeywordColor = ReformatText::kColorBlack; -const ReformatText::TextColor kCommentColor = ReformatText::kColorMediumGreen; -const ReformatText::TextColor kStringColor = ReformatText::kColorMediumBlue; -const ReformatText::TextColor kColonColor = ReformatText::kColorRed; - //kColorMediumGrey; - -/* - * =========================================================================== - * Applesoft BASIC - * =========================================================================== - */ - -/* - * Applesoft BASIC file format: - * - * <16-bit file length> [DOS 3.3 only; not visible here] - * ... - * - * - * Each line consists of: - * <16-bit address of next line (relative to $800)> - * <16-bit line number, usually 0-63999> - * ... - * - * - * All values are little-endian. Numbers are stored as characters. - */ - -/* - * Decide whether or not we want to handle this file. - */ -void -ReformatApplesoft::Examine(ReformatHolder* pHolder) -{ - ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; - - if (pHolder->GetFileType() == kTypeBAS) - applies = ReformatHolder::kApplicProbably; - - pHolder->SetApplic(ReformatHolder::kReformatApplesoft, applies, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); - pHolder->SetApplic(ReformatHolder::kReformatApplesoft_Hilite, applies, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); - - if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) - pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft_Hilite); - else - pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft); -} - -/* - * Values from 128 to 234 are tokens in Applesoft. Values from 235 to 255 - * show up as error messages. The goal here is to produce values that are - * human-readable and/or EXECable, so no attempt has been made to display - * the error values. - */ -static const char gApplesoftTokens[128*8] = { - "END\0 FOR\0 NEXT\0 DATA\0 INPUT\0 DEL\0 DIM\0 READ\0 " - "GR\0 TEXT\0 PR#\0 IN#\0 CALL\0 PLOT\0 HLIN\0 VLIN\0 " - "HGR2\0 HGR\0 HCOLOR=\0HPLOT\0 DRAW\0 XDRAW\0 HTAB\0 HOME\0 " - "ROT=\0 SCALE=\0 SHLOAD\0 TRACE\0 NOTRACE\0NORMAL\0 INVERSE\0FLASH\0 " - "COLOR=\0 POP\0 VTAB\0 HIMEM:\0 LOMEM:\0 ONERR\0 RESUME\0 RECALL\0 " - "STORE\0 SPEED=\0 LET\0 GOTO\0 RUN\0 IF\0 RESTORE\0&\0 " - "GOSUB\0 RETURN\0 REM\0 STOP\0 ON\0 WAIT\0 LOAD\0 SAVE\0 " - "DEF\0 POKE\0 PRINT\0 CONT\0 LIST\0 CLEAR\0 GET\0 NEW\0 " - "TAB(\0 TO\0 FN\0 SPC(\0 THEN\0 AT\0 NOT\0 STEP\0 " - "+\0 -\0 *\0 /\0 ^\0 AND\0 OR\0 >\0 " - "=\0 <\0 SGN\0 INT\0 ABS\0 USR\0 FRE\0 SCRN(\0 " - "PDL\0 POS\0 SQR\0 RND\0 LOG\0 EXP\0 COS\0 SIN\0 " - "TAN\0 ATN\0 PEEK\0 LEN\0 STR$\0 VAL\0 ASC\0 CHR$\0 " - "LEFT$\0 RIGHT$\0 MID$\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " - "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " - "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " -}; - -/* - * Make table available. - */ -/*static*/ const char* ReformatApplesoft::GetApplesoftTokens(void) -{ - return gApplesoftTokens; -} - - -/* - * Reformat an Applesoft BASIC program into a text format that mimics the - * output of the "LIST" command (with POKE 33,73 to suppress CRs). - */ -int -ReformatApplesoft::Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput) -{ - const unsigned char* srcPtr = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - long length = srcLen; - int retval = -1; - - if (srcLen > 65536) - fUseRTF = false; - if (fUseRTF) { - if (id != ReformatHolder::kReformatApplesoft_Hilite) - fUseRTF = false; - } - - RTFBegin(kRTFFlagColorTable); - - /* - * Make sure there's enough here to get started. We want to return an - * "okay" result because we want this treated like a reformatted empty - * BASIC program rather than a non-Applesoft file. - */ - if (length < 2) { - WMSG0(" BAS truncated?\n"); - //fExpBuf.CreateWorkBuf(); - BufPrintf("\r\n"); - goto done; - } - - while (length > 0) { - unsigned short nextAddr; - unsigned short lineNum; - bool inQuote = false; - bool inRem = false; - - nextAddr = Read16(&srcPtr, &length); - if (nextAddr == 0) { - /* ProDOS sticks an extra byte on the end? */ - if (length > 1) { - WMSG1(" BAS ended early; len is %d\n", length); - } - break; - } - - /* print line number */ - RTFSetColor(kLineNumColor); - lineNum = Read16(&srcPtr, &length); - BufPrintf(" %u ", lineNum); - - RTFSetColor(kDefaultColor); - - /* print a line */ - while (*srcPtr != 0 && length > 0) { - if (*srcPtr & 0x80) { - /* token */ - //RTFBoldOn(); - RTFSetColor(kKeywordColor); - BufPrintf(" %s ", &gApplesoftTokens[((*srcPtr) & 0x7f) << 3]); - //RTFBoldOff(); - RTFSetColor(kDefaultColor); - - if (*srcPtr == 0xb2) { - // REM -- do rest of line in green - RTFSetColor(kCommentColor); - inRem = true; - } - } else { - /* simple chracter */ - if (fUseRTF) { - if (*srcPtr == '"' && !inRem) { - if (!inQuote) { - RTFSetColor(kStringColor); - RTFPrintChar(*srcPtr); - } else { - RTFPrintChar(*srcPtr); - RTFSetColor(kDefaultColor); - } - inQuote = !inQuote; - } else if (*srcPtr == ':' && !inRem && !inQuote) { - RTFSetColor(kColonColor); - RTFPrintChar(*srcPtr); - RTFSetColor(kDefaultColor); - } else { - RTFPrintChar(*srcPtr); - } - } else { - BufPrintf("%c", *srcPtr); - } - } - - srcPtr++; - length--; - } - - if (inQuote || inRem) - RTFSetColor(kDefaultColor); - inQuote = inRem = false; - - srcPtr++; - length--; - - if (!length) { - WMSG0(" BAS truncated in mid-line\n"); - break; - } - - RTFNewPara(); - } - -done: - RTFEnd(); - - SetResultBuffer(pOutput); - retval = 0; - -//bail: - return retval; -} - - -/* - * =========================================================================== - * Integer BASIC - * =========================================================================== - */ -#include "Asm.h" - -/* - * Integer BASIC file format (thanks to Paul Schlyter, pausch at saaf.se): - * - * <16-bit file length> [DOS 3.3 only; not visible here] - * ... - * - * Each line consists of: - * <8-bit line length> - * <16-bit line number> - * ... - * - * - * Each line is a stream of bytes: - * $01: end of line - * $12-$7f: language token - * $b0-b9 ('0'-'9'): start of integer constant (first byte has no meaning?) - * next 16 bits hold the number - * $c1-da ('A'-'Z'): start of a variable name; ends on value <0x80 - * next several bytes hold the name - * - * Most of the first $11 tokens are illegal except as part of an integer - * constant, which just means that you can't type them into a program from - * the keyboard. If you POKE them in manually, things like "himem:" and - * "del" will work. - * - * $ba-$c0 and $db-$ff are illegal except in a string constant. - * - * There is no end-of-file marker. - */ - -/* - * Tokens. - */ -static const char* const gIntegerTokens[128] = { - "HIMEM:", "", "_ ", ":", - "LOAD ", "SAVE ", "CON ", "RUN ", - "RUN ", "DEL ", ",", "NEW ", - "CLR ", "AUTO ", ",", "MAN ", - "HIMEM:", "LOMEM:", "+", "-", - "*", "/", "=", "#", - ">=", ">", "<=", "<>", - "<", "AND ", "OR ", "MOD ", - - "^ ", "+", "(", ",", - "THEN ", "THEN ", ",", ",", - "\"", "\"", "(", "!", - "!", "(", "PEEK ", "RND ", - "SGN ", "ABS ", "PDL ", "RNDX ", - "(", "+", "-", "NOT ", - "(", "=", "#", "LEN(", - "ASC( ", "SCRN( ", ",", "(", - - "$", "$", "(", ",", - ",", ";", ";", ";", - ",", ",", ",", "TEXT ", - "GR ", "CALL ", "DIM ", "DIM ", - "TAB ", "END ", "INPUT ", "INPUT ", - "INPUT ", "FOR ", "=", "TO ", - "STEP ", "NEXT ", ",", "RETURN ", - "GOSUB ", "REM ", "LET ", "GOTO ", - - "IF ", "PRINT ", "PRINT ", "PRINT ", - "POKE ", ",", "COLOR= ", "PLOT ", - ",", "HLIN ", ",", "AT ", - "VLIN ", ",", "AT ", "VTAB ", - "=", "=", ")", ")", - "LIST ", ",", "LIST ", "POP ", - "NODSP ", "NODSP ", "NOTRACE ", "DSP ", - "DSP ", "TRACE ", "PR# ", "IN# " -}; - -/* - * Decide whether or not we want to handle this file. - */ -void -ReformatInteger::Examine(ReformatHolder* pHolder) -{ - ReformatHolder::ReformatApplies apply = ReformatHolder::kApplicNot; - if (pHolder->GetFileType() == kTypeINT) { - if (ReformatSCAssem::IsSCAssem(pHolder)) { - /* possibly intbasic */ - apply = ReformatHolder::kApplicMaybe; - } else if (ReformatLISA3::IsLISA(pHolder)) { - /* possibly intbasic */ - apply = ReformatHolder::kApplicMaybe; - } else if (ReformatLISA4::IsLISA(pHolder)) { - /* possibly intbasic */ - apply = ReformatHolder::kApplicMaybe; - } else { - /* definitely intbasic */ - apply = ReformatHolder::kApplicYes; - } - } else { - /* not intbasic */ - apply = ReformatHolder::kApplicNot; - } - - //apply = ReformatHolder::kApplicMaybe; // DEBUG DEBUG - - pHolder->SetApplic(ReformatHolder::kReformatInteger, apply, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); - pHolder->SetApplic(ReformatHolder::kReformatInteger_Hilite, apply, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); - - if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) - pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger_Hilite); - else - pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger); -} - -/* - * Reformat an Integer BASIC program into a text format that mimics the - * output of the "LIST" command. - */ -int -ReformatInteger::Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput) -{ - const unsigned char* srcPtr = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - long length = srcLen; - //unsigned short val16; - int retval = -1; - - //srcPtr += 0xff0; //0x228e; - //srcLen -= 0xff0; //0x228e; - - if (srcLen > 65536) - fUseRTF = false; - if (fUseRTF) { - if (id != ReformatHolder::kReformatInteger_Hilite) - fUseRTF = false; - } - - RTFBegin(kRTFFlagColorTable); - - /* - * Make sure there's enough here to get started. We want to return an - * "okay" result because we want this treated like a reformatted empty - * BASIC program rather than a non-Integer file. - */ - if (length < 2) { - WMSG0(" INT truncated?\n"); - BufPrintf("\r\n"); - goto done; - } - - while (length > 0) { - unsigned char lineLen; - unsigned short lineNum; - bool trailingSpace; - bool newTrailingSpace = false; - - /* pull the length byte, which we sanity-check */ - lineLen = *srcPtr++; - length--; - if (lineLen == 0) { - WMSG0(" INT found zero-length line?\n"); - break; - } - - /* line number */ - RTFSetColor(kLineNumColor); - lineNum = Read16(&srcPtr, &length); - BufPrintf("%5u ", lineNum); - RTFSetColor(kDefaultColor); - - trailingSpace = true; - while (*srcPtr != 0x01 && length > 0) { - if (*srcPtr == 0x28) { - /* start of quoted text */ - RTFSetColor(kStringColor); - BufPrintf("\""); - length--; - while (*++srcPtr != 0x29 && length > 0) { - /* escape chars, but let Ctrl-D and Ctrl-G through */ - if (fUseRTF && *srcPtr != 0x84 && *srcPtr != 0x87) - RTFPrintChar(*srcPtr & 0x7f); - else - BufPrintf("%c", *srcPtr & 0x7f); - length--; - } - if (*srcPtr != 0x29) { - WMSG0(" INT ended while in a string constant\n"); - break; - } - BufPrintf("\""); - RTFSetColor(kDefaultColor); - srcPtr++; - length--; - } else if (*srcPtr == 0x5d) { - /* start of REM statement, run to EOL */ - //RTFBoldOn(); - RTFSetColor(kKeywordColor); - BufPrintf("%sREM ", trailingSpace ? "" : " "); - //RTFBoldOff(); - RTFSetColor(kCommentColor); - length--; - while (*++srcPtr != 0x01) { - if (fUseRTF) - RTFPrintChar(*srcPtr & 0x7f); - else - BufPrintf("%c", *srcPtr & 0x7f); - length--; - } - RTFSetColor(kDefaultColor); - if (*srcPtr != 0x01) { - WMSG0(" INT ended while in a REM statement\n"); - break; - } - } else if (*srcPtr >= 0xb0 && *srcPtr <= 0xb9) { - /* start of integer constant */ - srcPtr++; - length--; - if (length < 2) { - WMSG0(" INT ended while in an integer constant\n"); - break; - } - int val; - val = Read16(&srcPtr, &length); - BufPrintf("%d", val); - } else if (*srcPtr >= 0xc1 && *srcPtr <= 0xda) { - /* start of variable name */ - while ((*srcPtr >= 0xc1 && *srcPtr <= 0xda) || - (*srcPtr >= 0xb0 && *srcPtr <= 0xb9)) - { - /* note no RTF-escaped chars in this range */ - BufPrintf("%c", *srcPtr & 0x7f); - srcPtr++; - length--; - } - } else if (*srcPtr < 0x80) { - /* found a token; try to get the whitespace right */ - /* (maybe should've left whitespace on the ends of tokens - that are always followed by whitespace...?) */ - const char* token; - token = gIntegerTokens[*srcPtr]; - //RTFBoldOn(); - if (*srcPtr == 0x03) // colon - RTFSetColor(kColonColor); - else - RTFSetColor(kKeywordColor); - if (token[0] >= 0x21 && token[0] <= 0x3f || *srcPtr < 0x12) { - /* does not need leading space */ - BufPrintf("%s", token); - } else { - /* needs leading space; combine with prev if it exists */ - if (trailingSpace) - BufPrintf("%s", token); - else - BufPrintf(" %s", token); - } - if (token[strlen(token)-1] == ' ') - newTrailingSpace = true; - //RTFBoldOff(); - RTFSetColor(kDefaultColor); - srcPtr++; - length--; - } else { - /* should not happen */ - WMSG2(" INT unexpected value 0x%02x at byte %d\n", - *srcPtr, srcPtr - pHolder->GetSourceBuf(part)); - - /* skip past it and keep trying */ - srcPtr++; - length--; - } - - trailingSpace = newTrailingSpace; - newTrailingSpace = false; - } /*while line*/ - - /* skip past EOL token */ - if (*srcPtr != 0x01 && length > 0) { - WMSG0("bailing\n"); // must've failed during processing - goto bail; - } - srcPtr++; - length--; - - RTFNewPara(); - } - -done: - RTFEnd(); - - SetResultBuffer(pOutput); - retval = 0; - -bail: - return retval; -} +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Convert BASIC programs. + */ +#include "StdAfx.h" +#include "BASIC.h" + + +/* our color map */ +const ReformatText::TextColor kDefaultColor = ReformatText::kColorDarkGrey; +const ReformatText::TextColor kLineNumColor = ReformatText::kColorDarkGrey; +const ReformatText::TextColor kKeywordColor = ReformatText::kColorBlack; +const ReformatText::TextColor kCommentColor = ReformatText::kColorMediumGreen; +const ReformatText::TextColor kStringColor = ReformatText::kColorMediumBlue; +const ReformatText::TextColor kColonColor = ReformatText::kColorRed; + //kColorMediumGrey; + +/* + * =========================================================================== + * Applesoft BASIC + * =========================================================================== + */ + +/* + * Applesoft BASIC file format: + * + * <16-bit file length> [DOS 3.3 only; not visible here] + * ... + * + * + * Each line consists of: + * <16-bit address of next line (relative to $800)> + * <16-bit line number, usually 0-63999> + * ... + * + * + * All values are little-endian. Numbers are stored as characters. + */ + +/* + * Decide whether or not we want to handle this file. + */ +void +ReformatApplesoft::Examine(ReformatHolder* pHolder) +{ + ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; + + if (pHolder->GetFileType() == kTypeBAS) + applies = ReformatHolder::kApplicProbably; + + pHolder->SetApplic(ReformatHolder::kReformatApplesoft, applies, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + pHolder->SetApplic(ReformatHolder::kReformatApplesoft_Hilite, applies, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + + if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) + pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft_Hilite); + else + pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft); +} + +/* + * Values from 128 to 234 are tokens in Applesoft. Values from 235 to 255 + * show up as error messages. The goal here is to produce values that are + * human-readable and/or EXECable, so no attempt has been made to display + * the error values. + */ +static const char gApplesoftTokens[128*8] = { + "END\0 FOR\0 NEXT\0 DATA\0 INPUT\0 DEL\0 DIM\0 READ\0 " + "GR\0 TEXT\0 PR#\0 IN#\0 CALL\0 PLOT\0 HLIN\0 VLIN\0 " + "HGR2\0 HGR\0 HCOLOR=\0HPLOT\0 DRAW\0 XDRAW\0 HTAB\0 HOME\0 " + "ROT=\0 SCALE=\0 SHLOAD\0 TRACE\0 NOTRACE\0NORMAL\0 INVERSE\0FLASH\0 " + "COLOR=\0 POP\0 VTAB\0 HIMEM:\0 LOMEM:\0 ONERR\0 RESUME\0 RECALL\0 " + "STORE\0 SPEED=\0 LET\0 GOTO\0 RUN\0 IF\0 RESTORE\0&\0 " + "GOSUB\0 RETURN\0 REM\0 STOP\0 ON\0 WAIT\0 LOAD\0 SAVE\0 " + "DEF\0 POKE\0 PRINT\0 CONT\0 LIST\0 CLEAR\0 GET\0 NEW\0 " + "TAB(\0 TO\0 FN\0 SPC(\0 THEN\0 AT\0 NOT\0 STEP\0 " + "+\0 -\0 *\0 /\0 ^\0 AND\0 OR\0 >\0 " + "=\0 <\0 SGN\0 INT\0 ABS\0 USR\0 FRE\0 SCRN(\0 " + "PDL\0 POS\0 SQR\0 RND\0 LOG\0 EXP\0 COS\0 SIN\0 " + "TAN\0 ATN\0 PEEK\0 LEN\0 STR$\0 VAL\0 ASC\0 CHR$\0 " + "LEFT$\0 RIGHT$\0 MID$\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " + "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " + "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " +}; + +/* + * Make table available. + */ +/*static*/ const char* ReformatApplesoft::GetApplesoftTokens(void) +{ + return gApplesoftTokens; +} + + +/* + * Reformat an Applesoft BASIC program into a text format that mimics the + * output of the "LIST" command (with POKE 33,73 to suppress CRs). + */ +int +ReformatApplesoft::Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput) +{ + const unsigned char* srcPtr = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + long length = srcLen; + int retval = -1; + + if (srcLen > 65536) + fUseRTF = false; + if (fUseRTF) { + if (id != ReformatHolder::kReformatApplesoft_Hilite) + fUseRTF = false; + } + + RTFBegin(kRTFFlagColorTable); + + /* + * Make sure there's enough here to get started. We want to return an + * "okay" result because we want this treated like a reformatted empty + * BASIC program rather than a non-Applesoft file. + */ + if (length < 2) { + WMSG0(" BAS truncated?\n"); + //fExpBuf.CreateWorkBuf(); + BufPrintf("\r\n"); + goto done; + } + + while (length > 0) { + unsigned short nextAddr; + unsigned short lineNum; + bool inQuote = false; + bool inRem = false; + + nextAddr = Read16(&srcPtr, &length); + if (nextAddr == 0) { + /* ProDOS sticks an extra byte on the end? */ + if (length > 1) { + WMSG1(" BAS ended early; len is %d\n", length); + } + break; + } + + /* print line number */ + RTFSetColor(kLineNumColor); + lineNum = Read16(&srcPtr, &length); + BufPrintf(" %u ", lineNum); + + RTFSetColor(kDefaultColor); + + /* print a line */ + while (*srcPtr != 0 && length > 0) { + if (*srcPtr & 0x80) { + /* token */ + //RTFBoldOn(); + RTFSetColor(kKeywordColor); + BufPrintf(" %s ", &gApplesoftTokens[((*srcPtr) & 0x7f) << 3]); + //RTFBoldOff(); + RTFSetColor(kDefaultColor); + + if (*srcPtr == 0xb2) { + // REM -- do rest of line in green + RTFSetColor(kCommentColor); + inRem = true; + } + } else { + /* simple chracter */ + if (fUseRTF) { + if (*srcPtr == '"' && !inRem) { + if (!inQuote) { + RTFSetColor(kStringColor); + RTFPrintChar(*srcPtr); + } else { + RTFPrintChar(*srcPtr); + RTFSetColor(kDefaultColor); + } + inQuote = !inQuote; + } else if (*srcPtr == ':' && !inRem && !inQuote) { + RTFSetColor(kColonColor); + RTFPrintChar(*srcPtr); + RTFSetColor(kDefaultColor); + } else { + RTFPrintChar(*srcPtr); + } + } else { + BufPrintf("%c", *srcPtr); + } + } + + srcPtr++; + length--; + } + + if (inQuote || inRem) + RTFSetColor(kDefaultColor); + inQuote = inRem = false; + + srcPtr++; + length--; + + if (!length) { + WMSG0(" BAS truncated in mid-line\n"); + break; + } + + RTFNewPara(); + } + +done: + RTFEnd(); + + SetResultBuffer(pOutput); + retval = 0; + +//bail: + return retval; +} + + +/* + * =========================================================================== + * Integer BASIC + * =========================================================================== + */ +#include "Asm.h" + +/* + * Integer BASIC file format (thanks to Paul Schlyter, pausch at saaf.se): + * + * <16-bit file length> [DOS 3.3 only; not visible here] + * ... + * + * Each line consists of: + * <8-bit line length> + * <16-bit line number> + * ... + * + * + * Each line is a stream of bytes: + * $01: end of line + * $12-$7f: language token + * $b0-b9 ('0'-'9'): start of integer constant (first byte has no meaning?) + * next 16 bits hold the number + * $c1-da ('A'-'Z'): start of a variable name; ends on value <0x80 + * next several bytes hold the name + * + * Most of the first $11 tokens are illegal except as part of an integer + * constant, which just means that you can't type them into a program from + * the keyboard. If you POKE them in manually, things like "himem:" and + * "del" will work. + * + * $ba-$c0 and $db-$ff are illegal except in a string constant. + * + * There is no end-of-file marker. + */ + +/* + * Tokens. + */ +static const char* const gIntegerTokens[128] = { + "HIMEM:", "", "_ ", ":", + "LOAD ", "SAVE ", "CON ", "RUN ", + "RUN ", "DEL ", ",", "NEW ", + "CLR ", "AUTO ", ",", "MAN ", + "HIMEM:", "LOMEM:", "+", "-", + "*", "/", "=", "#", + ">=", ">", "<=", "<>", + "<", "AND ", "OR ", "MOD ", + + "^ ", "+", "(", ",", + "THEN ", "THEN ", ",", ",", + "\"", "\"", "(", "!", + "!", "(", "PEEK ", "RND ", + "SGN ", "ABS ", "PDL ", "RNDX ", + "(", "+", "-", "NOT ", + "(", "=", "#", "LEN(", + "ASC( ", "SCRN( ", ",", "(", + + "$", "$", "(", ",", + ",", ";", ";", ";", + ",", ",", ",", "TEXT ", + "GR ", "CALL ", "DIM ", "DIM ", + "TAB ", "END ", "INPUT ", "INPUT ", + "INPUT ", "FOR ", "=", "TO ", + "STEP ", "NEXT ", ",", "RETURN ", + "GOSUB ", "REM ", "LET ", "GOTO ", + + "IF ", "PRINT ", "PRINT ", "PRINT ", + "POKE ", ",", "COLOR= ", "PLOT ", + ",", "HLIN ", ",", "AT ", + "VLIN ", ",", "AT ", "VTAB ", + "=", "=", ")", ")", + "LIST ", ",", "LIST ", "POP ", + "NODSP ", "NODSP ", "NOTRACE ", "DSP ", + "DSP ", "TRACE ", "PR# ", "IN# " +}; + +/* + * Decide whether or not we want to handle this file. + */ +void +ReformatInteger::Examine(ReformatHolder* pHolder) +{ + ReformatHolder::ReformatApplies apply = ReformatHolder::kApplicNot; + if (pHolder->GetFileType() == kTypeINT) { + if (ReformatSCAssem::IsSCAssem(pHolder)) { + /* possibly intbasic */ + apply = ReformatHolder::kApplicMaybe; + } else if (ReformatLISA3::IsLISA(pHolder)) { + /* possibly intbasic */ + apply = ReformatHolder::kApplicMaybe; + } else if (ReformatLISA4::IsLISA(pHolder)) { + /* possibly intbasic */ + apply = ReformatHolder::kApplicMaybe; + } else { + /* definitely intbasic */ + apply = ReformatHolder::kApplicYes; + } + } else { + /* not intbasic */ + apply = ReformatHolder::kApplicNot; + } + + //apply = ReformatHolder::kApplicMaybe; // DEBUG DEBUG + + pHolder->SetApplic(ReformatHolder::kReformatInteger, apply, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + pHolder->SetApplic(ReformatHolder::kReformatInteger_Hilite, apply, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + + if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) + pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger_Hilite); + else + pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger); +} + +/* + * Reformat an Integer BASIC program into a text format that mimics the + * output of the "LIST" command. + */ +int +ReformatInteger::Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput) +{ + const unsigned char* srcPtr = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + long length = srcLen; + //unsigned short val16; + int retval = -1; + + //srcPtr += 0xff0; //0x228e; + //srcLen -= 0xff0; //0x228e; + + if (srcLen > 65536) + fUseRTF = false; + if (fUseRTF) { + if (id != ReformatHolder::kReformatInteger_Hilite) + fUseRTF = false; + } + + RTFBegin(kRTFFlagColorTable); + + /* + * Make sure there's enough here to get started. We want to return an + * "okay" result because we want this treated like a reformatted empty + * BASIC program rather than a non-Integer file. + */ + if (length < 2) { + WMSG0(" INT truncated?\n"); + BufPrintf("\r\n"); + goto done; + } + + while (length > 0) { + unsigned char lineLen; + unsigned short lineNum; + bool trailingSpace; + bool newTrailingSpace = false; + + /* pull the length byte, which we sanity-check */ + lineLen = *srcPtr++; + length--; + if (lineLen == 0) { + WMSG0(" INT found zero-length line?\n"); + break; + } + + /* line number */ + RTFSetColor(kLineNumColor); + lineNum = Read16(&srcPtr, &length); + BufPrintf("%5u ", lineNum); + RTFSetColor(kDefaultColor); + + trailingSpace = true; + while (*srcPtr != 0x01 && length > 0) { + if (*srcPtr == 0x28) { + /* start of quoted text */ + RTFSetColor(kStringColor); + BufPrintf("\""); + length--; + while (*++srcPtr != 0x29 && length > 0) { + /* escape chars, but let Ctrl-D and Ctrl-G through */ + if (fUseRTF && *srcPtr != 0x84 && *srcPtr != 0x87) + RTFPrintChar(*srcPtr & 0x7f); + else + BufPrintf("%c", *srcPtr & 0x7f); + length--; + } + if (*srcPtr != 0x29) { + WMSG0(" INT ended while in a string constant\n"); + break; + } + BufPrintf("\""); + RTFSetColor(kDefaultColor); + srcPtr++; + length--; + } else if (*srcPtr == 0x5d) { + /* start of REM statement, run to EOL */ + //RTFBoldOn(); + RTFSetColor(kKeywordColor); + BufPrintf("%sREM ", trailingSpace ? "" : " "); + //RTFBoldOff(); + RTFSetColor(kCommentColor); + length--; + while (*++srcPtr != 0x01) { + if (fUseRTF) + RTFPrintChar(*srcPtr & 0x7f); + else + BufPrintf("%c", *srcPtr & 0x7f); + length--; + } + RTFSetColor(kDefaultColor); + if (*srcPtr != 0x01) { + WMSG0(" INT ended while in a REM statement\n"); + break; + } + } else if (*srcPtr >= 0xb0 && *srcPtr <= 0xb9) { + /* start of integer constant */ + srcPtr++; + length--; + if (length < 2) { + WMSG0(" INT ended while in an integer constant\n"); + break; + } + int val; + val = Read16(&srcPtr, &length); + BufPrintf("%d", val); + } else if (*srcPtr >= 0xc1 && *srcPtr <= 0xda) { + /* start of variable name */ + while ((*srcPtr >= 0xc1 && *srcPtr <= 0xda) || + (*srcPtr >= 0xb0 && *srcPtr <= 0xb9)) + { + /* note no RTF-escaped chars in this range */ + BufPrintf("%c", *srcPtr & 0x7f); + srcPtr++; + length--; + } + } else if (*srcPtr < 0x80) { + /* found a token; try to get the whitespace right */ + /* (maybe should've left whitespace on the ends of tokens + that are always followed by whitespace...?) */ + const char* token; + token = gIntegerTokens[*srcPtr]; + //RTFBoldOn(); + if (*srcPtr == 0x03) // colon + RTFSetColor(kColonColor); + else + RTFSetColor(kKeywordColor); + if (token[0] >= 0x21 && token[0] <= 0x3f || *srcPtr < 0x12) { + /* does not need leading space */ + BufPrintf("%s", token); + } else { + /* needs leading space; combine with prev if it exists */ + if (trailingSpace) + BufPrintf("%s", token); + else + BufPrintf(" %s", token); + } + if (token[strlen(token)-1] == ' ') + newTrailingSpace = true; + //RTFBoldOff(); + RTFSetColor(kDefaultColor); + srcPtr++; + length--; + } else { + /* should not happen */ + WMSG2(" INT unexpected value 0x%02x at byte %d\n", + *srcPtr, srcPtr - pHolder->GetSourceBuf(part)); + + /* skip past it and keep trying */ + srcPtr++; + length--; + } + + trailingSpace = newTrailingSpace; + newTrailingSpace = false; + } /*while line*/ + + /* skip past EOL token */ + if (*srcPtr != 0x01 && length > 0) { + WMSG0("bailing\n"); // must've failed during processing + goto bail; + } + srcPtr++; + length--; + + RTFNewPara(); + } + +done: + RTFEnd(); + + SetResultBuffer(pOutput); + retval = 0; + +bail: + return retval; +} + +/* + * =========================================================================== + * Apple /// Business BASIC + * =========================================================================== + */ + +/* + * Apple /// Business BASIC file format: + * + * <16-bit file length> + * ... + * + * + * Each line consists of: + * <8-bit offset to next line> + * <16-bit line number, usually 0-63999> + * ... + * + * + * All values are little-endian. Numbers are stored as characters. + */ + +/* + * Decide whether or not we want to handle this file. + */ +void +ReformatBusiness::Examine(ReformatHolder* pHolder) +{ + ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; + + if (pHolder->GetFileType() == kTypeBA3) + applies = ReformatHolder::kApplicYes; + + pHolder->SetApplic(ReformatHolder::kReformatBusiness, applies, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + pHolder->SetApplic(ReformatHolder::kReformatBusiness_Hilite, applies, + ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); + + if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) + pHolder->SetApplicPreferred(ReformatHolder::kReformatBusiness_Hilite); + else + pHolder->SetApplicPreferred(ReformatHolder::kReformatBusiness); +} + +/* + * Values from 128 to 234 are tokens in Business BASIC. Values from 235 to 255 + * show up as error messages. The goal here is to produce values that are + * human-readable and/or EXECable, so no attempt has been made to display + * the error values. + */ +static const char gBusinessTokens[128*10] = { + + "END\0 FOR\0 NEXT\0 INPUT\0 OUTPUT\0 DIM\0 READ\0 WRITE\0 " + "OPEN\0 CLOSE\0 *error*\0 TEXT\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 WINDOW\0 INVOKE\0 PERFORM\0 *error*\0 *error*\0 " + "FRE\0 HPOS\0 VPOS\0 ERRLIN\0 ERR\0 KBD\0 EOF\0 TIME$\0 " + "DATE$\0 PREFIX$\0 EXFN.\0 EXFN%.\0 OUTREC\0 INDENT\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 POP\0 HOME\0 *error*\0 " + "SUB$(\0 OFF\0 TRACE\0 NOTRACE\0 NORMAL\0 INVERSE\0 SCALE(\0 RESUME\0 " + "*error*\0 LET\0 GOTO\0 IF\0 RESTORE\0 SWAP\0 GOSUB\0 RETURN\0 " + "REM\0 STOP\0 ON\0 *error*\0 LOAD\0 SAVE\0 DELETE\0 RUN\0 " + "RENAME\0 LOCK\0 UNLOCK\0 CREATE\0 EXEC\0 CHAIN\0 *error*\0 *error*\0 " + "*error*\0 CATALOG\0 *error*\0 *error*\0 DATA\0 IMAGE\0 CAT\0 DEF\0 " + "*error*\0 PRINT\0 DEL\0 ELSE\0 CONT\0 LIST\0 CLEAR\0 GET\0 " + "NEW\0 TAB\0 TO\0 SPC(\0 USING\0 THEN\0 *error*\0 MOD\0 " + "STEP\0 AND\0 OR\0 EXTENSION\0DIV\0 *error*\0 FN\0 NOT\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 tf7\0 " +}; + +static const char gExtendedBusinessTokens[128*10] = { + "TAB(\0 TO\0 SPC(\0 USING\0 THEN\0 *error*\0 MOD\0 STEP\0 " + "AND\0 OR\0 EXTENSION\0DIV\0 *error*\0 FN\0 NOT\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 AS\0 SGN(\0 INT(\0 ABS(\0 " + "*error*\0 TYP(\0 REC(\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 PDL(\0 BUTTON(\0 SQR(\0 " + "RND(\0 LOG(\0 EXP(\0 COS(\0 SIN(\0 TAN(\0 ATN(\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 STR$(\0 HEX$(\0 CHR$(\0 LEN(\0 VAL(\0 " + "ASC(\0 TEN(\0 *error*\0 *error*\0 CONV(\0 CONV&(\0 CONV$(\0 CONV%(\0 " + "LEFT$(\0 RIGHT$(\0 MID$(\0 INSTR$(\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " + "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 " +}; + +/* + * Reformat an Apple /// Business BASIC program into a text format that + * mimics the output of the "LIST" command. + */ +int +ReformatBusiness::Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput) +{ + const unsigned char* srcPtr = pHolder->GetSourceBuf(part); + long srcLen = pHolder->GetSourceLen(part); + long length = srcLen; + int retval = -1; + + if (srcLen > 65536) + fUseRTF = false; + if (fUseRTF) { + if (id != ReformatHolder::kReformatBusiness_Hilite) + fUseRTF = false; + } + + RTFBegin(kRTFFlagColorTable); + + /* + * Make sure there's enough here to get started. We want to return an + * "okay" result because we want this treated like a reformatted empty + * BASIC program rather than a non-Applesoft file. + */ + if (length < 2) { + WMSG0(" BAS truncated?\n"); + //fExpBuf.CreateWorkBuf(); + BufPrintf("\r\n"); + goto done; + } + + unsigned short fileLength; + fileLength = Read16(&srcPtr, &length); + WMSG1(" BA3 internal file length is: %d\n", fileLength); + + while (length > 0) { + unsigned short increment; + unsigned short extendedToken; + unsigned short lineNum; + bool inQuote = false; + bool inRem = false; + + increment = Read8(&srcPtr, &length); + WMSG1(" BA3 increment to next line is: %d\n", increment); + if (increment == 0) { + /* ProDOS sticks an extra byte on the end? */ + if (length > 1) { + WMSG1(" BAS ended early; len is %d\n", length); + } + break; + } + + /* print line number */ + RTFSetColor(kLineNumColor); + lineNum = Read16(&srcPtr, &length); + WMSG1(" BA3 line number: %d\n", lineNum); + BufPrintf(" %u ", lineNum); + + RTFSetColor(kDefaultColor); + + /* print a line */ + while (*srcPtr != 0 && length > 0) { + if (*srcPtr & 0x80) { + /* token */ + //RTFBoldOn(); + RTFSetColor(kKeywordColor); + if (((*srcPtr) & 0x7f) == 0x7f) + { + extendedToken = Read8(&srcPtr, &length); + BufPrintf(" %s ", &gExtendedBusinessTokens[((*srcPtr) & 0x7f) * 10]); + } + else + BufPrintf(" %s ", &gBusinessTokens[((*srcPtr) & 0x7f) * 10]); + //RTFBoldOff(); + RTFSetColor(kDefaultColor); + + if (*srcPtr == 0xc0) { + // REM -- do rest of line in green + RTFSetColor(kCommentColor); + inRem = true; + } + } else { + /* simple chracter */ + if (fUseRTF) { + if (*srcPtr == '"' && !inRem) { + if (!inQuote) { + RTFSetColor(kStringColor); + RTFPrintChar(*srcPtr); + } else { + RTFPrintChar(*srcPtr); + RTFSetColor(kDefaultColor); + } + inQuote = !inQuote; + } else if (*srcPtr == ':' && !inRem && !inQuote) { + RTFSetColor(kColonColor); + RTFPrintChar(*srcPtr); + RTFSetColor(kDefaultColor); + } else { + RTFPrintChar(*srcPtr); + } + } else { + BufPrintf("%c", *srcPtr); + } + } + + srcPtr++; + length--; + } + + if (inQuote || inRem) + RTFSetColor(kDefaultColor); + inQuote = inRem = false; + + srcPtr++; + length--; + + if (!length) { + WMSG0(" BAS truncated in mid-line\n"); + break; + } + + RTFNewPara(); + } + +done: + RTFEnd(); + + SetResultBuffer(pOutput); + retval = 0; + +//bail: + return retval; +} diff --git a/reformat/BASIC.h b/reformat/BASIC.h index 287d72a..38e847c 100644 --- a/reformat/BASIC.h +++ b/reformat/BASIC.h @@ -1,46 +1,64 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Reformat BASIC programs. - */ -#ifndef __LR_BASIC__ -#define __LR_BASIC__ - -#include "ReformatBase.h" - -/* - * Reformat an Applesoft BASIC program into readable text. - */ -class ReformatApplesoft : public ReformatText { -public: - ReformatApplesoft(void) {} - virtual ~ReformatApplesoft(void) {} - - virtual void Examine(ReformatHolder* pHolder); - virtual int Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput); - - /* share our token list with others */ - enum { kTokenLen = 8, kTokenCount = 107 }; - static const char* GetApplesoftTokens(void); -}; - -/* - * Reformat an Integer BASIC program into readable text. - */ -class ReformatInteger : public ReformatText { -public: - ReformatInteger(void) {} - virtual ~ReformatInteger(void) {} - - virtual void Examine(ReformatHolder* pHolder); - virtual int Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput); -}; - +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Reformat BASIC programs. + */ +#ifndef __LR_BASIC__ +#define __LR_BASIC__ + +#include "ReformatBase.h" + +/* + * Reformat an Applesoft BASIC program into readable text. + */ +class ReformatApplesoft : public ReformatText { +public: + ReformatApplesoft(void) {} + virtual ~ReformatApplesoft(void) {} + + virtual void Examine(ReformatHolder* pHolder); + virtual int Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput); + + /* share our token list with others */ + enum { kTokenLen = 8, kTokenCount = 107 }; + static const char* GetApplesoftTokens(void); +}; + +/* + * Reformat an Integer BASIC program into readable text. + */ +class ReformatInteger : public ReformatText { +public: + ReformatInteger(void) {} + virtual ~ReformatInteger(void) {} + + virtual void Examine(ReformatHolder* pHolder); + virtual int Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput); +}; + +/* + * Reformat an Apple /// Business BASIC program into readable text. + */ +class ReformatBusiness : public ReformatText { +public: + ReformatBusiness(void) {} + virtual ~ReformatBusiness(void) {} + + virtual void Examine(ReformatHolder* pHolder); + virtual int Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput); + + /* share our token list with others - but this won't really work in its current form... */ + enum { kTokenLen = 10, kTokenCount = 107 }; + static const char* GetBusinessTokens(void); +}; + #endif /*__LR_BASIC__*/ \ No newline at end of file diff --git a/reformat/Reformat.cpp b/reformat/Reformat.cpp index eea6a19..3561736 100644 --- a/reformat/Reformat.cpp +++ b/reformat/Reformat.cpp @@ -1,586 +1,594 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Common code for reformatters. - */ -#include "StdAfx.h" -#include "Reformat.h" - -#include "ReformatBase.h" -#include "AppleWorks.h" -#include "Asm.h" -#include "AWGS.h" -#include "Basic.h" -#include "CPMFiles.h" -#include "Directory.h" -#include "Disasm.h" -#include "DoubleHiRes.h" -#include "HiRes.h" -#include "MacPaint.h" -#include "PascalFiles.h" -#include "PrintShop.h" -#include "ResourceFork.h" -#include "Simple.h" -#include "SuperHiRes.h" -#include "Teach.h" -#include "Text8.h" - -/* - * Create an instance of the class identified by "id". - */ -/*static*/ Reformat* -ReformatHolder::GetReformatInstance(ReformatID id) -{ - Reformat* pReformat = nil; - - switch (id) { - case kReformatTextEOL_HA: pReformat = new ReformatEOL_HA; break; - case kReformatRaw: pReformat = new ReformatRaw; break; - case kReformatHexDump: pReformat = new ReformatHexDump; break; - case kReformatResourceFork: pReformat = new ReformatResourceFork; break; - - case kReformatProDOSDirectory: pReformat = new ReformatDirectory; break; - case kReformatPascalText: pReformat = new ReformatPascalText; break; - case kReformatPascalCode: pReformat = new ReformatPascalCode; break; - case kReformatCPMText: pReformat = new ReformatCPMText; break; - case kReformatApplesoft: pReformat = new ReformatApplesoft; break; - case kReformatApplesoft_Hilite: pReformat = new ReformatApplesoft; break; - case kReformatInteger: pReformat = new ReformatInteger; break; - case kReformatInteger_Hilite: pReformat = new ReformatInteger; break; - case kReformatSCAssem: pReformat = new ReformatSCAssem; break; - case kReformatMerlin: pReformat = new ReformatMerlin; break; - case kReformatLISA2: pReformat = new ReformatLISA2; break; - case kReformatLISA3: pReformat = new ReformatLISA3; break; - case kReformatLISA4: pReformat = new ReformatLISA4; break; - case kReformatMonitor8: pReformat = new ReformatDisasm8; break; - case kReformatDisasmMerlin8: pReformat = new ReformatDisasm8; break; - case kReformatMonitor16Long: pReformat = new ReformatDisasm16; break; - case kReformatMonitor16Short: pReformat = new ReformatDisasm16; break; - case kReformatDisasmOrcam16: pReformat = new ReformatDisasm16; break; - case kReformatAWGS_WP: pReformat = new ReformatAWGS_WP; break; - case kReformatTeach: pReformat = new ReformatTeach; break; - case kReformatGWP: pReformat = new ReformatGWP; break; - case kReformatMagicWindow: pReformat = new ReformatMagicWindow; break; - case kReformatAWP: pReformat = new ReformatAWP; break; - case kReformatADB: pReformat = new ReformatADB; break; - case kReformatASP: pReformat = new ReformatASP; break; - case kReformatHiRes: pReformat = new ReformatHiRes; break; - case kReformatHiRes_BW: pReformat = new ReformatHiRes; break; - case kReformatDHR_Latched: pReformat = new ReformatDHR; break; - case kReformatDHR_BW: pReformat = new ReformatDHR; break; - case kReformatDHR_Plain140: pReformat = new ReformatDHR; break; - case kReformatDHR_Window: pReformat = new ReformatDHR; break; - case kReformatSHR_PIC: pReformat = new ReformatUnpackedSHR; break; - case kReformatSHR_JEQ: pReformat = new ReformatJEQSHR; break; - case kReformatSHR_Paintworks: pReformat = new ReformatPaintworksSHR; break; - case kReformatSHR_Packed: pReformat = new ReformatPackedSHR; break; - case kReformatSHR_APF: pReformat = new ReformatAPFSHR; break; - case kReformatSHR_3200: pReformat = new Reformat3200SHR; break; - case kReformatSHR_3201: pReformat = new Reformat3201SHR; break; - case kReformatSHR_DG256: pReformat = new ReformatDG256SHR; break; - case kReformatSHR_DG3200: pReformat = new ReformatDG3200SHR; break; - case kReformatPrintShop: pReformat = new ReformatPrintShop; break; - case kReformatMacPaint: pReformat = new ReformatMacPaint; break; - case kReformatUnknown: - case kReformatMAX: - default: assert(false); break; - } - - return pReformat; -} - -/* - * Return a string describing the class identified by "id". We need this for - * the pop-up menu in the file viewer. - * - * Would have been nice to embed these in the individual classes, but that's - * harder to maintain. - */ -/*static*/ const char* -ReformatHolder::GetReformatName(ReformatID id) -{ - const char* descr = nil; - - switch (id) { - case kReformatTextEOL_HA: - descr = "Converted Text"; - break; - case kReformatRaw: - descr = "Raw"; - break; - case kReformatHexDump: - descr = "Hex Dump"; - break; - case kReformatResourceFork: - descr = "Resource Fork"; - break; - case kReformatProDOSDirectory: - descr = "ProDOS Directory"; - break; - case kReformatPascalText: - descr = "Pascal Text"; - break; - case kReformatPascalCode: - descr = "Pascal Code"; - break; - case kReformatCPMText: - descr = "CP/M Text"; - break; - case kReformatApplesoft: - descr = "Applesoft BASIC"; - break; - case kReformatApplesoft_Hilite: - descr = "Applesoft BASIC w/Highlighting"; - break; - case kReformatInteger: - descr = "Integer BASIC"; - break; - case kReformatInteger_Hilite: - descr = "Integer BASIC w/Highlighting"; - break; - case kReformatSCAssem: - descr = "S-C Assembler"; - break; - case kReformatMerlin: - descr = "Merlin Assembler"; - break; - case kReformatLISA2: - descr = "LISA Assembler (v2)"; - break; - case kReformatLISA3: - descr = "LISA Assembler (v3)"; - break; - case kReformatLISA4: - descr = "LISA Assembler (v4/v5)"; - break; - case kReformatMonitor8: - descr = "//e monitor listing"; - break; - case kReformatDisasmMerlin8: - descr = "8-bit disassembly (Merlin)"; - break; - case kReformatMonitor16Long: - descr = "IIgs monitor listing (long regs)"; - break; - case kReformatMonitor16Short: - descr = "IIgs monitor listing (short regs)"; - break; - case kReformatDisasmOrcam16: - descr = "16-bit disassembly (Orca/M)"; - break; - case kReformatAWGS_WP: - descr = "AppleWorks GS Word Processor"; - break; - case kReformatTeach: - descr = "Teach Text"; - break; - case kReformatGWP: - descr = "Generic IIgs text document"; - break; - case kReformatMagicWindow: - descr = "Magic Window"; - break; - case kReformatAWP: - descr = "AppleWorks Word Processor"; - break; - case kReformatADB: - descr = "AppleWorks Database"; - break; - case kReformatASP: - descr = "AppleWorks Spreadsheet"; - break; - case kReformatHiRes: - descr = "Hi-Res / Color"; - break; - case kReformatHiRes_BW: - descr = "Hi-Res / B&W"; - break; - case kReformatDHR_Latched: - descr = "Double Hi-Res / Latched"; - break; - case kReformatDHR_BW: - descr = "Double Hi-Res / B&W"; - break; - case kReformatDHR_Plain140: - descr = "Double Hi-Res / Plain140"; - break; - case kReformatDHR_Window: - descr = "Double Hi-Res / Windowed"; - break; - case kReformatSHR_PIC: - descr = "Super Hi-Res"; - break; - case kReformatSHR_JEQ: - descr = "JEQ Super Hi-Res"; - break; - case kReformatSHR_Paintworks: - descr = "Paintworks Super Hi-Res"; - break; - case kReformatSHR_Packed: - descr = "Packed Super Hi-Res"; - break; - case kReformatSHR_APF: - descr = "APF Super Hi-Res"; - break; - case kReformatSHR_3200: - descr = "3200-Color Super Hi-Res"; - break; - case kReformatSHR_3201: - descr = "Packed 3200-Color Super Hi-Res"; - break; - case kReformatSHR_DG256: - descr = "DreamGrafix 256-Color Super Hi-Res"; - break; - case kReformatSHR_DG3200: - descr = "DreamGrafix 3200-Color Super Hi-Res"; - break; - case kReformatPrintShop: - descr = "Print Shop Clip Art"; - break; - case kReformatMacPaint: - descr = "MacPaint"; - break; - case kReformatUnknown: - case kReformatMAX: - default: - assert(false); - descr = "UNKNOWN"; - break; - } - - return descr; -} - - -/* - * Set the file attributes. These are used by TestApplicability tests to - * decide what it is we're looking at. - */ -void -ReformatHolder::SetSourceAttributes(long fileType, long auxType, - SourceFormat sourceFormat, const char* nameExt) -{ - fFileType = fileType; - fAuxType = auxType; - fSourceFormat = sourceFormat; - - if (nameExt == nil) { - assert(fNameExt == nil); - fNameExt = new char[1]; - fNameExt[0] = '\0'; - } else { - fNameExt = new char[strlen(nameExt)+1]; - strcpy(fNameExt, nameExt); - } -} - -/* - * Run through the set of reformatters and figure out which apply. - * - * Each reformatter function is handed the full set. If it can make a - * determination for more than one entry (e.g. Applesoft and ApplesoftHilite), - * it can set all that apply. There must not be any overlap between - * reformatters -- this is here so that a single reformatter may have more - * than one entry without having to re-process the data multiple times. - * - * Before calling here, the file data and file attributes (e.g. file type) - * should be stored in "ReformatHolder". - */ -void -ReformatHolder::TestApplicability(void) -{ - Reformat* pReformat; - int i; - - for (i = 0; i < kReformatMAX; i++) { - if (fApplies[kPartData][i] != kApplicUnknown) { - assert(fApplies[kPartRsrc][i] != kApplicUnknown); - assert(fApplies[kPartCmmt][i] != kApplicUnknown); - continue; // already set by previous test - } - if (!fAllow[i]) { - if (i != 0) { - WMSG1(" NOTE: Applic %d disallowed\n", i); - // did you update ConfigureReformatFromPreferences()? - } - fApplies[kPartData][i] = kApplicNot; - fApplies[kPartRsrc][i] = kApplicNot; - fApplies[kPartCmmt][i] = kApplicNot; - continue; - } - - /* - * Create an instance of the object, test its applicability, and - * then destroy the instance. It's less efficient to do it this - * way than some other approaches, but it's easier maintenance - * than creating a separate table of pointers to static functions. - */ - pReformat = GetReformatInstance((ReformatID) i); - assert(pReformat != nil); - pReformat->Examine(this); - delete pReformat; - } - - /* don't mess with the unknown */ - assert(fApplies[kPartData][kReformatUnknown] == kApplicNot); - assert(fApplies[kPartRsrc][kReformatUnknown] == kApplicNot); - assert(fApplies[kPartCmmt][kReformatUnknown] == kApplicNot); -} - -/* - * Return the appropriate applicability level. - */ -ReformatHolder::ReformatApplies -ReformatHolder::GetApplic(ReformatPart part, ReformatID id) const -{ - if (id < kReformatUnknown || id >= kReformatMAX) { - assert(false); - return kApplicUnknown; - } - if (part <= kPartUnknown || part >= kPartMAX) { - assert(false); - return kApplicUnknown; - } - - return fApplies[part][id]; -} - -/* - * Set the appropriate applicability level. - */ -void -ReformatHolder::SetApplic(ReformatID id, ReformatApplies applyData, - ReformatApplies applyRsrc, ReformatApplies applyCmmt) -{ - if (id <= kReformatUnknown || id >= kReformatMAX) { - assert(false); - return; - } - - fApplies[kPartData][id] = applyData; - fApplies[kPartRsrc][id] = applyRsrc; - fApplies[kPartCmmt][id] = applyCmmt; -} - -/* - * Set the "preferred" flag on all parts that aren't "unknown" or "not" - * for the specified id. If "part" isn't kPartUnknown, then only that - * part will be altered. - * - * The idea is to prefer one variation over another, such as highlighting - * a BASIC program or choosing a double-hi-res algorithm. The preference - * indicates the default choice, but does not exclude others. - */ -void -ReformatHolder::SetApplicPreferred(ReformatID id, ReformatPart part) -{ - for (int i = 0; i < kPartMAX; i++) { - if (i == kPartUnknown) - continue; - - if (part != kPartUnknown && i != part) - continue; - - // Don't set "preferred" flag for "not", "probably not", "always". - if (fApplies[i][id] >= kApplicAlways) { - fApplies[i][id] = - (ReformatApplies) (fApplies[i][id] | kApplicPreferred); - } - } -} - - -/* - * Returns <0, 0, or >0 depending on whether app1 is worse, equal to, - * or better than app2. - */ -int -ReformatHolder::CompareApplies(ReformatApplies app1, ReformatApplies app2) -{ - if ((app1 & kApplicPrefMask) < (app2 & kApplicPrefMask)) - return -1; - else if ((app1 & kApplicPrefMask) > (app2 & kApplicPrefMask)) - return 1; - else return (app1 - app2); // compare with "preferred" bit -} - -/* - * Find the best reformatter for the specified part of the loaded file. - */ -ReformatHolder::ReformatID -ReformatHolder::FindBest(ReformatPart part) -{ - ReformatID bestID = kReformatUnknown; - ReformatApplies bestApply = kApplicNot; - ReformatApplies apply; - int i; - - /* if the source couldn't be loaded, just return "raw" */ - if (fErrorBuf[part] != nil) - return kReformatRaw; - - /* - * Use the best option, or an equivalent-valued option that has - * the "preferred" flag set. - */ - for (i = 0; i < kReformatMAX; i++) { - apply = GetApplic(part, (ReformatID) i); - if (CompareApplies(apply, bestApply) > 0) { - bestApply = apply; - bestID = (ReformatID) i; - } - } - - if (bestID == kReformatUnknown || bestApply == kApplicNot) { - WMSG0("Did you forget to call TestApplicability?\n"); - assert(false); - return kReformatRaw; - } - - WMSG2("Best is %d at lvl=%d\n", bestID, bestApply); - - return bestID; -} - -/* - * Apply the requested formatter to the specified part. - */ -ReformatOutput* -ReformatHolder::Apply(ReformatPart part, ReformatID id) -{ - ReformatOutput* pOutput; - Reformat* pReformat; - int result; - - if (id <= kReformatUnknown || id >= kReformatMAX || - part <= kPartUnknown || part >= kPartMAX) - { - WMSG2("Invalid reformat request (part=%d id=%d)\n", part, id); - assert(false); - return nil; - } - - /* create a place for the output */ - pOutput = new ReformatOutput; - if (pOutput == nil) { - assert(false); - return nil; // alloc failure - } - - /* - * If the caller was unable to fill our source buffer, they will have - * supplied us with an error message. Return that instead of the data. - */ - if (fErrorBuf[part] != nil) { - pOutput->SetTextBuf(fErrorBuf[part], strlen(fErrorBuf[part]), false); - pOutput->SetOutputKind(ReformatOutput::kOutputErrorMsg); - pOutput->SetFormatDescr(_T("Error Message")); - return pOutput; - } - - /* - * Set the format description here, based on the id. If the reformatter - * fails we will need to change this, but it allows the reformatter to - * override our label with its own in case it wants to indicate some - * sort of sub-variant. - */ - pOutput->SetFormatDescr(GetReformatName(id)); - - /* create an instance of a reformatter */ - pReformat = GetReformatInstance(id); - assert(pReformat != nil); - result = pReformat->Process(this, id, part, pOutput); - delete pReformat; - - /* - * If it fails, return a pointer to the source buffer. - * - * This commonly happens on zero-length files. The chosen reformatter - * rejects it, returns -1, and we return the source buffer. Since even - * zero-length files are guaranteed to have some sort of allocated - * buffer, "pOutput" never points at nil. Unless, of course, a text - * reformatter produces no output but still returns "success". - */ - if (result < 0) { - pOutput->SetTextBuf((char*)fSourceBuf[part], fSourceLen[part], false); - pOutput->SetOutputKind(ReformatOutput::kOutputRaw); - pOutput->SetFormatDescr(GetReformatName(kReformatRaw)); - } - - return pOutput; -} - - -/* - * Get the appropriate input buffer. - */ -const unsigned char* -ReformatHolder::GetSourceBuf(ReformatPart part) const -{ - if (part <= kPartUnknown || part >= kPartMAX) { - assert(false); - return nil; - } - - return fSourceBuf[part]; -} - -/* - * Get the length of the appropriate input buffer. - */ -long -ReformatHolder::GetSourceLen(ReformatPart part) const -{ - if (part <= kPartUnknown || part >= kPartMAX) { - assert(false); - return nil; - } - - return fSourceLen[part]; -} - -/* - * Set the input buffer. - * - * A buffer is required, even for empty input. This makes the overall - * housekeeping simpler. - * - * The ReformatHolder "owns" the buffer afterward, so the caller should - * discard its pointer. - */ -void -ReformatHolder::SetSourceBuf(ReformatPart part, unsigned char* buf, - long len) -{ - if (part <= kPartUnknown || part >= kPartMAX) { - assert(false); - return; - } - assert(buf != nil); - assert(len >= 0); - - fSourceBuf[part] = buf; - fSourceLen[part] = len; -} - - -/* - * Specify an error message to return instead of reformatted text for a - * given part. - */ -void -ReformatHolder::SetErrorMsg(ReformatPart part, const char* msg) -{ - assert(msg != nil && *msg != '\0'); - assert(part > kPartUnknown && part < kPartMAX); - assert(fErrorBuf[part] == nil); - - fErrorBuf[part] = new char[strlen(msg) + 1]; - if (fErrorBuf[part] != nil) { - WMSG2("+++ set error message for part %d to '%s'\n", part, msg); - strcpy(fErrorBuf[part], msg); - } -} +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Common code for reformatters. + */ +#include "StdAfx.h" +#include "Reformat.h" + +#include "ReformatBase.h" +#include "AppleWorks.h" +#include "Asm.h" +#include "AWGS.h" +#include "Basic.h" +#include "CPMFiles.h" +#include "Directory.h" +#include "Disasm.h" +#include "DoubleHiRes.h" +#include "HiRes.h" +#include "MacPaint.h" +#include "PascalFiles.h" +#include "PrintShop.h" +#include "ResourceFork.h" +#include "Simple.h" +#include "SuperHiRes.h" +#include "Teach.h" +#include "Text8.h" + +/* + * Create an instance of the class identified by "id". + */ +/*static*/ Reformat* +ReformatHolder::GetReformatInstance(ReformatID id) +{ + Reformat* pReformat = nil; + + switch (id) { + case kReformatTextEOL_HA: pReformat = new ReformatEOL_HA; break; + case kReformatRaw: pReformat = new ReformatRaw; break; + case kReformatHexDump: pReformat = new ReformatHexDump; break; + case kReformatResourceFork: pReformat = new ReformatResourceFork; break; + + case kReformatProDOSDirectory: pReformat = new ReformatDirectory; break; + case kReformatPascalText: pReformat = new ReformatPascalText; break; + case kReformatPascalCode: pReformat = new ReformatPascalCode; break; + case kReformatCPMText: pReformat = new ReformatCPMText; break; + case kReformatApplesoft: pReformat = new ReformatApplesoft; break; + case kReformatApplesoft_Hilite: pReformat = new ReformatApplesoft; break; + case kReformatInteger: pReformat = new ReformatInteger; break; + case kReformatInteger_Hilite: pReformat = new ReformatInteger; break; + case kReformatBusiness: pReformat = new ReformatBusiness; break; + case kReformatBusiness_Hilite: pReformat = new ReformatBusiness; break; + case kReformatSCAssem: pReformat = new ReformatSCAssem; break; + case kReformatMerlin: pReformat = new ReformatMerlin; break; + case kReformatLISA2: pReformat = new ReformatLISA2; break; + case kReformatLISA3: pReformat = new ReformatLISA3; break; + case kReformatLISA4: pReformat = new ReformatLISA4; break; + case kReformatMonitor8: pReformat = new ReformatDisasm8; break; + case kReformatDisasmMerlin8: pReformat = new ReformatDisasm8; break; + case kReformatMonitor16Long: pReformat = new ReformatDisasm16; break; + case kReformatMonitor16Short: pReformat = new ReformatDisasm16; break; + case kReformatDisasmOrcam16: pReformat = new ReformatDisasm16; break; + case kReformatAWGS_WP: pReformat = new ReformatAWGS_WP; break; + case kReformatTeach: pReformat = new ReformatTeach; break; + case kReformatGWP: pReformat = new ReformatGWP; break; + case kReformatMagicWindow: pReformat = new ReformatMagicWindow; break; + case kReformatAWP: pReformat = new ReformatAWP; break; + case kReformatADB: pReformat = new ReformatADB; break; + case kReformatASP: pReformat = new ReformatASP; break; + case kReformatHiRes: pReformat = new ReformatHiRes; break; + case kReformatHiRes_BW: pReformat = new ReformatHiRes; break; + case kReformatDHR_Latched: pReformat = new ReformatDHR; break; + case kReformatDHR_BW: pReformat = new ReformatDHR; break; + case kReformatDHR_Plain140: pReformat = new ReformatDHR; break; + case kReformatDHR_Window: pReformat = new ReformatDHR; break; + case kReformatSHR_PIC: pReformat = new ReformatUnpackedSHR; break; + case kReformatSHR_JEQ: pReformat = new ReformatJEQSHR; break; + case kReformatSHR_Paintworks: pReformat = new ReformatPaintworksSHR; break; + case kReformatSHR_Packed: pReformat = new ReformatPackedSHR; break; + case kReformatSHR_APF: pReformat = new ReformatAPFSHR; break; + case kReformatSHR_3200: pReformat = new Reformat3200SHR; break; + case kReformatSHR_3201: pReformat = new Reformat3201SHR; break; + case kReformatSHR_DG256: pReformat = new ReformatDG256SHR; break; + case kReformatSHR_DG3200: pReformat = new ReformatDG3200SHR; break; + case kReformatPrintShop: pReformat = new ReformatPrintShop; break; + case kReformatMacPaint: pReformat = new ReformatMacPaint; break; + case kReformatUnknown: + case kReformatMAX: + default: assert(false); break; + } + + return pReformat; +} + +/* + * Return a string describing the class identified by "id". We need this for + * the pop-up menu in the file viewer. + * + * Would have been nice to embed these in the individual classes, but that's + * harder to maintain. + */ +/*static*/ const char* +ReformatHolder::GetReformatName(ReformatID id) +{ + const char* descr = nil; + + switch (id) { + case kReformatTextEOL_HA: + descr = "Converted Text"; + break; + case kReformatRaw: + descr = "Raw"; + break; + case kReformatHexDump: + descr = "Hex Dump"; + break; + case kReformatResourceFork: + descr = "Resource Fork"; + break; + case kReformatProDOSDirectory: + descr = "ProDOS Directory"; + break; + case kReformatPascalText: + descr = "Pascal Text"; + break; + case kReformatPascalCode: + descr = "Pascal Code"; + break; + case kReformatCPMText: + descr = "CP/M Text"; + break; + case kReformatApplesoft: + descr = "Applesoft BASIC"; + break; + case kReformatApplesoft_Hilite: + descr = "Applesoft BASIC w/Highlighting"; + break; + case kReformatInteger: + descr = "Integer BASIC"; + break; + case kReformatInteger_Hilite: + descr = "Integer BASIC w/Highlighting"; + break; + case kReformatBusiness: + descr = "Apple /// Business BASIC"; + break; + case kReformatBusiness_Hilite: + descr = "Apple /// Business BASIC w/Highlighting"; + break; + case kReformatSCAssem: + descr = "S-C Assembler"; + break; + case kReformatMerlin: + descr = "Merlin Assembler"; + break; + case kReformatLISA2: + descr = "LISA Assembler (v2)"; + break; + case kReformatLISA3: + descr = "LISA Assembler (v3)"; + break; + case kReformatLISA4: + descr = "LISA Assembler (v4/v5)"; + break; + case kReformatMonitor8: + descr = "//e monitor listing"; + break; + case kReformatDisasmMerlin8: + descr = "8-bit disassembly (Merlin)"; + break; + case kReformatMonitor16Long: + descr = "IIgs monitor listing (long regs)"; + break; + case kReformatMonitor16Short: + descr = "IIgs monitor listing (short regs)"; + break; + case kReformatDisasmOrcam16: + descr = "16-bit disassembly (Orca/M)"; + break; + case kReformatAWGS_WP: + descr = "AppleWorks GS Word Processor"; + break; + case kReformatTeach: + descr = "Teach Text"; + break; + case kReformatGWP: + descr = "Generic IIgs text document"; + break; + case kReformatMagicWindow: + descr = "Magic Window"; + break; + case kReformatAWP: + descr = "AppleWorks Word Processor"; + break; + case kReformatADB: + descr = "AppleWorks Database"; + break; + case kReformatASP: + descr = "AppleWorks Spreadsheet"; + break; + case kReformatHiRes: + descr = "Hi-Res / Color"; + break; + case kReformatHiRes_BW: + descr = "Hi-Res / B&W"; + break; + case kReformatDHR_Latched: + descr = "Double Hi-Res / Latched"; + break; + case kReformatDHR_BW: + descr = "Double Hi-Res / B&W"; + break; + case kReformatDHR_Plain140: + descr = "Double Hi-Res / Plain140"; + break; + case kReformatDHR_Window: + descr = "Double Hi-Res / Windowed"; + break; + case kReformatSHR_PIC: + descr = "Super Hi-Res"; + break; + case kReformatSHR_JEQ: + descr = "JEQ Super Hi-Res"; + break; + case kReformatSHR_Paintworks: + descr = "Paintworks Super Hi-Res"; + break; + case kReformatSHR_Packed: + descr = "Packed Super Hi-Res"; + break; + case kReformatSHR_APF: + descr = "APF Super Hi-Res"; + break; + case kReformatSHR_3200: + descr = "3200-Color Super Hi-Res"; + break; + case kReformatSHR_3201: + descr = "Packed 3200-Color Super Hi-Res"; + break; + case kReformatSHR_DG256: + descr = "DreamGrafix 256-Color Super Hi-Res"; + break; + case kReformatSHR_DG3200: + descr = "DreamGrafix 3200-Color Super Hi-Res"; + break; + case kReformatPrintShop: + descr = "Print Shop Clip Art"; + break; + case kReformatMacPaint: + descr = "MacPaint"; + break; + case kReformatUnknown: + case kReformatMAX: + default: + assert(false); + descr = "UNKNOWN"; + break; + } + + return descr; +} + + +/* + * Set the file attributes. These are used by TestApplicability tests to + * decide what it is we're looking at. + */ +void +ReformatHolder::SetSourceAttributes(long fileType, long auxType, + SourceFormat sourceFormat, const char* nameExt) +{ + fFileType = fileType; + fAuxType = auxType; + fSourceFormat = sourceFormat; + + if (nameExt == nil) { + assert(fNameExt == nil); + fNameExt = new char[1]; + fNameExt[0] = '\0'; + } else { + fNameExt = new char[strlen(nameExt)+1]; + strcpy(fNameExt, nameExt); + } +} + +/* + * Run through the set of reformatters and figure out which apply. + * + * Each reformatter function is handed the full set. If it can make a + * determination for more than one entry (e.g. Applesoft and ApplesoftHilite), + * it can set all that apply. There must not be any overlap between + * reformatters -- this is here so that a single reformatter may have more + * than one entry without having to re-process the data multiple times. + * + * Before calling here, the file data and file attributes (e.g. file type) + * should be stored in "ReformatHolder". + */ +void +ReformatHolder::TestApplicability(void) +{ + Reformat* pReformat; + int i; + + for (i = 0; i < kReformatMAX; i++) { + if (fApplies[kPartData][i] != kApplicUnknown) { + assert(fApplies[kPartRsrc][i] != kApplicUnknown); + assert(fApplies[kPartCmmt][i] != kApplicUnknown); + continue; // already set by previous test + } + if (!fAllow[i]) { + if (i != 0) { + WMSG1(" NOTE: Applic %d disallowed\n", i); + // did you update ConfigureReformatFromPreferences()? + } + fApplies[kPartData][i] = kApplicNot; + fApplies[kPartRsrc][i] = kApplicNot; + fApplies[kPartCmmt][i] = kApplicNot; + continue; + } + + /* + * Create an instance of the object, test its applicability, and + * then destroy the instance. It's less efficient to do it this + * way than some other approaches, but it's easier maintenance + * than creating a separate table of pointers to static functions. + */ + pReformat = GetReformatInstance((ReformatID) i); + assert(pReformat != nil); + pReformat->Examine(this); + delete pReformat; + } + + /* don't mess with the unknown */ + assert(fApplies[kPartData][kReformatUnknown] == kApplicNot); + assert(fApplies[kPartRsrc][kReformatUnknown] == kApplicNot); + assert(fApplies[kPartCmmt][kReformatUnknown] == kApplicNot); +} + +/* + * Return the appropriate applicability level. + */ +ReformatHolder::ReformatApplies +ReformatHolder::GetApplic(ReformatPart part, ReformatID id) const +{ + if (id < kReformatUnknown || id >= kReformatMAX) { + assert(false); + return kApplicUnknown; + } + if (part <= kPartUnknown || part >= kPartMAX) { + assert(false); + return kApplicUnknown; + } + + return fApplies[part][id]; +} + +/* + * Set the appropriate applicability level. + */ +void +ReformatHolder::SetApplic(ReformatID id, ReformatApplies applyData, + ReformatApplies applyRsrc, ReformatApplies applyCmmt) +{ + if (id <= kReformatUnknown || id >= kReformatMAX) { + assert(false); + return; + } + + fApplies[kPartData][id] = applyData; + fApplies[kPartRsrc][id] = applyRsrc; + fApplies[kPartCmmt][id] = applyCmmt; +} + +/* + * Set the "preferred" flag on all parts that aren't "unknown" or "not" + * for the specified id. If "part" isn't kPartUnknown, then only that + * part will be altered. + * + * The idea is to prefer one variation over another, such as highlighting + * a BASIC program or choosing a double-hi-res algorithm. The preference + * indicates the default choice, but does not exclude others. + */ +void +ReformatHolder::SetApplicPreferred(ReformatID id, ReformatPart part) +{ + for (int i = 0; i < kPartMAX; i++) { + if (i == kPartUnknown) + continue; + + if (part != kPartUnknown && i != part) + continue; + + // Don't set "preferred" flag for "not", "probably not", "always". + if (fApplies[i][id] >= kApplicAlways) { + fApplies[i][id] = + (ReformatApplies) (fApplies[i][id] | kApplicPreferred); + } + } +} + + +/* + * Returns <0, 0, or >0 depending on whether app1 is worse, equal to, + * or better than app2. + */ +int +ReformatHolder::CompareApplies(ReformatApplies app1, ReformatApplies app2) +{ + if ((app1 & kApplicPrefMask) < (app2 & kApplicPrefMask)) + return -1; + else if ((app1 & kApplicPrefMask) > (app2 & kApplicPrefMask)) + return 1; + else return (app1 - app2); // compare with "preferred" bit +} + +/* + * Find the best reformatter for the specified part of the loaded file. + */ +ReformatHolder::ReformatID +ReformatHolder::FindBest(ReformatPart part) +{ + ReformatID bestID = kReformatUnknown; + ReformatApplies bestApply = kApplicNot; + ReformatApplies apply; + int i; + + /* if the source couldn't be loaded, just return "raw" */ + if (fErrorBuf[part] != nil) + return kReformatRaw; + + /* + * Use the best option, or an equivalent-valued option that has + * the "preferred" flag set. + */ + for (i = 0; i < kReformatMAX; i++) { + apply = GetApplic(part, (ReformatID) i); + if (CompareApplies(apply, bestApply) > 0) { + bestApply = apply; + bestID = (ReformatID) i; + } + } + + if (bestID == kReformatUnknown || bestApply == kApplicNot) { + WMSG0("Did you forget to call TestApplicability?\n"); + assert(false); + return kReformatRaw; + } + + WMSG2("Best is %d at lvl=%d\n", bestID, bestApply); + + return bestID; +} + +/* + * Apply the requested formatter to the specified part. + */ +ReformatOutput* +ReformatHolder::Apply(ReformatPart part, ReformatID id) +{ + ReformatOutput* pOutput; + Reformat* pReformat; + int result; + + if (id <= kReformatUnknown || id >= kReformatMAX || + part <= kPartUnknown || part >= kPartMAX) + { + WMSG2("Invalid reformat request (part=%d id=%d)\n", part, id); + assert(false); + return nil; + } + + /* create a place for the output */ + pOutput = new ReformatOutput; + if (pOutput == nil) { + assert(false); + return nil; // alloc failure + } + + /* + * If the caller was unable to fill our source buffer, they will have + * supplied us with an error message. Return that instead of the data. + */ + if (fErrorBuf[part] != nil) { + pOutput->SetTextBuf(fErrorBuf[part], strlen(fErrorBuf[part]), false); + pOutput->SetOutputKind(ReformatOutput::kOutputErrorMsg); + pOutput->SetFormatDescr(_T("Error Message")); + return pOutput; + } + + /* + * Set the format description here, based on the id. If the reformatter + * fails we will need to change this, but it allows the reformatter to + * override our label with its own in case it wants to indicate some + * sort of sub-variant. + */ + pOutput->SetFormatDescr(GetReformatName(id)); + + /* create an instance of a reformatter */ + pReformat = GetReformatInstance(id); + assert(pReformat != nil); + result = pReformat->Process(this, id, part, pOutput); + delete pReformat; + + /* + * If it fails, return a pointer to the source buffer. + * + * This commonly happens on zero-length files. The chosen reformatter + * rejects it, returns -1, and we return the source buffer. Since even + * zero-length files are guaranteed to have some sort of allocated + * buffer, "pOutput" never points at nil. Unless, of course, a text + * reformatter produces no output but still returns "success". + */ + if (result < 0) { + pOutput->SetTextBuf((char*)fSourceBuf[part], fSourceLen[part], false); + pOutput->SetOutputKind(ReformatOutput::kOutputRaw); + pOutput->SetFormatDescr(GetReformatName(kReformatRaw)); + } + + return pOutput; +} + + +/* + * Get the appropriate input buffer. + */ +const unsigned char* +ReformatHolder::GetSourceBuf(ReformatPart part) const +{ + if (part <= kPartUnknown || part >= kPartMAX) { + assert(false); + return nil; + } + + return fSourceBuf[part]; +} + +/* + * Get the length of the appropriate input buffer. + */ +long +ReformatHolder::GetSourceLen(ReformatPart part) const +{ + if (part <= kPartUnknown || part >= kPartMAX) { + assert(false); + return nil; + } + + return fSourceLen[part]; +} + +/* + * Set the input buffer. + * + * A buffer is required, even for empty input. This makes the overall + * housekeeping simpler. + * + * The ReformatHolder "owns" the buffer afterward, so the caller should + * discard its pointer. + */ +void +ReformatHolder::SetSourceBuf(ReformatPart part, unsigned char* buf, + long len) +{ + if (part <= kPartUnknown || part >= kPartMAX) { + assert(false); + return; + } + assert(buf != nil); + assert(len >= 0); + + fSourceBuf[part] = buf; + fSourceLen[part] = len; +} + + +/* + * Specify an error message to return instead of reformatted text for a + * given part. + */ +void +ReformatHolder::SetErrorMsg(ReformatPart part, const char* msg) +{ + assert(msg != nil && *msg != '\0'); + assert(part > kPartUnknown && part < kPartMAX); + assert(fErrorBuf[part] == nil); + + fErrorBuf[part] = new char[strlen(msg) + 1]; + if (fErrorBuf[part] != nil) { + WMSG2("+++ set error message for part %d to '%s'\n", part, msg); + strcpy(fErrorBuf[part], msg); + } +} diff --git a/reformat/Reformat.h b/reformat/Reformat.h index 3c146d6..164846f 100644 --- a/reformat/Reformat.h +++ b/reformat/Reformat.h @@ -1,516 +1,519 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * File reformatter class. Used to encapsulate working state and RTF - * knowledge while rewriting a file. - * - * Currently missing: a way to provide progress updates when reformatting - * a large file. - */ -#ifndef __LR_REFORMAT__ -#define __LR_REFORMAT__ - -#include "../util/UtilLib.h" - -class Reformat; -class ReformatOutput; - - -/* - * This holds all three file parts (data, resource, comment) for use by the - * reformatters. - * - * The "source" buffers are owned by this class, and will be freed when - * the object is deleted. - * - * Typical calling sequence: - * - Prep: - * - Allocate object - * - Load parts into source buffers - * - MainWindow::ConfigureReformatFromPreferences() - * - SetSourceAttributes() - * - TestApplicability() - * - Action: - * - id = FindBest(part) - * - pOutput = Apply(part, id) - * - Cleanup: - * - Destroy ReformatOutput (when done with part) - * - Destroy ReformatHolder (when done with all parts) - * - * When adding new formatters: - * Any additions here need to be reflected in the switch statements in - * GetReformatInstance() and GetReformatName(), and added to the set of - * things allowed by preferences in ConfigureReformatFromPreferences. - * New classes must also be added to the list of friends, below. - */ -class ReformatHolder { -public: - /* - * Reformatters, including minor variants. - * - * TextEOL_HA and Raw must be the first two entries. If you change these, - * you may also need to adjust the way extraction is handled over in - * DoBulkExtract. The extract code depends on getting "raw" data back - * for files that don't have a better reformatter. - */ - typedef enum ReformatID { - kReformatUnknown = 0, - - /* don't change the order of these! */ - kReformatTextEOL_HA, // default for unknown types (if enabled) - kReformatRaw, // backup default - kReformatHexDump, - - /* from here on, only order within sub-groups matters */ - kReformatResourceFork, - - kReformatPascalText, - kReformatPascalCode, - - kReformatCPMText, - - kReformatApplesoft, - kReformatApplesoft_Hilite, - kReformatInteger, - kReformatInteger_Hilite, - - kReformatSCAssem, - kReformatMerlin, - kReformatLISA2, - kReformatLISA3, - kReformatLISA4, - - kReformatMonitor8, - kReformatDisasmMerlin8, - kReformatMonitor16Long, - kReformatMonitor16Short, - kReformatDisasmOrcam16, - - kReformatAWGS_WP, - kReformatTeach, - kReformatGWP, - - kReformatMagicWindow, - - kReformatAWP, - kReformatADB, - kReformatASP, - - kReformatHiRes, - kReformatHiRes_BW, - - kReformatDHR_Latched, - kReformatDHR_BW, - kReformatDHR_Window, - kReformatDHR_Plain140, - - kReformatProDOSDirectory, - - kReformatSHR_PIC, - kReformatSHR_JEQ, - kReformatSHR_Paintworks, - kReformatSHR_Packed, - kReformatSHR_APF, - kReformatSHR_3200, - kReformatSHR_3201, - kReformatSHR_DG256, - kReformatSHR_DG3200, - - kReformatPrintShop, - - kReformatMacPaint, - - kReformatMAX // must be last - } ReformatID; - - /* - * Set options. Each value is a "long". - */ - typedef enum OptionID { - kOptUnknown = 0, - - kOptHiliteHexDump, - kOptHiliteBASIC, - kOptHiResBW, - kOptDHRAlgorithm, - kOptRelaxGfxTypeCheck, - kOptOneByteBrkCop, - - kOptMAX // must be last - } OptionID; - - /* - * Each reformatter examines the input and determines its applicability. - * This ranges from "impossible" to being the preferred sub-variant of - * a verified converter. - * - * Thought: could set kApplicPreferred to 0x01, use even values for - * the enumeration, and then just do a numeric sort. - */ - typedef enum ReformatApplies { - kApplicUnknown = 0, // not set - kApplicNot, // does not apply - kApplicProbablyNot, // allow, but don't use as default, ever - kApplicAlways, // generic, always-applicable (e.g. hex) - kApplicMaybe, // might work - kApplicProbably, // should work, based on size and type - kApplicYes, // contents look right - kApplicMAX, // must be at end, not including bits - - kApplicPreferred = 0x80, // tag preferred variation - kApplicPrefMask = 0x7f, // mask off "preferred" flag - } ReformatApplies; - - /* which part of the file are we targetting? */ - typedef enum ReformatPart { - kPartUnknown = -1, - kPartData = 0, - kPartRsrc, - kPartCmmt, - kPartMAX // must be last - } ReformatPart; - - /* - * This is more or less a restatement of DiskImg::FSFormat. We do this so - * we don't force everybody to include DiskImg.h, especially since there's - * only a couple of interesting cases. - * - * We want to know if it's DOS so we can relax some file-type checking, - * and we want to know if it's CP/M so we can adjust the way we think - * about text files. - */ - typedef enum SourceFormat { - kSourceFormatGeneric = 0, - kSourceFormatDOS, - kSourceFormatCPM, - } SourceFormat; - - - /* - * Construct/destruct our object. - */ - ReformatHolder(void) { - int i; - for (int part = 0; part < kPartMAX; part++) { - if (part == kPartUnknown) - continue; - for (i = 0; i < kReformatMAX; i++) - fApplies[part][i] = kApplicUnknown; - fSourceBuf[part] = nil; - fSourceLen[part] = nil; - fErrorBuf[part] = nil; - } - for (i = 0; i < kReformatMAX; i++) { - fAllow[i] = false; - } - for (i = 0; i < kOptMAX; i++) - fOption[i] = 0; - - fFileType = fAuxType = 0; - fSourceFormat = kSourceFormatGeneric; - fNameExt = nil; - } - ~ReformatHolder(void) { - WMSG0("In ~ReformatHolder\n"); - for (int i = 0; i < kPartMAX; i++) { - if (i == kPartUnknown) - continue; - delete[] fSourceBuf[i]; - delete[] fErrorBuf[i]; - } - delete[] fNameExt; - } - - /* set attributes before calling TestApplicability */ - void SetSourceAttributes(long fileType, long auxType, - SourceFormat sourceFormat, const char* nameExt); - /* run through the list of reformatters, testing each against the data */ - void TestApplicability(void); - /* get a ReformatApplies value */ - ReformatApplies GetApplic(ReformatPart part, ReformatID id) const; - - /* compare two ReformatApplies values */ - int CompareApplies(ReformatApplies app1, ReformatApplies app2); - - /* find the best reformatter for this part */ - ReformatID FindBest(ReformatPart part); - /* apply the chosen reformatter */ - ReformatOutput* Apply(ReformatPart part, ReformatID id); - - - /* - * Getters & setters. - */ - bool GetReformatAllowed(ReformatID id) const { return fAllow[id]; } - void SetReformatAllowed(ReformatID id, bool val) { fAllow[id] = val; } - long GetOption(OptionID id) const { return fOption[id]; } - void SetOption(OptionID id, long val) { fOption[id] = val; } - - /* use this to force "reformatted" output to show an error instead */ - void SetErrorMsg(ReformatPart part, const char* msg); - - /* give a pointer (allocated with new[]) for one of the inputs */ - void SetSourceBuf(ReformatPart part, unsigned char* buf, - long len); - - static const char* GetReformatName(ReformatID id); - - - /* make these friends so they can call the "protected" stuff below */ - friend class ReformatText; - friend class ReformatGraphics; - friend class ReformatAWGS_WP; - friend class ReformatTeach; - friend class ReformatGWP; - friend class ReformatMagicWindow; - friend class ReformatAWP; - friend class ReformatADB; - friend class ReformatASP; - friend class ReformatSCAssem; - friend class ReformatMerlin; - friend class ReformatLISA2; - friend class ReformatLISA3; - friend class ReformatLISA4; - friend class ReformatDisasm8; - friend class ReformatDisasm16; - friend class ReformatApplesoft; - friend class ReformatInteger; - friend class ReformatCPMText; - friend class ReformatDirectory; - friend class ReformatDHR; - friend class ReformatHiRes; - friend class ReformatMacPaint; - friend class ReformatPascalCode; - friend class ReformatPascalText; - friend class ReformatResourceFork; - friend class ReformatRaw; - friend class ReformatHexDump; - friend class ReformatEOL_HA; - friend class ReformatUnpackedSHR; - friend class ReformatJEQSHR; - friend class ReformatPaintworksSHR; - friend class ReformatPackedSHR; - friend class ReformatAPFSHR; - friend class Reformat3200SHR; - friend class Reformat3201SHR; - friend class DreamGrafix; - friend class ReformatDG256SHR; - friend class ReformatDG3200SHR; - friend class ReformatPrintShop; - -protected: - /* - * Functions for the use of reformatters. - */ - /* set the applicability level */ - void SetApplic(ReformatID id, ReformatApplies applyData, - ReformatApplies applyRsrc, ReformatApplies applyCmmt); - /* set the "preferred" flag on all non-"not" entries */ - void SetApplicPreferred(ReformatID id, ReformatPart part = kPartUnknown); - - const unsigned char* GetSourceBuf(ReformatPart part) const; - long GetSourceLen(ReformatPart part) const; - - long GetFileType(void) const { return fFileType; } - long GetAuxType(void) const { return fAuxType; } - SourceFormat GetSourceFormat(void) const { return fSourceFormat; } - const char* GetNameExt(void) const { return fNameExt; } - -private: - /* - * Utility functions. - */ - static Reformat* GetReformatInstance(ReformatID id); - - /* set by app: which reformatters are allowed? */ - bool fAllow[kReformatMAX]; - - /* set by app: various options */ - long fOption[kOptMAX]; - - /* set by TestApplicability: which tests work with this data? */ - ReformatApplies fApplies[kPartMAX][kReformatMAX]; - - /* file attributes, used to determine applicability */ - long fFileType; - long fAuxType; - SourceFormat fSourceFormat; - char* fNameExt; // guaranteed non-nil - - /* input goes here */ - unsigned char* fSourceBuf[kPartMAX]; - long fSourceLen[kPartMAX]; - - char* fErrorBuf[kPartMAX]; -}; - -/* - * This holds reformatted (or raw) output. - */ -class ReformatOutput { -public: - /* what form does the reformatted data take */ - typedef enum OutputKind { - kOutputUnknown = 0, - kOutputRaw, // reformatting not applied - kOutputErrorMsg, // text is an error message - kOutputText, - kOutputRTF, - kOutputCSV, - kOutputBitmap, - } OutputKind; - - ReformatOutput(void) : - fOutputKind(kOutputUnknown), - //fOutputID - fOutputFormatDescr(_T("(none)")), - fMultipleFonts(false), - fTextBuf(nil), - fTextLen(-1), - fDoDeleteText(true), - fpDIB(nil) - {} - virtual ~ReformatOutput(void) { - if (fDoDeleteText) - delete[] fTextBuf; - delete fpDIB; - } - - /* - * Getters, used by all. - */ - OutputKind GetOutputKind(void) const { return fOutputKind; } - const char* GetTextBuf(void) const { return fTextBuf; } - long GetTextLen(void) const { return fTextLen; } - const MyDIBitmap* GetDIB(void) const { return fpDIB; } - const char* GetFormatDescr(void) const { return fOutputFormatDescr; } - // multiple-font flag currently not used - bool GetMultipleFontsFlag(void) const { return fMultipleFonts; } - - /* - * Setters, used by reformatters. - */ - /* set the format description; string must be persistent (static) */ - void SetFormatDescr(const char* str) { fOutputFormatDescr = str; } - /* set the kind of output we're providing */ - void SetOutputKind(OutputKind kind) { fOutputKind = kind; } - void SetMultipleFontsFlag(bool val) { fMultipleFonts = val; } - - /* set the output */ - void SetTextBuf(char* buf, long len, bool doDelete) { - assert(fTextBuf == nil); - fTextBuf = buf; - fTextLen = len; - fDoDeleteText = doDelete; - } - - void SetDIB(MyDIBitmap* pDIB) { - ASSERT(fpDIB == nil); - fpDIB = pDIB; - } - -private: - /* what we're holding */ - OutputKind fOutputKind; - //ReformatID fOutputID; - const char* fOutputFormatDescr; - - /* output RTF uses multiple fonts, so ignore font change request */ - bool fMultipleFonts; - - /* storage; either fTextBuf or fpDIB will be nil */ - char* fTextBuf; - long fTextLen; - bool fDoDeleteText; - MyDIBitmap* fpDIB; - - //char* fErrorMsg; - //ReformatPart fLastPart; - //bool fErrorMessage; // output buffer holds an error msg -}; - - -/* - * Static namespace for some NiftyList lookup functions. Do not instantiate. - */ -class NiftyList { -public: - // one-time initialization - static bool AppInit(const char* fileName); - // one-time cleanup - static bool AppCleanup(void); - - static const char* LookupP8MLI(unsigned char code) { - return Lookup(fP8MLI, code); - } - static const char* LookupGSOS(unsigned short code) { - return Lookup(fGSOS, code); - } - static const char* LookupToolbox(unsigned short req) { - return Lookup(fSystemTools, req); - } - static const char* LookupE1Vector(unsigned short addr) { - return Lookup(fE1Vectors, addr); - } - static const char* LookupE0Vector(unsigned short addr) { - return Lookup(fE0Vectors, addr); - } - static const char* Lookup00Addr(unsigned short addr) { - //if (addr < 0xc000) - // return nil; // ignore Davex Bxxx values - return Lookup(f00Addrs, addr); - } - static const char* Lookup01Vector(unsigned short addr) { - return Lookup(f01Vectors, addr); - } - -private: - NiftyList(void) { assert(false); } - ~NiftyList(void) {} - - /* - * Structures for holding data. - */ - typedef struct NameValue { - const char* name; - unsigned short value; - } NameValue; - typedef struct DataSet { - long numEntries; - NameValue* pEntries; - } DataSet; - - static DataSet fP8MLI; - static DataSet fGSOS; - static DataSet fSystemTools; - static DataSet fE1Vectors; - static DataSet fE0Vectors; - static DataSet f00Addrs; - static DataSet f01Vectors; - - typedef enum LoadMode { - kModeUnknown = 0, - kModeNormal, - kModeSkip, - //kModeByteSwap, - } LoadMode; - static bool ReadSection(char** ppData, long* pRemLen, DataSet* pSet, - LoadMode mode); - static int ScanLine(const char* pData, long remLen); - static int SortNameValue(const void *, const void *); - static unsigned short ConvHexFour(const char* data); - static void DumpSection(const DataSet& dataSet); - - static const char* Lookup(const DataSet& dataSet, unsigned short key); - - /* we sit on a copy of the entire file */ - static char* fFileData; - - // make sure app calls AppInit - static bool fDataReady; -}; - -#endif /*__LR_REFORMAT__*/ +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * File reformatter class. Used to encapsulate working state and RTF + * knowledge while rewriting a file. + * + * Currently missing: a way to provide progress updates when reformatting + * a large file. + */ +#ifndef __LR_REFORMAT__ +#define __LR_REFORMAT__ + +#include "../util/UtilLib.h" + +class Reformat; +class ReformatOutput; + + +/* + * This holds all three file parts (data, resource, comment) for use by the + * reformatters. + * + * The "source" buffers are owned by this class, and will be freed when + * the object is deleted. + * + * Typical calling sequence: + * - Prep: + * - Allocate object + * - Load parts into source buffers + * - MainWindow::ConfigureReformatFromPreferences() + * - SetSourceAttributes() + * - TestApplicability() + * - Action: + * - id = FindBest(part) + * - pOutput = Apply(part, id) + * - Cleanup: + * - Destroy ReformatOutput (when done with part) + * - Destroy ReformatHolder (when done with all parts) + * + * When adding new formatters: + * Any additions here need to be reflected in the switch statements in + * GetReformatInstance() and GetReformatName(), and added to the set of + * things allowed by preferences in ConfigureReformatFromPreferences. + * New classes must also be added to the list of friends, below. + */ +class ReformatHolder { +public: + /* + * Reformatters, including minor variants. + * + * TextEOL_HA and Raw must be the first two entries. If you change these, + * you may also need to adjust the way extraction is handled over in + * DoBulkExtract. The extract code depends on getting "raw" data back + * for files that don't have a better reformatter. + */ + typedef enum ReformatID { + kReformatUnknown = 0, + + /* don't change the order of these! */ + kReformatTextEOL_HA, // default for unknown types (if enabled) + kReformatRaw, // backup default + kReformatHexDump, + + /* from here on, only order within sub-groups matters */ + kReformatResourceFork, + + kReformatPascalText, + kReformatPascalCode, + + kReformatCPMText, + + kReformatApplesoft, + kReformatApplesoft_Hilite, + kReformatInteger, + kReformatInteger_Hilite, + kReformatBusiness, + kReformatBusiness_Hilite, + + kReformatSCAssem, + kReformatMerlin, + kReformatLISA2, + kReformatLISA3, + kReformatLISA4, + + kReformatMonitor8, + kReformatDisasmMerlin8, + kReformatMonitor16Long, + kReformatMonitor16Short, + kReformatDisasmOrcam16, + + kReformatAWGS_WP, + kReformatTeach, + kReformatGWP, + + kReformatMagicWindow, + + kReformatAWP, + kReformatADB, + kReformatASP, + + kReformatHiRes, + kReformatHiRes_BW, + + kReformatDHR_Latched, + kReformatDHR_BW, + kReformatDHR_Window, + kReformatDHR_Plain140, + + kReformatProDOSDirectory, + + kReformatSHR_PIC, + kReformatSHR_JEQ, + kReformatSHR_Paintworks, + kReformatSHR_Packed, + kReformatSHR_APF, + kReformatSHR_3200, + kReformatSHR_3201, + kReformatSHR_DG256, + kReformatSHR_DG3200, + + kReformatPrintShop, + + kReformatMacPaint, + + kReformatMAX // must be last + } ReformatID; + + /* + * Set options. Each value is a "long". + */ + typedef enum OptionID { + kOptUnknown = 0, + + kOptHiliteHexDump, + kOptHiliteBASIC, + kOptHiResBW, + kOptDHRAlgorithm, + kOptRelaxGfxTypeCheck, + kOptOneByteBrkCop, + + kOptMAX // must be last + } OptionID; + + /* + * Each reformatter examines the input and determines its applicability. + * This ranges from "impossible" to being the preferred sub-variant of + * a verified converter. + * + * Thought: could set kApplicPreferred to 0x01, use even values for + * the enumeration, and then just do a numeric sort. + */ + typedef enum ReformatApplies { + kApplicUnknown = 0, // not set + kApplicNot, // does not apply + kApplicProbablyNot, // allow, but don't use as default, ever + kApplicAlways, // generic, always-applicable (e.g. hex) + kApplicMaybe, // might work + kApplicProbably, // should work, based on size and type + kApplicYes, // contents look right + kApplicMAX, // must be at end, not including bits + + kApplicPreferred = 0x80, // tag preferred variation + kApplicPrefMask = 0x7f, // mask off "preferred" flag + } ReformatApplies; + + /* which part of the file are we targetting? */ + typedef enum ReformatPart { + kPartUnknown = -1, + kPartData = 0, + kPartRsrc, + kPartCmmt, + kPartMAX // must be last + } ReformatPart; + + /* + * This is more or less a restatement of DiskImg::FSFormat. We do this so + * we don't force everybody to include DiskImg.h, especially since there's + * only a couple of interesting cases. + * + * We want to know if it's DOS so we can relax some file-type checking, + * and we want to know if it's CP/M so we can adjust the way we think + * about text files. + */ + typedef enum SourceFormat { + kSourceFormatGeneric = 0, + kSourceFormatDOS, + kSourceFormatCPM, + } SourceFormat; + + + /* + * Construct/destruct our object. + */ + ReformatHolder(void) { + int i; + for (int part = 0; part < kPartMAX; part++) { + if (part == kPartUnknown) + continue; + for (i = 0; i < kReformatMAX; i++) + fApplies[part][i] = kApplicUnknown; + fSourceBuf[part] = nil; + fSourceLen[part] = nil; + fErrorBuf[part] = nil; + } + for (i = 0; i < kReformatMAX; i++) { + fAllow[i] = false; + } + for (i = 0; i < kOptMAX; i++) + fOption[i] = 0; + + fFileType = fAuxType = 0; + fSourceFormat = kSourceFormatGeneric; + fNameExt = nil; + } + ~ReformatHolder(void) { + WMSG0("In ~ReformatHolder\n"); + for (int i = 0; i < kPartMAX; i++) { + if (i == kPartUnknown) + continue; + delete[] fSourceBuf[i]; + delete[] fErrorBuf[i]; + } + delete[] fNameExt; + } + + /* set attributes before calling TestApplicability */ + void SetSourceAttributes(long fileType, long auxType, + SourceFormat sourceFormat, const char* nameExt); + /* run through the list of reformatters, testing each against the data */ + void TestApplicability(void); + /* get a ReformatApplies value */ + ReformatApplies GetApplic(ReformatPart part, ReformatID id) const; + + /* compare two ReformatApplies values */ + int CompareApplies(ReformatApplies app1, ReformatApplies app2); + + /* find the best reformatter for this part */ + ReformatID FindBest(ReformatPart part); + /* apply the chosen reformatter */ + ReformatOutput* Apply(ReformatPart part, ReformatID id); + + + /* + * Getters & setters. + */ + bool GetReformatAllowed(ReformatID id) const { return fAllow[id]; } + void SetReformatAllowed(ReformatID id, bool val) { fAllow[id] = val; } + long GetOption(OptionID id) const { return fOption[id]; } + void SetOption(OptionID id, long val) { fOption[id] = val; } + + /* use this to force "reformatted" output to show an error instead */ + void SetErrorMsg(ReformatPart part, const char* msg); + + /* give a pointer (allocated with new[]) for one of the inputs */ + void SetSourceBuf(ReformatPart part, unsigned char* buf, + long len); + + static const char* GetReformatName(ReformatID id); + + + /* make these friends so they can call the "protected" stuff below */ + friend class ReformatText; + friend class ReformatGraphics; + friend class ReformatAWGS_WP; + friend class ReformatTeach; + friend class ReformatGWP; + friend class ReformatMagicWindow; + friend class ReformatAWP; + friend class ReformatADB; + friend class ReformatASP; + friend class ReformatSCAssem; + friend class ReformatMerlin; + friend class ReformatLISA2; + friend class ReformatLISA3; + friend class ReformatLISA4; + friend class ReformatDisasm8; + friend class ReformatDisasm16; + friend class ReformatApplesoft; + friend class ReformatInteger; + friend class ReformatBusiness; + friend class ReformatCPMText; + friend class ReformatDirectory; + friend class ReformatDHR; + friend class ReformatHiRes; + friend class ReformatMacPaint; + friend class ReformatPascalCode; + friend class ReformatPascalText; + friend class ReformatResourceFork; + friend class ReformatRaw; + friend class ReformatHexDump; + friend class ReformatEOL_HA; + friend class ReformatUnpackedSHR; + friend class ReformatJEQSHR; + friend class ReformatPaintworksSHR; + friend class ReformatPackedSHR; + friend class ReformatAPFSHR; + friend class Reformat3200SHR; + friend class Reformat3201SHR; + friend class DreamGrafix; + friend class ReformatDG256SHR; + friend class ReformatDG3200SHR; + friend class ReformatPrintShop; + +protected: + /* + * Functions for the use of reformatters. + */ + /* set the applicability level */ + void SetApplic(ReformatID id, ReformatApplies applyData, + ReformatApplies applyRsrc, ReformatApplies applyCmmt); + /* set the "preferred" flag on all non-"not" entries */ + void SetApplicPreferred(ReformatID id, ReformatPart part = kPartUnknown); + + const unsigned char* GetSourceBuf(ReformatPart part) const; + long GetSourceLen(ReformatPart part) const; + + long GetFileType(void) const { return fFileType; } + long GetAuxType(void) const { return fAuxType; } + SourceFormat GetSourceFormat(void) const { return fSourceFormat; } + const char* GetNameExt(void) const { return fNameExt; } + +private: + /* + * Utility functions. + */ + static Reformat* GetReformatInstance(ReformatID id); + + /* set by app: which reformatters are allowed? */ + bool fAllow[kReformatMAX]; + + /* set by app: various options */ + long fOption[kOptMAX]; + + /* set by TestApplicability: which tests work with this data? */ + ReformatApplies fApplies[kPartMAX][kReformatMAX]; + + /* file attributes, used to determine applicability */ + long fFileType; + long fAuxType; + SourceFormat fSourceFormat; + char* fNameExt; // guaranteed non-nil + + /* input goes here */ + unsigned char* fSourceBuf[kPartMAX]; + long fSourceLen[kPartMAX]; + + char* fErrorBuf[kPartMAX]; +}; + +/* + * This holds reformatted (or raw) output. + */ +class ReformatOutput { +public: + /* what form does the reformatted data take */ + typedef enum OutputKind { + kOutputUnknown = 0, + kOutputRaw, // reformatting not applied + kOutputErrorMsg, // text is an error message + kOutputText, + kOutputRTF, + kOutputCSV, + kOutputBitmap, + } OutputKind; + + ReformatOutput(void) : + fOutputKind(kOutputUnknown), + //fOutputID + fOutputFormatDescr(_T("(none)")), + fMultipleFonts(false), + fTextBuf(nil), + fTextLen(-1), + fDoDeleteText(true), + fpDIB(nil) + {} + virtual ~ReformatOutput(void) { + if (fDoDeleteText) + delete[] fTextBuf; + delete fpDIB; + } + + /* + * Getters, used by all. + */ + OutputKind GetOutputKind(void) const { return fOutputKind; } + const char* GetTextBuf(void) const { return fTextBuf; } + long GetTextLen(void) const { return fTextLen; } + const MyDIBitmap* GetDIB(void) const { return fpDIB; } + const char* GetFormatDescr(void) const { return fOutputFormatDescr; } + // multiple-font flag currently not used + bool GetMultipleFontsFlag(void) const { return fMultipleFonts; } + + /* + * Setters, used by reformatters. + */ + /* set the format description; string must be persistent (static) */ + void SetFormatDescr(const char* str) { fOutputFormatDescr = str; } + /* set the kind of output we're providing */ + void SetOutputKind(OutputKind kind) { fOutputKind = kind; } + void SetMultipleFontsFlag(bool val) { fMultipleFonts = val; } + + /* set the output */ + void SetTextBuf(char* buf, long len, bool doDelete) { + assert(fTextBuf == nil); + fTextBuf = buf; + fTextLen = len; + fDoDeleteText = doDelete; + } + + void SetDIB(MyDIBitmap* pDIB) { + ASSERT(fpDIB == nil); + fpDIB = pDIB; + } + +private: + /* what we're holding */ + OutputKind fOutputKind; + //ReformatID fOutputID; + const char* fOutputFormatDescr; + + /* output RTF uses multiple fonts, so ignore font change request */ + bool fMultipleFonts; + + /* storage; either fTextBuf or fpDIB will be nil */ + char* fTextBuf; + long fTextLen; + bool fDoDeleteText; + MyDIBitmap* fpDIB; + + //char* fErrorMsg; + //ReformatPart fLastPart; + //bool fErrorMessage; // output buffer holds an error msg +}; + + +/* + * Static namespace for some NiftyList lookup functions. Do not instantiate. + */ +class NiftyList { +public: + // one-time initialization + static bool AppInit(const char* fileName); + // one-time cleanup + static bool AppCleanup(void); + + static const char* LookupP8MLI(unsigned char code) { + return Lookup(fP8MLI, code); + } + static const char* LookupGSOS(unsigned short code) { + return Lookup(fGSOS, code); + } + static const char* LookupToolbox(unsigned short req) { + return Lookup(fSystemTools, req); + } + static const char* LookupE1Vector(unsigned short addr) { + return Lookup(fE1Vectors, addr); + } + static const char* LookupE0Vector(unsigned short addr) { + return Lookup(fE0Vectors, addr); + } + static const char* Lookup00Addr(unsigned short addr) { + //if (addr < 0xc000) + // return nil; // ignore Davex Bxxx values + return Lookup(f00Addrs, addr); + } + static const char* Lookup01Vector(unsigned short addr) { + return Lookup(f01Vectors, addr); + } + +private: + NiftyList(void) { assert(false); } + ~NiftyList(void) {} + + /* + * Structures for holding data. + */ + typedef struct NameValue { + const char* name; + unsigned short value; + } NameValue; + typedef struct DataSet { + long numEntries; + NameValue* pEntries; + } DataSet; + + static DataSet fP8MLI; + static DataSet fGSOS; + static DataSet fSystemTools; + static DataSet fE1Vectors; + static DataSet fE0Vectors; + static DataSet f00Addrs; + static DataSet f01Vectors; + + typedef enum LoadMode { + kModeUnknown = 0, + kModeNormal, + kModeSkip, + //kModeByteSwap, + } LoadMode; + static bool ReadSection(char** ppData, long* pRemLen, DataSet* pSet, + LoadMode mode); + static int ScanLine(const char* pData, long remLen); + static int SortNameValue(const void *, const void *); + static unsigned short ConvHexFour(const char* data); + static void DumpSection(const DataSet& dataSet); + + static const char* Lookup(const DataSet& dataSet, unsigned short key); + + /* we sit on a copy of the entire file */ + static char* fFileData; + + // make sure app calls AppInit + static bool fDataReady; +}; + +#endif /*__LR_REFORMAT__*/ diff --git a/reformat/ReformatBase.h b/reformat/ReformatBase.h index 0e99f86..5370c08 100644 --- a/reformat/ReformatBase.h +++ b/reformat/ReformatBase.h @@ -1,387 +1,388 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Reformatter base classes. The main app does not need this header file. - * - * Every converter turns the source into text or graphics. Currently it's - * not possible to convert something into a mix of both. We could change - * that, but we'd have to figure out what that means when extracting a file - * (i.e. figure out the RTF embedded bitmap format). - */ -#ifndef __LR_REFORMAT_BASE__ -#define __LR_REFORMAT_BASE__ - -#include "Reformat.h" - -#define BufPrintf fExpBuf.Printf - - -/* - * Abstract base class for reformatting a file into readable text. - * - * The transmuted version is written on top of the original, or is allocated - * in new[]ed storage and replaces the original (which is delete[]d). - */ -class Reformat { -public: - Reformat(void) {} - virtual ~Reformat(void) {} - - enum { - kTypePCD = 0x02, - kTypePTX = 0x03, - kTypeTXT = 0x04, - kTypeBIN = 0x06, - kTypeFOT = 0x08, - kTypeDIR = 0x0f, - kTypeADB = 0x19, - kTypeAWP = 0x1a, - kTypeASP = 0x1b, - kType8OB = 0x2b, - kTypeP8C = 0x2e, - kTypeGWP = 0x50, - kTypeOBJ = 0xb1, - kTypeLIB = 0xb2, - kTypeFST = 0xbd, - kTypePNT = 0xc0, - kTypePIC = 0xc1, - kTypeCMD = 0xf0, - kTypeDOS_B = 0xf4, // alternate 'B' - kTypeOS = 0xf9, - kTypeINT = 0xfa, - kTypeBAS = 0xfc, - kTypeSYS = 0xff, - }; - - /* test applicability of all file parts */ - virtual void Examine(ReformatHolder* pHolder) = 0; - - /* reformat appropriately; returns 0 on success, -1 on error */ - virtual int Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput) = 0; - - // grab the next 8 bits - static inline unsigned char Read8(const unsigned char** pBuf, long* pLength) { - if (*pLength > 0) { - (*pLength)--; - return *(*pBuf)++; - } else { - // ought to throw an exception here - ASSERT(false); - return (unsigned char) -1; - } - } - // grab a 16-bit little-endian value - static inline unsigned short Read16(const unsigned char** pBuf, long* pLength) { - unsigned short val; - if (*pLength >= 2) { - val = *(*pBuf)++; - val |= *(*pBuf)++ << 8; - *pLength -= 2; - } else { - // ought to throw an exception here - ASSERT(false); - val = (unsigned short) -1; - } - return val; - } - // grab a 16-bit little-endian value - static inline unsigned long Read32(const unsigned char** pBuf, long* pLength) { - unsigned long val; - if (*pLength >= 4) { - val = *(*pBuf)++; - val |= *(*pBuf)++ << 8; - val |= *(*pBuf)++ << 16; - val |= *(*pBuf)++ << 24; - *pLength -= 4; - } else { - // ought to throw an exception here - ASSERT(false); - val = (unsigned long) -1; - } - return val; - } - - static inline unsigned short Get16LE(const unsigned char* buf) { - return *buf | *(buf+1) << 8; - } - static inline unsigned long Get32LE(const unsigned char* buf) { - return *buf | *(buf+1) << 8 | *(buf+2) << 16 | *(buf+3) << 24; - } - static inline unsigned short Get16BE(const unsigned char* buf) { - return *buf << 8 | *(buf+1); - } - static inline unsigned long Get32BE(const unsigned char* buf) { - return *buf << 24 | *(buf+1) << 16 | *(buf+2) << 8 | *(buf+3); - } - static inline unsigned short Get16(const unsigned char* buf, bool littleEndian) { - if (littleEndian) - return Get16LE(buf); - else - return Get16BE(buf); - } - static inline unsigned long Get32(const unsigned char* buf, bool littleEndian) { - if (littleEndian) - return Get32LE(buf); - else - return Get32BE(buf); - } -}; - -/* - * Abstract base class for reformatting a graphics file into a - * device-independent bitmap.. - */ -class ReformatGraphics: public Reformat { -public: - ReformatGraphics() { - InitPalette(); - } - -protected: - void SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib); - - /* - * Color palette to use for color conversions. We store it here - * so it can be configured to suit the user's tastes. - */ - enum { kPaletteBlack, kPaletteRed, kPaletteDarkBlue, - kPalettePurple, kPaletteDarkGreen, kPaletteDarkGrey, - kPaletteMediumBlue, kPaletteLightBlue, kPaletteBrown, - kPaletteOrange, kPaletteLightGrey, kPalettePink, - kPaletteGreen, kPaletteYellow, kPaletteAqua, - kPaletteWhite, kPaletteSize }; - RGBQUAD fPalette[kPaletteSize]; - - int UnpackBytes(unsigned char* dst, const unsigned char* src, - long dstRem, long srcLen); - void UnPackBits(const unsigned char** pSrcBuf, long* pSrcLen, - unsigned char** pOutPtr, long dstLen, unsigned char xorVal); - -private: - void InitPalette(); -}; - -/* - * Abstract base class for reformatting a file into readable text. - * - * Includes an expanding buffer that can be appended to, and a set of RTF - * primitives for adding structure. - */ -class ReformatText : public Reformat { -public: - typedef enum ParagraphJustify { - kJustifyLeft, - kJustifyRight, - kJustifyCenter, - kJustifyFull, - } ParagraphJustify; - - ReformatText(void) { - fUseRTF = true; - fLeftMargin = fRightMargin = 0; - fPointSize = fPreMultPointSize = 8; - fGSFontSizeMult = 1.0; - fJustified = kJustifyLeft; - fBoldEnabled = fItalicEnabled = fUnderlineEnabled = - fSuperscriptEnabled = fSubscriptEnabled = false; - fTextColor = kColorNone; - }; - virtual ~ReformatText(void) {} - - /* - * The numeric values are determined by the RTF header that we output. - * If the header in RTFBegin() changes, update these values. - */ - typedef enum RTFFont { - // basic fonts (one monospace, one proportional, Courier New default) - kFontCourierNew = 0, - kFontTimesRoman = 1, - kFontArial = 2, - kFontSymbol = 3, - } RTFFont; - typedef enum { - kColorNone = 0, - // full colors (RGB 0 or 255) - kColorBlack = 1, - kColorBlue = 2, - kColorCyan = 3, - kColorGreen = 4, // a little bright for white bkgnd - kColorPink = 5, - kColorRed = 6, - kColorYellow = 7, - kColorWhite = 8, - // mixed colors - kColorMediumBlue = 9, - kColorMediumAqua = 10, - kColorMediumGreen = 11, - kColorMagena = 12, - kColorMediumRed = 13, - kColorOlive = 14, - kColorMediumGrey = 15, - kColorLightGrey = 16, - kColorDarkGrey = 17, - kColorOrange = 18, - } TextColor; - - /* Apple IIgs families */ - typedef enum GSFontFamily { - // standard fonts, defined in toolbox ref - kGSFontNewYork = 0x0002, - kGSFontGeneva = 0x0003, // sans-sarif, like Arial - kGSFontMonaco = 0x0004, // monospace - kGSFontVenice = 0x0005, // script font - kGSFontLondon = 0x0006, - kGSFontAthens = 0x0007, - kGSFontSanFran = 0x0008, - kGSFontToronto = 0x0009, - kGSFontCairo = 0x000b, - kGSFontLosAngeles = 0x000c, - kGSFontTimes = 0x0014, // sarif, equal to Times New Roman - kGSFontHelvetica = 0x0015, // sans-sarif, equal to Arial - kGSFontCourier = 0x0016, // monospace, sarif, Courier - kGSFontSymbol = 0x0017, - kGSFontTaliesin = 0x0018, - // I had these installed, by apps or by Pointless - kGSFontStarfleet = 0x078d, - kGSFontWestern = 0x088e, - kGSFontGenoa = 0x0bcb, - kGSFontClassical = 0x2baa, - kGSFontChicago = 0x3fff, - kGSFontGenesys = 0x7530, - kGSFontPCMonospace = 0x7fdc, // monospace, sans-sarif - kGSFontAppleM = 0x7f58, // looks like classic Apple II font - kGSFontUnknown1 = 0x9c50, // found in French AWGS doc "CONVSEC" - kGSFontUnknown2 = 0x9c54, // found in French AWGS doc "CONVSEC" - // ROM font - kGSFontShaston = 0xfffe, // rounded sans-sarif - } GSFontFamily; - - /* QuickDraw II font styles; this is a bit mask */ - typedef enum QDFontStyle { - kQDStyleBold = 0x01, - kQDStyleItalic = 0x02, - kQDStyleUnderline = 0x04, - kQDStyleOutline = 0x08, - kQDStyleShadow = 0x10, - kQDStyleReserved = 0x20, - kQDStyleSuperscript = 0x40, // not in QDII -- AWGS only - kQDStyleSubscript = 0x80, // not in QDII -- AWGS only - } QDFontStyle; - - /* flags for RTFBegin */ - enum { - kRTFFlagColorTable = 1, // include color table - }; - -protected: - void RTFBegin(int flags = 0); - void RTFEnd(void); - void RTFSetPara(void); - void RTFNewPara(void); - void RTFPageBreak(void); - void RTFTab(void); - void RTFBoldOn(void); - void RTFBoldOff(void); - void RTFItalicOn(void); - void RTFItalicOff(void); - void RTFUnderlineOn(void); - void RTFUnderlineOff(void); - void RTFParaLeft(void); - void RTFParaRight(void); - void RTFParaCenter(void); - void RTFParaJustify(void); - void RTFLeftMargin(int margin); - void RTFRightMargin(int margin); - //void RTFSetMargins(void); - void RTFSubscriptOn(void); - void RTFSubscriptOff(void); - void RTFSuperscriptOn(void); - void RTFSuperscriptOff(void); - void RTFSetColor(TextColor color); - void RTFSetFont(RTFFont font); - void RTFSetFontSize(int points); - void RTFSetGSFont(unsigned short family); - void RTFSetGSFontSize(int points); - void RTFSetGSFontStyle(unsigned char qdStyle); -// void RTFProportionalOn(void); -// void RTFProportionalOff(void); - - void ConvertEOL(const unsigned char* srcBuf, long srcLen, - bool stripHiBits); - void BufHexDump(const unsigned char* srcBuf, long srcLen); - void SetResultBuffer(ReformatOutput* pOutput, bool multiFont = false); - - ExpandBuffer fExpBuf; - bool fUseRTF; - - // 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 < 0x7f) - return ch; - else if (ch < 0xa0 || ch == 0xff) // 0xff becomes 0x7f - return '.'; - else - return ch & 0x7f; - } - // output an RTF-escaped char (do we want to trap Ctrl-Z?) - // (only use this if we're in RTF mode) - inline void RTFPrintChar(unsigned char ch) { - ch = PrintableChar(ch); - RTFPrintExtChar(ch); - } - // output an RTF-escaped char, allowing high ASCII - // (only use this if we're in RTF mode) - inline void RTFPrintExtChar(unsigned char ch) { - if (ch == '\\') - fExpBuf.Printf("\\\\"); - else if (ch == '{') - fExpBuf.Printf("\\{"); - else if (ch == '}') - fExpBuf.Printf("\\}"); - else - fExpBuf.Printf("%c", ch); - } - // output a char, doubling up double quotes (for .CSV) - inline void BufPrintQChar(unsigned char ch) { - if (ch == '"') - fExpBuf.Printf("\"\""); - else - fExpBuf.Printf("%c", ch); - } - - // convert IIgs documents - unsigned char ConvertGSChar(unsigned char ch) { - if (ch < 128) - return ch; - else - return kGSCharConv[ch-128]; - } - void CheckGSCharConv(void); - -private: - int CreateWorkBuf(void); - enum { kRTFUnitsPerInch = 1440 }; // TWIPS - - static const unsigned char kGSCharConv[]; - - int fLeftMargin, fRightMargin; // for documents, in 1/10th inch - int fPointSize; - int fPreMultPointSize; - float fGSFontSizeMult; - bool fBoldEnabled; - bool fItalicEnabled; - bool fUnderlineEnabled; - bool fSuperscriptEnabled; - bool fSubscriptEnabled; - ParagraphJustify fJustified; - TextColor fTextColor; -}; - -#endif /*__LR_REFORMAT_BASE__*/ +/* + * CiderPress + * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ +/* + * Reformatter base classes. The main app does not need this header file. + * + * Every converter turns the source into text or graphics. Currently it's + * not possible to convert something into a mix of both. We could change + * that, but we'd have to figure out what that means when extracting a file + * (i.e. figure out the RTF embedded bitmap format). + */ +#ifndef __LR_REFORMAT_BASE__ +#define __LR_REFORMAT_BASE__ + +#include "Reformat.h" + +#define BufPrintf fExpBuf.Printf + + +/* + * Abstract base class for reformatting a file into readable text. + * + * The transmuted version is written on top of the original, or is allocated + * in new[]ed storage and replaces the original (which is delete[]d). + */ +class Reformat { +public: + Reformat(void) {} + virtual ~Reformat(void) {} + + enum { + kTypePCD = 0x02, + kTypePTX = 0x03, + kTypeTXT = 0x04, + kTypeBIN = 0x06, + kTypeFOT = 0x08, + kTypeBA3 = 0x09, + kTypeDIR = 0x0f, + kTypeADB = 0x19, + kTypeAWP = 0x1a, + kTypeASP = 0x1b, + kType8OB = 0x2b, + kTypeP8C = 0x2e, + kTypeGWP = 0x50, + kTypeOBJ = 0xb1, + kTypeLIB = 0xb2, + kTypeFST = 0xbd, + kTypePNT = 0xc0, + kTypePIC = 0xc1, + kTypeCMD = 0xf0, + kTypeDOS_B = 0xf4, // alternate 'B' + kTypeOS = 0xf9, + kTypeINT = 0xfa, + kTypeBAS = 0xfc, + kTypeSYS = 0xff, + }; + + /* test applicability of all file parts */ + virtual void Examine(ReformatHolder* pHolder) = 0; + + /* reformat appropriately; returns 0 on success, -1 on error */ + virtual int Process(const ReformatHolder* pHolder, + ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, + ReformatOutput* pOutput) = 0; + + // grab the next 8 bits + static inline unsigned char Read8(const unsigned char** pBuf, long* pLength) { + if (*pLength > 0) { + (*pLength)--; + return *(*pBuf)++; + } else { + // ought to throw an exception here + ASSERT(false); + return (unsigned char) -1; + } + } + // grab a 16-bit little-endian value + static inline unsigned short Read16(const unsigned char** pBuf, long* pLength) { + unsigned short val; + if (*pLength >= 2) { + val = *(*pBuf)++; + val |= *(*pBuf)++ << 8; + *pLength -= 2; + } else { + // ought to throw an exception here + ASSERT(false); + val = (unsigned short) -1; + } + return val; + } + // grab a 16-bit little-endian value + static inline unsigned long Read32(const unsigned char** pBuf, long* pLength) { + unsigned long val; + if (*pLength >= 4) { + val = *(*pBuf)++; + val |= *(*pBuf)++ << 8; + val |= *(*pBuf)++ << 16; + val |= *(*pBuf)++ << 24; + *pLength -= 4; + } else { + // ought to throw an exception here + ASSERT(false); + val = (unsigned long) -1; + } + return val; + } + + static inline unsigned short Get16LE(const unsigned char* buf) { + return *buf | *(buf+1) << 8; + } + static inline unsigned long Get32LE(const unsigned char* buf) { + return *buf | *(buf+1) << 8 | *(buf+2) << 16 | *(buf+3) << 24; + } + static inline unsigned short Get16BE(const unsigned char* buf) { + return *buf << 8 | *(buf+1); + } + static inline unsigned long Get32BE(const unsigned char* buf) { + return *buf << 24 | *(buf+1) << 16 | *(buf+2) << 8 | *(buf+3); + } + static inline unsigned short Get16(const unsigned char* buf, bool littleEndian) { + if (littleEndian) + return Get16LE(buf); + else + return Get16BE(buf); + } + static inline unsigned long Get32(const unsigned char* buf, bool littleEndian) { + if (littleEndian) + return Get32LE(buf); + else + return Get32BE(buf); + } +}; + +/* + * Abstract base class for reformatting a graphics file into a + * device-independent bitmap.. + */ +class ReformatGraphics: public Reformat { +public: + ReformatGraphics() { + InitPalette(); + } + +protected: + void SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib); + + /* + * Color palette to use for color conversions. We store it here + * so it can be configured to suit the user's tastes. + */ + enum { kPaletteBlack, kPaletteRed, kPaletteDarkBlue, + kPalettePurple, kPaletteDarkGreen, kPaletteDarkGrey, + kPaletteMediumBlue, kPaletteLightBlue, kPaletteBrown, + kPaletteOrange, kPaletteLightGrey, kPalettePink, + kPaletteGreen, kPaletteYellow, kPaletteAqua, + kPaletteWhite, kPaletteSize }; + RGBQUAD fPalette[kPaletteSize]; + + int UnpackBytes(unsigned char* dst, const unsigned char* src, + long dstRem, long srcLen); + void UnPackBits(const unsigned char** pSrcBuf, long* pSrcLen, + unsigned char** pOutPtr, long dstLen, unsigned char xorVal); + +private: + void InitPalette(); +}; + +/* + * Abstract base class for reformatting a file into readable text. + * + * Includes an expanding buffer that can be appended to, and a set of RTF + * primitives for adding structure. + */ +class ReformatText : public Reformat { +public: + typedef enum ParagraphJustify { + kJustifyLeft, + kJustifyRight, + kJustifyCenter, + kJustifyFull, + } ParagraphJustify; + + ReformatText(void) { + fUseRTF = true; + fLeftMargin = fRightMargin = 0; + fPointSize = fPreMultPointSize = 8; + fGSFontSizeMult = 1.0; + fJustified = kJustifyLeft; + fBoldEnabled = fItalicEnabled = fUnderlineEnabled = + fSuperscriptEnabled = fSubscriptEnabled = false; + fTextColor = kColorNone; + }; + virtual ~ReformatText(void) {} + + /* + * The numeric values are determined by the RTF header that we output. + * If the header in RTFBegin() changes, update these values. + */ + typedef enum RTFFont { + // basic fonts (one monospace, one proportional, Courier New default) + kFontCourierNew = 0, + kFontTimesRoman = 1, + kFontArial = 2, + kFontSymbol = 3, + } RTFFont; + typedef enum { + kColorNone = 0, + // full colors (RGB 0 or 255) + kColorBlack = 1, + kColorBlue = 2, + kColorCyan = 3, + kColorGreen = 4, // a little bright for white bkgnd + kColorPink = 5, + kColorRed = 6, + kColorYellow = 7, + kColorWhite = 8, + // mixed colors + kColorMediumBlue = 9, + kColorMediumAqua = 10, + kColorMediumGreen = 11, + kColorMagena = 12, + kColorMediumRed = 13, + kColorOlive = 14, + kColorMediumGrey = 15, + kColorLightGrey = 16, + kColorDarkGrey = 17, + kColorOrange = 18, + } TextColor; + + /* Apple IIgs families */ + typedef enum GSFontFamily { + // standard fonts, defined in toolbox ref + kGSFontNewYork = 0x0002, + kGSFontGeneva = 0x0003, // sans-sarif, like Arial + kGSFontMonaco = 0x0004, // monospace + kGSFontVenice = 0x0005, // script font + kGSFontLondon = 0x0006, + kGSFontAthens = 0x0007, + kGSFontSanFran = 0x0008, + kGSFontToronto = 0x0009, + kGSFontCairo = 0x000b, + kGSFontLosAngeles = 0x000c, + kGSFontTimes = 0x0014, // sarif, equal to Times New Roman + kGSFontHelvetica = 0x0015, // sans-sarif, equal to Arial + kGSFontCourier = 0x0016, // monospace, sarif, Courier + kGSFontSymbol = 0x0017, + kGSFontTaliesin = 0x0018, + // I had these installed, by apps or by Pointless + kGSFontStarfleet = 0x078d, + kGSFontWestern = 0x088e, + kGSFontGenoa = 0x0bcb, + kGSFontClassical = 0x2baa, + kGSFontChicago = 0x3fff, + kGSFontGenesys = 0x7530, + kGSFontPCMonospace = 0x7fdc, // monospace, sans-sarif + kGSFontAppleM = 0x7f58, // looks like classic Apple II font + kGSFontUnknown1 = 0x9c50, // found in French AWGS doc "CONVSEC" + kGSFontUnknown2 = 0x9c54, // found in French AWGS doc "CONVSEC" + // ROM font + kGSFontShaston = 0xfffe, // rounded sans-sarif + } GSFontFamily; + + /* QuickDraw II font styles; this is a bit mask */ + typedef enum QDFontStyle { + kQDStyleBold = 0x01, + kQDStyleItalic = 0x02, + kQDStyleUnderline = 0x04, + kQDStyleOutline = 0x08, + kQDStyleShadow = 0x10, + kQDStyleReserved = 0x20, + kQDStyleSuperscript = 0x40, // not in QDII -- AWGS only + kQDStyleSubscript = 0x80, // not in QDII -- AWGS only + } QDFontStyle; + + /* flags for RTFBegin */ + enum { + kRTFFlagColorTable = 1, // include color table + }; + +protected: + void RTFBegin(int flags = 0); + void RTFEnd(void); + void RTFSetPara(void); + void RTFNewPara(void); + void RTFPageBreak(void); + void RTFTab(void); + void RTFBoldOn(void); + void RTFBoldOff(void); + void RTFItalicOn(void); + void RTFItalicOff(void); + void RTFUnderlineOn(void); + void RTFUnderlineOff(void); + void RTFParaLeft(void); + void RTFParaRight(void); + void RTFParaCenter(void); + void RTFParaJustify(void); + void RTFLeftMargin(int margin); + void RTFRightMargin(int margin); + //void RTFSetMargins(void); + void RTFSubscriptOn(void); + void RTFSubscriptOff(void); + void RTFSuperscriptOn(void); + void RTFSuperscriptOff(void); + void RTFSetColor(TextColor color); + void RTFSetFont(RTFFont font); + void RTFSetFontSize(int points); + void RTFSetGSFont(unsigned short family); + void RTFSetGSFontSize(int points); + void RTFSetGSFontStyle(unsigned char qdStyle); +// void RTFProportionalOn(void); +// void RTFProportionalOff(void); + + void ConvertEOL(const unsigned char* srcBuf, long srcLen, + bool stripHiBits); + void BufHexDump(const unsigned char* srcBuf, long srcLen); + void SetResultBuffer(ReformatOutput* pOutput, bool multiFont = false); + + ExpandBuffer fExpBuf; + bool fUseRTF; + + // 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 < 0x7f) + return ch; + else if (ch < 0xa0 || ch == 0xff) // 0xff becomes 0x7f + return '.'; + else + return ch & 0x7f; + } + // output an RTF-escaped char (do we want to trap Ctrl-Z?) + // (only use this if we're in RTF mode) + inline void RTFPrintChar(unsigned char ch) { + ch = PrintableChar(ch); + RTFPrintExtChar(ch); + } + // output an RTF-escaped char, allowing high ASCII + // (only use this if we're in RTF mode) + inline void RTFPrintExtChar(unsigned char ch) { + if (ch == '\\') + fExpBuf.Printf("\\\\"); + else if (ch == '{') + fExpBuf.Printf("\\{"); + else if (ch == '}') + fExpBuf.Printf("\\}"); + else + fExpBuf.Printf("%c", ch); + } + // output a char, doubling up double quotes (for .CSV) + inline void BufPrintQChar(unsigned char ch) { + if (ch == '"') + fExpBuf.Printf("\"\""); + else + fExpBuf.Printf("%c", ch); + } + + // convert IIgs documents + unsigned char ConvertGSChar(unsigned char ch) { + if (ch < 128) + return ch; + else + return kGSCharConv[ch-128]; + } + void CheckGSCharConv(void); + +private: + int CreateWorkBuf(void); + enum { kRTFUnitsPerInch = 1440 }; // TWIPS + + static const unsigned char kGSCharConv[]; + + int fLeftMargin, fRightMargin; // for documents, in 1/10th inch + int fPointSize; + int fPreMultPointSize; + float fGSFontSizeMult; + bool fBoldEnabled; + bool fItalicEnabled; + bool fUnderlineEnabled; + bool fSuperscriptEnabled; + bool fSubscriptEnabled; + ParagraphJustify fJustified; + TextColor fTextColor; +}; + +#endif /*__LR_REFORMAT_BASE__*/