mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-27 08:49:20 +00:00
5e9d8f0102
The test for "is this an S-C Assembler source file" tried to dereference a null pointer when asked to examine a file with a zero-length data fork. The test only fires for files with type=INT and auxType=0, so this is pretty hard to hit. The specific failing case had a damaged file with the appropriate file type. Issue #42
536 lines
17 KiB
C++
536 lines
17 KiB
C++
/*
|
|
* 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 <stdint.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,
|
|
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) {
|
|
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;
|
|
|
|
fFileType = fAuxType = 0;
|
|
fSourceFormat = kSourceFormatGeneric;
|
|
fNameExt = NULL;
|
|
}
|
|
~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*/
|