ciderpress/reformat/Teach.cpp
Andy McFadden 8f61f84585 Use types with explicit sizes
Much of what the "reformat" code does involves processing data that is
8, 16, or 32 bits.  We want to use size-specific types from stdint.h
(e.g. uint16_t) rather than "unsigned short".

This was a quick pass to replace the various "unsigned" declarations.
More can be done here and elsewhere.
2014-11-20 18:10:18 -08:00

421 lines
12 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Reformat "generic" GWP and Teach files.
*/
#include "StdAfx.h"
#include "Teach.h"
#include "ResourceFork.h"
/*
* ==========================================================================
* ReformatGWP
* ==========================================================================
*/
/*
* Decide whether or not we want to treat this file as a "generic" IIgs
* document. Possibly useful for converting the special characters. We'll
* activate it for any GWP file other than the two known types.
*/
void
ReformatGWP::Examine(ReformatHolder* pHolder)
{
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
if (pHolder->GetFileType() == kTypeGWP &&
(pHolder->GetAuxType() != 0x5445 && pHolder->GetAuxType() != 0x8010))
{
applies = ReformatHolder::kApplicProbably;
}
pHolder->SetApplic(ReformatHolder::kReformatGWP, applies,
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}
/*
* Convert GWP into formatted text.
*/
int
ReformatGWP::Process(const ReformatHolder* pHolder,
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
ReformatOutput* pOutput)
{
const uint8_t* srcBuf = pHolder->GetSourceBuf(part);
long srcLen = pHolder->GetSourceLen(part);
fUseRTF = false;
CheckGSCharConv();
RTFBegin();
/* convert EOL markers and IIgs characters */
uint8_t ch;
while (srcLen) {
ch = *srcBuf++;
srcLen--;
if (ch == '\r') {
/* got CR, check for CRLF -- not really expected on IIgs */
if (srcLen != 0 && *srcBuf == '\n') {
srcBuf++;
srcLen--;
}
BufPrintf("\r\n");
} else if (ch == '\n') {
BufPrintf("\r\n");
} else {
// RTF is always off, so just use BufPrintf
BufPrintf("%c", ConvertGSChar(ch));
}
}
RTFEnd();
SetResultBuffer(pOutput);
return 0;
}
/*
* ==========================================================================
* ReformatTeach
* ==========================================================================
*/
/*
* Decide whether or not we want to handle this file.
*/
void
ReformatTeach::Examine(ReformatHolder* pHolder)
{
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
if (pHolder->GetFileType() == kTypeGWP && pHolder->GetAuxType() == 0x5445)
applies = ReformatHolder::kApplicYes;
pHolder->SetApplic(ReformatHolder::kReformatTeach, applies,
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}
/*
* Convert Teach Text into formatted text.
*
* The text is in the data fork and the formatting is in the resource fork.
*/
int
ReformatTeach::Process(const ReformatHolder* pHolder,
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
ReformatOutput* pOutput)
{
const uint8_t* dataBuf;
const uint8_t* rsrcBuf;
long dataLen, rsrcLen;
const uint8_t* styleBlock;
long styleLen;
if (part != ReformatHolder::kPartData)
return -1;
dataBuf = pHolder->GetSourceBuf(ReformatHolder::kPartData);
dataLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
rsrcBuf = pHolder->GetSourceBuf(ReformatHolder::kPartRsrc);
rsrcLen = pHolder->GetSourceLen(ReformatHolder::kPartRsrc);
if (dataBuf == NULL || rsrcBuf == NULL || dataLen <= 0 || rsrcLen <= 0) {
LOGI("Teach reformatter missing one fork of the file");
return -1;
}
CheckGSCharConv();
/* find the rStyleBlock */
if (!ReformatResourceFork::GetResource(rsrcBuf, rsrcLen, 0x8012, 0x0001,
&styleBlock, &styleLen))
{
LOGI("Resource fork of Teach Text file not found");
return -1;
}
RStyleBlock rStyleBlock;
if (!rStyleBlock.Create(styleBlock, styleLen)) {
LOGI("Unable to unpack rStyleBlock");
return -1;
}
fUseRTF = true;
RTFBegin();
/*
* Configure ruler options. The format allows us to have more than one
* ruler, but doesn't provide a way to select between them, so we have
* to assume that the first ruler is global.
*
* In practice, the Teach application doesn't provide a way to set
* margins or tab stops, so there's really not much to do. It seems to
* create tab stops with 64-pixel widths, which translates to 10 tab
* stops per page. WordPad seems to default to 12, so I figure there's
* not much point in futzing with it since the fonts are all different
* anyway.
*/
RStyleBlock::TERuler* pRuler = rStyleBlock.GetRuler(0);
assert(pRuler != NULL);
if (pRuler->GetJustification() != RStyleBlock::TERuler::kJustLeft) {
LOGI("WARNING: not using left justified Teach text");
/* ignore it */
}
for (int style = 0; style < rStyleBlock.GetNumStyleItems(); style++) {
RStyleBlock::StyleItem* pStyleItem;
RStyleBlock::TEStyle* pStyle;
int numBytes;
/* set up the style */
pStyleItem = rStyleBlock.GetStyleItem(style);
assert(pStyleItem != NULL);
if (pStyleItem->GetLength() == RStyleBlock::StyleItem::kUnusedItem)
continue;
numBytes = pStyleItem->GetLength();
pStyle = rStyleBlock.GetStyle(pStyleItem->GetStyleIndex());
/*
* Set the font first; that defines the point size multiplier. Set
* the size second, because the point size determines whether we
* show underline. Set the style last.
*/
RTFSetGSFont(pStyle->GetFontFamily());
RTFSetGSFontSize(pStyle->GetFontSize());
RTFSetGSFontStyle(pStyle->GetTextStyle());
/*
* The Teach app doesn't let you set text colors, but it appears
* there's a way to do it, since the SpyHunterGS Read.Me file has
* a bit of red in it.
*/
if (pStyle->GetTextColor() != 0) {
// do something with color, someday
}
/* output the characters */
uint8_t uch;
while (numBytes--) {
if (!dataLen) {
LOGI("WARNING: Teach underrun (%ld wanted)", numBytes);
break;
}
uch = *dataBuf;
if (uch == '\r') {
RTFNewPara();
} else if (uch == '\t') {
RTFTab();
} else {
RTFPrintExtChar(ConvertGSChar(uch));
}
dataBuf++;
dataLen--;
}
}
if (dataLen) {
LOGI("WARNING: Teach overrun (%ld remain)", dataLen);
/* no big deal */
}
RTFEnd();
SetResultBuffer(pOutput, true);
return 0;
}
/*
* ==========================================================================
* RStyleBlock functions
* ==========================================================================
*/
/*
* Unpack an rStyleBlock resource.
*/
bool
RStyleBlock::Create(const uint8_t* buf, long len)
{
uint16_t version;
uint32_t partLen;
int i;
assert(buf != NULL);
if (len < kMinLen) {
LOGI("Too short to be rStyleBlock (%d)", len);
return false;
}
version = Reformat::Read16(&buf, &len);
if (version != kExpectedVersion) {
LOGI("Bad rStyleBlock version (%d)", version);
return false;
}
/* extract ruler(s) */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (uint32_t) (len+8)) {
/* not enough to satisfy data + two more counts */
LOGI("Invalid part1 length (%d vs %d)", partLen, len);
return false;
}
fNumRulers = 1;
fpRulers = new TERuler[fNumRulers];
if (fpRulers == NULL)
return false;
if (fpRulers->Create(buf, partLen) <= 0)
return false;
buf += partLen;
len -= partLen;
/* extract TEStyles */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (uint32_t) (len + 4)) {
LOGI("Invalid part2 length (%d vs %d)", partLen, len);
return false;
}
if ((partLen % TEStyle::kDataLen) != 0) {
LOGI("Invalid part2 length (%d mod %d)",
partLen, TEStyle::kDataLen);
return false;
}
fNumStyles = partLen / TEStyle::kDataLen;
fpStyles = new TEStyle[fNumStyles];
if (fpStyles == NULL)
return false;
for (i = 0; i < fNumStyles; i++) {
fpStyles[i].Create(buf);
buf += TEStyle::kDataLen;
len -= TEStyle::kDataLen;
}
/* extract StyleItems */
fNumStyleItems = (int) Reformat::Read32(&buf, &len);
partLen = fNumStyleItems * StyleItem::kItemDataLen;
if (partLen > (uint32_t) len) {
LOGI("Invalid part3 length (%d vs %d)", partLen, len);
return false;
}
fpStyleItems = new StyleItem[fNumStyleItems];
if (fpStyleItems == NULL)
return false;
for (i = 0; i < fNumStyleItems; i++) {
fpStyleItems[i].Create(buf);
if ((fpStyleItems[i].GetOffset() % TEStyle::kDataLen) != 0) {
LOGI("Invalid offset %d (mod %d)",
fpStyleItems[i].GetOffset(), TEStyle::kDataLen);
return false;
}
buf += StyleItem::kItemDataLen;
len -= StyleItem::kItemDataLen;
}
if (len != 0) {
LOGI("WARNING: at end of rStyleBlock, len is %ld", len);
}
return true;
}
/*
* Construct a TERuler from a chunk of data.
*
* Returns the #of bytes consumed, or -1 on failure.
*/
int
RStyleBlock::TERuler::Create(const uint8_t* buf, long len)
{
long origLen = len;
if (len < kMinLen)
return -1;
fLeftMargin = Reformat::Get16LE(buf);
fLeftIndent = Reformat::Get16LE(buf + 2);
fRightMargin = Reformat::Get16LE(buf + 4);
fJust = Reformat::Get16LE(buf + 6);
fExtraLS = Reformat::Get16LE(buf + 8);
fFlags = Reformat::Get16LE(buf + 10);
fUserData = Reformat::Get32LE(buf + 12);
fTabType = Reformat::Get16LE(buf + 16);
buf += 18;
len -= 18;
/*
* What we do now depends on the value of fTabType:
* 0: no tabs are set, there's nothing else to read.
* 1: regularly-spaced tabs; spacing defined by fTabTerminator field.
* 2: irregular tabs, defined by array terminated by 0xffff in the
* fTabTerminator field.
*
* At present we're just throwing the tab array out.
*/
switch (fTabType) {
case 0:
fTabTerminator = 0;
break;
case 1:
fTabTerminator = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
break;
case 2:
while (len >= 2) {
TabItem tabItem;
tabItem.tabKind = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
if (tabItem.tabKind == kTabArrayEnd || len < 2)
break;
tabItem.tabData = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
}
break;
default:
LOGI("Invalid tab type %d", fTabType);
return -1;
}
LOGI("TERuler consumed %ld bytes (%ld left over)", origLen - len, len);
return origLen - len;
}
/*
* Extract a TEStyle object from the buffer.
*/
void
RStyleBlock::TEStyle::Create(const uint8_t* buf)
{
fFontID = Reformat::Get32LE(buf);
fForeColor = Reformat::Get16LE(buf + 4);
fBackColor = Reformat::Get16LE(buf + 6);
fUserData = Reformat::Get32LE(buf + 8);
LOGI(" TEStyle: font fam=0x%04x size=%-2d style=0x%02x fore=0x%04x",
GetFontFamily(), GetFontSize(), GetTextStyle(), fForeColor);
}
/*
* Extract a StyleItem object from the buffer.
*/
void
RStyleBlock::StyleItem::Create(const uint8_t* buf)
{
fLength = Reformat::Get32LE(buf);
fOffset = Reformat::Get32LE(buf + 4);
LOGI(" StyleItem: len=%ld off=%ld", fLength, fOffset);
}