/* * CiderPress * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. * 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 REFORMAT_REFORMAT_H #define REFORMAT_REFORMAT_H #include "../util/UtilLib.h" #include 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, kReformatGutenberg, 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, kOptMouseTextToASCII, 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. We want to know if it's Gutenberg because they only * have one type of file, and it's indistinguishable from any other text file! */ typedef enum SourceFormat { kSourceFormatGeneric = 0, kSourceFormatDOS, kSourceFormatCPM, kSourceFormatGutenberg, } SourceFormat; /* * Construct/destruct our object. */ ReformatHolder(void) : fApplies(), fFileType(0), fAuxType(0), fSourceFormat(kSourceFormatGeneric), fNameExt(NULL), fSourceBuf(), fSourceLen(), fErrorBuf() { 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] = NULL; fSourceLen[part] = 0; fErrorBuf[part] = NULL; } for (i = 0; i < kReformatMAX; i++) { fAllow[i] = false; } for (i = 0; i < kOptMAX; i++) fOption[i] = 0; } ~ReformatHolder(void) { LOGI("In ~ReformatHolder"); for (int i = 0; i < kPartMAX; i++) { if (i == kPartUnknown) continue; delete[] fSourceBuf[i]; delete[] fErrorBuf[i]; } delete[] fNameExt; } /** * Set attributes before calling TestApplicability. We want the * Apple II filename, so the name extension is passed as a narrow string. */ 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); void SetErrorMsg(ReformatPart part, const CString& str); /* give a pointer (allocated with new[]) for one of the inputs */ void SetSourceBuf(ReformatPart part, uint8_t* buf, long len); static const WCHAR* 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 ReformatGutenberg; 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 uint8_t* 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: DECLARE_COPY_AND_OPEQ(ReformatHolder) /* * 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-NULL /* input goes here */ uint8_t* 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(L"(none)"), fMultipleFonts(false), fTextBuf(NULL), fTextLen(-1), fDoDeleteText(true), fpDIB(NULL) {} 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 WCHAR* 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 WCHAR* 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 */ // TODO: split into two different functions, one takes const char* and // doesn't delete, the other char* and does delete void SetTextBuf(char* buf, long len, bool doDelete) { assert(fTextBuf == NULL); fTextBuf = buf; fTextLen = len; fDoDeleteText = doDelete; } void SetDIB(MyDIBitmap* pDIB) { ASSERT(fpDIB == NULL); fpDIB = pDIB; } private: DECLARE_COPY_AND_OPEQ(ReformatOutput) /* what we're holding */ OutputKind fOutputKind; //ReformatID fOutputID; const WCHAR* fOutputFormatDescr; /* output RTF uses multiple fonts, so ignore font change request */ bool fMultipleFonts; /* storage; either fTextBuf or fpDIB will be NULL */ 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 WCHAR* fileName); // one-time cleanup static bool AppCleanup(void); static const char* LookupP8MLI(uint8_t code) { return Lookup(fP8MLI, code); } static const char* LookupGSOS(uint16_t code) { return Lookup(fGSOS, code); } static const char* LookupToolbox(uint16_t req) { return Lookup(fSystemTools, req); } static const char* LookupE1Vector(uint16_t addr) { return Lookup(fE1Vectors, addr); } static const char* LookupE0Vector(uint16_t addr) { return Lookup(fE0Vectors, addr); } static const char* Lookup00Addr(uint16_t addr) { //if (addr < 0xc000) // return NULL; // ignore Davex Bxxx values return Lookup(f00Addrs, addr); } static const char* Lookup01Vector(uint16_t addr) { return Lookup(f01Vectors, addr); } private: NiftyList(void); // do not instantiate ~NiftyList(void); /* * Structures for holding data. */ typedef struct NameValue { const char* name; uint16_t 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 uint16_t ConvHexFour(const char* data); static void DumpSection(const DataSet& dataSet); static const char* Lookup(const DataSet& dataSet, uint16_t key); /* we sit on a copy of the entire file */ static char* fFileData; // make sure app calls AppInit static bool fDataReady; }; #endif /*REFORMAT_REFORMAT_H*/