mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-25 03:33:22 +01:00
2873 lines
60 KiB
C++
2873 lines
60 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Chunky file compiler and decompiler class implementations.
|
|
|
|
***************************************************************************/
|
|
#include "kidframe.h" //because we need scrcomg
|
|
ASSERTNAME
|
|
|
|
RTCLASS(CHCM)
|
|
RTCLASS(CHLX)
|
|
RTCLASS(CHDC)
|
|
|
|
|
|
PSZ _mpertpsz[] =
|
|
{
|
|
PszLit("no error"),
|
|
PszLit("Internal allocation error"), //ertOom
|
|
PszLit("Can't open the given file"), //ertOpenFile
|
|
PszLit("Can't read the given metafile"), //ertReadMeta
|
|
PszLit("Number not in range for BYTE"), //ertRangeByte
|
|
PszLit("Number not in range for SHORT"), //ertRangeShort
|
|
PszLit("Invalid data before atomic chunk"), //ertBufData
|
|
PszLit("Open parenthesis '(' expected"), //ertParenOpen
|
|
PszLit("Unexpected end of file"), //ertEof
|
|
PszLit("String expected"), //ertNeedString
|
|
PszLit("Numeric value expected"), //ertNeedNumber
|
|
PszLit("Unexpected Token"), //ertBadToken
|
|
PszLit("Close parenthesis ')' expected"), //ertParenClose
|
|
PszLit("Invalid CHUNK declaration"), //ertChunkHead
|
|
PszLit("Duplicate CHUNK declaration"), //ertDupChunk
|
|
PszLit("Invalid CHILD declaration"), //ertBodyChildHead
|
|
PszLit("Child chunk doesn't exist"), //ertChildMissing
|
|
PszLit("A cycle would be created by this adoption"), //ertCycle
|
|
PszLit("Invalid PARENT declaration"), //ertBodyParentHead
|
|
PszLit("Parent chunk doesn't exist"), //ertParentMissing
|
|
PszLit("Alignment parameter out of range"), //ertBodyAlignRange
|
|
PszLit("File name expected"), //ertBodyFile
|
|
PszLit("ENDCHUNK expected"), //ertNeedEndChunk
|
|
PszLit("Invalid GL or AL declaration"), //ertListHead
|
|
PszLit("Invalid size for list entries"), //ertListEntrySize
|
|
PszLit("Variable undefined"), //ertVarUndefined
|
|
PszLit("Too much data for item"), //ertItemOverflow
|
|
PszLit("Can't have a free item in a general collection"), //ertBadFree
|
|
PszLit("Syntax error"), //ertSyntax
|
|
PszLit("Invalid GG or AG declaration"), //ertGroupHead
|
|
PszLit("Invalid size for fixed group data"), //ertGroupEntrySize
|
|
PszLit("Invalid GST or AST declaration"), //ertGstHead
|
|
PszLit("Invalid size for extra string table data"), //ertGstEntrySize
|
|
PszLit("Script compilation failed"), //ertScript
|
|
PszLit("Invalid ADOPT declaration"), //ertAdoptHead
|
|
PszLit("CHUNK declaration expected"), //ertNeedChunk
|
|
PszLit("Invalid BITMAP declaration"), //ertBodyBitmapHead
|
|
PszLit("Can't read the given bitmap file"), //ertReadBitmap
|
|
PszLit("Disassembling the script failed"), //ertBadScript
|
|
PszLit("Can't read the given cursor file"), //ertReadCursor
|
|
PszLit("Can't read given file as a packed file"), //ertPackedFile
|
|
PszLit("Can't read the given midi file"), //ertReadMidi
|
|
PszLit("Bad pack format"), //ertBadPackFmt
|
|
PszLit("Illegal LONER primitive in SUBFILE"), //ertLonerInSub
|
|
PszLit("Unterminated SUBFILE"), //ertNoEndSubFile
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the CHCM class.
|
|
***************************************************************************/
|
|
CHCM::CHCM(void)
|
|
{
|
|
_pglcsfc = pvNil;
|
|
_pcfl = pvNil;
|
|
_pchlx = pvNil;
|
|
_pglckiLoner = pvNil;
|
|
_pmsnkError = pvNil;
|
|
_cactError = 0;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the CHCM class.
|
|
***************************************************************************/
|
|
CHCM::~CHCM(void)
|
|
{
|
|
if (pvNil != _pglcsfc)
|
|
{
|
|
CSFC csfc;
|
|
|
|
while (_pglcsfc->FPop(&csfc))
|
|
{
|
|
ReleasePpo(&_pcfl);
|
|
_pcfl = csfc.pcfl;
|
|
}
|
|
ReleasePpo(&_pglcsfc);
|
|
}
|
|
ReleasePpo(&_pcfl);
|
|
ReleasePpo(&_pchlx);
|
|
ReleasePpo(&_pglckiLoner);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert that the CHCM is a valid object.
|
|
***************************************************************************/
|
|
void CHCM::AssertValid(ulong grf)
|
|
{
|
|
CHCM_PAR::AssertValid(grf);
|
|
AssertNilOrPo(_pcfl, 0);
|
|
AssertPo(&_bsf, 0);
|
|
AssertNilOrPo(_pchlx, 0);
|
|
AssertNilOrPo(_pglckiLoner, 0);
|
|
AssertNilOrPo(_pmsnkError, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the CHCM object.
|
|
***************************************************************************/
|
|
void CHCM::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
CHCM_PAR::MarkMem();
|
|
MarkMemObj(_pglcsfc);
|
|
MarkMemObj(_pcfl);
|
|
MarkMemObj(&_bsf);
|
|
MarkMemObj(_pchlx);
|
|
MarkMemObj(_pglckiLoner);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Registers an error, prints error message with filename and line number.
|
|
pszMessage may be nil.
|
|
***************************************************************************/
|
|
void CHCM::_Error(long ert, PSZ pszMessage)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ert, ertNil, ertLim);
|
|
AssertPo(_pchlx, 0);
|
|
STN stnFile;
|
|
STN stn;
|
|
|
|
_pchlx->GetStnFile(&stnFile);
|
|
if (ertNil == ert)
|
|
{
|
|
stn.FFormatSz(
|
|
pszMessage == pvNil ? PszLit("%s(%d:%d) : warning") :
|
|
PszLit("%s(%d:%d) : warning : %z"),
|
|
&stnFile, _pchlx->LwLine(), _pchlx->IchLine(), pszMessage);
|
|
}
|
|
else
|
|
{
|
|
_cactError++;
|
|
stn.FFormatSz(
|
|
pszMessage == pvNil ? PszLit("%s(%d:%d) : error : %z") :
|
|
PszLit("%s(%d:%d) : error : %z : %z"),
|
|
&stnFile, _pchlx->LwLine(), _pchlx->IchLine(),
|
|
_mpertpsz[ert], pszMessage);
|
|
}
|
|
|
|
_pmsnkError->ReportLine(stn.Psz());
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Checks that lw could be accepted under the current numerical mode.
|
|
***************************************************************************/
|
|
void CHCM::_GetRgbFromLw(long lw, byte *prgb)
|
|
{
|
|
AssertThis(0);
|
|
AssertPvCb(prgb, size(long));
|
|
|
|
switch(_cbNum)
|
|
{
|
|
case size(byte):
|
|
if (lw < -128 || lw > kbMax)
|
|
_Error(ertRangeByte);
|
|
prgb[0] = B0Lw(lw);
|
|
break;
|
|
|
|
case size(short):
|
|
if ((lw < kswMin) || (lw > ksuMax))
|
|
_Error(ertRangeShort);
|
|
*(short *)prgb = SwLow(lw);
|
|
break;
|
|
|
|
default:
|
|
Assert(_cbNum == size(long), "invalid numerical mode");
|
|
_cbNum = size(long);
|
|
*(long *)prgb = lw;
|
|
break;
|
|
}
|
|
|
|
if (_bo != kboCur && _cbNum > 1)
|
|
ReversePb(prgb, _cbNum);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Checks if data is already in the buffer (and issues an error) for a
|
|
non-buffer command such as metafile import.
|
|
***************************************************************************/
|
|
void CHCM::_ErrorOnData(PSZ pszPreceed)
|
|
{
|
|
AssertThis(0);
|
|
AssertSz(pszPreceed);
|
|
|
|
if (_bsf.IbMac() > 0)
|
|
{
|
|
// already data
|
|
_Error(ertBufData, pszPreceed);
|
|
|
|
// clear buffer
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a token, automatically handling mode change commands and negatives.
|
|
Return true iff *ptok is valid, not whether an error occurred.
|
|
***************************************************************************/
|
|
bool CHCM::_FGetCleanTok(TOK *ptok, bool fEofOk)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ptok);
|
|
|
|
long cactNegate = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (!_pchlx->FGetTokSkipSemi(ptok))
|
|
{
|
|
if (cactNegate > 0)
|
|
_Error(ertSyntax);
|
|
if (!fEofOk)
|
|
_Error(ertEof);
|
|
return fFalse;
|
|
}
|
|
|
|
switch (ptok->tt)
|
|
{
|
|
default:
|
|
if (cactNegate > 0)
|
|
_Error(ertSyntax);
|
|
return fTrue;
|
|
case ttLong:
|
|
if (cactNegate & 1)
|
|
ptok->lw = -ptok->lw;
|
|
return fTrue;
|
|
case ttSub:
|
|
cactNegate++;
|
|
break;
|
|
case ttModeStn:
|
|
_sm = smStn;
|
|
break;
|
|
case ttModeStz:
|
|
_sm = smStz;
|
|
break;
|
|
case ttModeSz:
|
|
_sm = smSz;
|
|
break;
|
|
case ttModeSt:
|
|
_sm = smSt;
|
|
break;
|
|
case ttModeByte:
|
|
_cbNum = size(byte);
|
|
break;
|
|
case ttModeShort:
|
|
_cbNum = size(short);
|
|
break;
|
|
case ttModeLong:
|
|
_cbNum = size(long);
|
|
break;
|
|
case ttMacBo:
|
|
_bo = MacWin(kboCur, kboOther);
|
|
break;
|
|
case ttWinBo:
|
|
_bo = MacWin(kboOther, kboCur);
|
|
break;
|
|
case ttMacOsk:
|
|
_osk = koskMac;
|
|
break;
|
|
case ttWinOsk:
|
|
_osk = koskWin;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Skip tokens until we encounter the given token type.
|
|
***************************************************************************/
|
|
void CHCM::_SkipPastTok(long tt)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
|
|
while (_FGetCleanTok(&tok) && tt != tok.tt)
|
|
;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a parenthesized header from the source file.
|
|
***************************************************************************/
|
|
bool CHCM::_FParseParenHeader(PHP *prgphp, long cphpMax, long *pcphp)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cphpMax, 1, kcbMax);
|
|
AssertPvCb(prgphp, LwMul(cphpMax, size(PHP)));
|
|
AssertVarMem(pcphp);
|
|
|
|
TOK tok;
|
|
long iphp;
|
|
|
|
if (!_pchlx->FGetTok(&tok))
|
|
{
|
|
TrashVar(pcphp);
|
|
return fFalse;
|
|
}
|
|
|
|
if (ttOpenParen != tok.tt)
|
|
{
|
|
_Error(ertParenOpen);
|
|
goto LFail;
|
|
}
|
|
|
|
for (iphp = 0; iphp < cphpMax; iphp++)
|
|
{
|
|
AssertNilOrPo(prgphp[iphp].pstn, 0);
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
{
|
|
TrashVar(pcphp);
|
|
return fFalse;
|
|
}
|
|
|
|
if (ttCloseParen == tok.tt)
|
|
{
|
|
// close paren = end of header
|
|
*pcphp = iphp;
|
|
|
|
// empty remaining strings
|
|
for ( ; iphp < cphpMax; iphp++)
|
|
{
|
|
AssertNilOrPo(prgphp[iphp].pstn, 0);
|
|
if (pvNil != prgphp[iphp].pstn)
|
|
prgphp[iphp].pstn->SetNil();
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
if (ttLong == tok.tt)
|
|
{
|
|
// numerical value
|
|
if (prgphp[iphp].pstn == pvNil)
|
|
prgphp[iphp].lw = tok.lw;
|
|
else
|
|
{
|
|
_Error(ertNeedString);
|
|
prgphp[iphp].pstn->SetNil();
|
|
}
|
|
}
|
|
else if (ttString == tok.tt)
|
|
{
|
|
// string
|
|
if (prgphp[iphp].pstn != pvNil)
|
|
*prgphp[iphp].pstn = tok.stn;
|
|
else
|
|
{
|
|
_Error(ertNeedNumber);
|
|
prgphp[iphp].lw = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// invalid token in header
|
|
_Error(ertBadToken);
|
|
}
|
|
}
|
|
|
|
// get closing paren
|
|
if (!_pchlx->FGetTok(&tok))
|
|
{
|
|
TrashVar(pcphp);
|
|
return fFalse;
|
|
}
|
|
|
|
if (ttCloseParen != tok.tt)
|
|
{
|
|
_Error(ertParenClose);
|
|
LFail:
|
|
_SkipPastTok(ttCloseParen);
|
|
return fFalse;
|
|
}
|
|
|
|
*pcphp = cphpMax;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a chunk header from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseChunkHeader(CTG *pctg, CNO *pcno)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pctg);
|
|
AssertVarMem(pcno);
|
|
|
|
STN stnChunkName;
|
|
PHP rgphp[3];
|
|
long cphp;
|
|
|
|
ClearPb(rgphp, size(rgphp));
|
|
rgphp[2].pstn = &stnChunkName;
|
|
if (!_FParseParenHeader(rgphp, 3, &cphp) || cphp < 2)
|
|
{
|
|
_Error(ertChunkHead);
|
|
goto LFail;
|
|
}
|
|
|
|
*pctg = rgphp[0].lw;
|
|
*pcno = rgphp[1].lw;
|
|
|
|
// write empty chunk
|
|
if (_pcfl->FFind(*pctg, *pcno))
|
|
{
|
|
// duplicate chunk!
|
|
_Error(ertDupChunk);
|
|
LFail:
|
|
_SkipPastTok(ttEndChunk);
|
|
TrashVar(pctg);
|
|
TrashVar(pcno);
|
|
return;
|
|
}
|
|
|
|
// create the chunk and set its name
|
|
if (!_pcfl->FPutPv(pvNil, 0, *pctg, *pcno) ||
|
|
stnChunkName.Cch() > 0 && !FError() &&
|
|
!_pcfl->FSetName(*pctg, *pcno, &stnChunkName))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Append a string to the chunk data stream.
|
|
***************************************************************************/
|
|
void CHCM::_AppendString(PSTN pstnValue)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstnValue, 0);
|
|
|
|
void *pv;
|
|
long cb;
|
|
byte rgb[kcbMaxDataStn];
|
|
|
|
switch (_sm)
|
|
{
|
|
default:
|
|
Bug("Invalid string mode");
|
|
//fall through
|
|
case smStn:
|
|
cb = pstnValue->CbData();
|
|
pstnValue->GetData(rgb);
|
|
pv = rgb;
|
|
break;
|
|
case smStz:
|
|
pv = pstnValue->Pstz();
|
|
cb = CchTotStz((PSTZ)pv) * size(achar);
|
|
break;
|
|
case smSz:
|
|
pv = pstnValue->Psz();
|
|
cb = CchTotSz((PSZ)pv) * size(achar);
|
|
break;
|
|
case smSt:
|
|
pv = pstnValue->Pst();
|
|
cb = CchTotSt((PST)pv) * size(achar);
|
|
break;
|
|
}
|
|
|
|
if (!FError() && !_bsf.FReplace(pv, cb, _bsf.IbMac(), 0))
|
|
_Error(ertOom);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Stores a numerical value in the chunk data stream.
|
|
***************************************************************************/
|
|
void CHCM::_AppendNumber(long lwValue)
|
|
{
|
|
AssertThis(0);
|
|
byte rgb[size(long)];
|
|
|
|
_GetRgbFromLw(lwValue, rgb);
|
|
if (!FError() && !_bsf.FReplace(rgb, _cbNum, _bsf.IbMac(), 0))
|
|
_Error(ertOom);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a child statement from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyChild(CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
CTG ctgChild;
|
|
CNO cnoChild;
|
|
CHID chid;
|
|
PHP rgphp[3];
|
|
long cphp;
|
|
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 3, &cphp) || cphp < 2)
|
|
{
|
|
_Error(ertBodyChildHead);
|
|
return;
|
|
}
|
|
|
|
ctgChild = rgphp[0].lw;
|
|
cnoChild = rgphp[1].lw;
|
|
chid = rgphp[2].lw;
|
|
|
|
// check if chunk exists
|
|
if (!_pcfl->FFind(ctgChild, cnoChild))
|
|
{
|
|
_Error(ertChildMissing);
|
|
return;
|
|
}
|
|
|
|
// check if cycle would be created
|
|
if (_pcfl->TIsDescendent(ctgChild, cnoChild, ctg, cno) != tNo)
|
|
{
|
|
_Error(ertCycle);
|
|
return;
|
|
}
|
|
|
|
// do the adoption
|
|
if (!FError() && !_pcfl->FAdoptChild(ctg, cno, ctgChild, cnoChild,
|
|
chid, fTrue))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a parent statement from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyParent(CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
CTG ctgParent;
|
|
CNO cnoParent;
|
|
CHID chid;
|
|
PHP rgphp[3];
|
|
long cphp;
|
|
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 3, &cphp) || cphp < 2)
|
|
{
|
|
_Error(ertBodyParentHead);
|
|
return;
|
|
}
|
|
|
|
ctgParent = rgphp[0].lw;
|
|
cnoParent = rgphp[1].lw;
|
|
chid = rgphp[2].lw;
|
|
|
|
// check if chunk exists
|
|
if (!_pcfl->FFind(ctgParent, cnoParent))
|
|
{
|
|
_Error(ertParentMissing);
|
|
return;
|
|
}
|
|
|
|
// check if cycle would be created
|
|
if (_pcfl->TIsDescendent(ctg, cno, ctgParent, cnoParent) != tNo)
|
|
{
|
|
_Error(ertCycle);
|
|
return;
|
|
}
|
|
|
|
// do the adoption
|
|
if (!FError() && !_pcfl->FAdoptChild(ctgParent, cnoParent, ctg, cno,
|
|
chid, fTrue))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse an align statement from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyAlign(void)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
return;
|
|
|
|
if (tok.tt != ttLong)
|
|
{
|
|
_Error(ertNeedNumber);
|
|
return;
|
|
}
|
|
|
|
if (!FIn(tok.lw, kcbMinAlign, kcbMaxAlign + 1))
|
|
{
|
|
STN stn;
|
|
|
|
stn.FFormatSz(PszLit("legal range for alignment is (%d, %d)"),
|
|
kcbMinAlign, kcbMaxAlign);
|
|
_Error(ertBodyAlignRange, stn.Psz());
|
|
return;
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
//actually do the padding
|
|
byte rgb[100];
|
|
long cb;
|
|
long ibMac = _bsf.IbMac();
|
|
long ibMacNew = LwRoundAway(ibMac, tok.lw);
|
|
|
|
AssertIn(ibMacNew, ibMac, ibMac + tok.lw);
|
|
while ((ibMac = _bsf.IbMac()) < ibMacNew)
|
|
{
|
|
cb = LwMin(ibMacNew - ibMac, size(rgb));
|
|
ClearPb(rgb, cb);
|
|
if (!_bsf.FReplace(rgb, cb, ibMac, 0))
|
|
{
|
|
_Error(ertOom);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a file statement from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyFile(void)
|
|
{
|
|
AssertThis(0);
|
|
FNI fni;
|
|
FLO floSrc;
|
|
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (pvNil == (floSrc.pfil = FIL::PfilOpen(&fni)))
|
|
{
|
|
_Error(ertOpenFile);
|
|
return;
|
|
}
|
|
|
|
floSrc.fp = 0;
|
|
floSrc.cb = floSrc.pfil->FpMac();
|
|
if (!_bsf.FReplaceFlo(&floSrc, fFalse, _bsf.IbMac(), 0))
|
|
_Error(ertOom);
|
|
|
|
ReleasePpo(&floSrc.pfil);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Start a write operation. If fPack is true, allocate a temporary block.
|
|
Otherwise, get the block on the CFL. The caller should write its data
|
|
into the pblck, then call _FEndWrite to complete the operation.
|
|
***************************************************************************/
|
|
bool CHCM::_FPrepWrite(bool fPack, long cb, CTG ctg, CNO cno, PBLCK pblck)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, 0);
|
|
|
|
if (fPack)
|
|
{
|
|
pblck->Free();
|
|
Assert(!pblck->FPacked(), "why is block packed?");
|
|
return pblck->FSetTemp(cb);
|
|
}
|
|
|
|
return _pcfl->FPut(cb, ctg, cno, pblck);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Balances a call to _FPrepWrite.
|
|
***************************************************************************/
|
|
bool CHCM::_FEndWrite(bool fPack, CTG ctg, CNO cno, PBLCK pblck)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, fblckUnpacked);
|
|
|
|
if (fPack)
|
|
{
|
|
// we don't fail if we can't compress it
|
|
pblck->FPackData();
|
|
return _pcfl->FPutBlck(pblck, ctg, cno);
|
|
}
|
|
|
|
AssertPo(pblck, fblckFile);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a metafile import command from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyMeta(bool fPack, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
FNI fni;
|
|
BLCK blck;
|
|
PPIC ppic;
|
|
TOK tok;
|
|
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (pvNil == (ppic = PIC::PpicReadNative(&fni)))
|
|
{
|
|
_Error(ertReadMeta);
|
|
return;
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, ppic->CbOnFile(), ctg, cno, &blck) ||
|
|
!ppic->FWrite(&blck) || !_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
ReleasePpo(&ppic);
|
|
|
|
if (_FGetCleanTok(&tok) && ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a bitmap import command from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyBitmap(bool fPack, bool fMask, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
FNI fni;
|
|
BLCK blck;
|
|
TOK tok;
|
|
PHP rgphp[3];
|
|
long cphp;
|
|
PMBMP pmbmp = pvNil;
|
|
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 3, &cphp))
|
|
{
|
|
_Error(ertBodyBitmapHead);
|
|
return;
|
|
}
|
|
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
goto LFail;
|
|
}
|
|
|
|
if (pvNil == (pmbmp = MBMP::PmbmpReadNative(&fni, (byte)rgphp[0].lw,
|
|
rgphp[1].lw, rgphp[2].lw, fMask ? fmbmpMask : fmbmpNil)))
|
|
{
|
|
STN stn;
|
|
fni.GetStnPath(&stn);
|
|
_Error(ertReadBitmap, stn.Psz());
|
|
goto LFail;
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, pmbmp->CbOnFile(), ctg, cno, &blck) ||
|
|
!pmbmp->FWrite(&blck) || !_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
ReleasePpo(&pmbmp);
|
|
|
|
if (_FGetCleanTok(&tok) && ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
LFail:
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a palette import command from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyPalette(bool fPack, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
FNI fni;
|
|
BLCK blck;
|
|
TOK tok;
|
|
PGL pglclr;
|
|
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
goto LFail;
|
|
}
|
|
|
|
if (!FReadBitmap(&fni, pvNil, &pglclr, pvNil, pvNil, pvNil))
|
|
{
|
|
_Error(ertReadBitmap);
|
|
goto LFail;
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, pglclr->CbOnFile(), ctg, cno, &blck) ||
|
|
!pglclr->FWrite(&blck) || !_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
ReleasePpo(&pglclr);
|
|
|
|
if (_FGetCleanTok(&tok) && ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
LFail:
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a midi import command from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyMidi(bool fPack, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
FNI fni;
|
|
BLCK blck;
|
|
TOK tok;
|
|
PMIDS pmids;
|
|
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
goto LFail;
|
|
}
|
|
|
|
if (pvNil == (pmids = MIDS::PmidsReadNative(&fni)))
|
|
{
|
|
_Error(ertReadMidi);
|
|
goto LFail;
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, pmids->CbOnFile(), ctg, cno, &blck) ||
|
|
!pmids->FWrite(&blck) || !_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
ReleasePpo(&pmids);
|
|
|
|
if (_FGetCleanTok(&tok) && ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
LFail:
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a cursor import command from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyCursor(bool fPack, CTG ctg, CNO cno)
|
|
{
|
|
// These are for parsing a Windows cursor file
|
|
struct CURDIR
|
|
{
|
|
byte dxp;
|
|
byte dyp;
|
|
byte bZero1;
|
|
byte bZero2;
|
|
short xp;
|
|
short yp;
|
|
long cb;
|
|
long bv;
|
|
};
|
|
|
|
struct CURH
|
|
{
|
|
long cbCurh;
|
|
long dxp;
|
|
long dyp;
|
|
short swOne1;
|
|
short swOne2;
|
|
long lwZero1;
|
|
long lwZero2;
|
|
long lwZero3;
|
|
long lwZero4;
|
|
long lwZero5;
|
|
long lwZero6;
|
|
long lw1;
|
|
long lw2;
|
|
};
|
|
|
|
AssertThis(0);
|
|
FNI fni;
|
|
BLCK blck;
|
|
FLO floSrc;
|
|
TOK tok;
|
|
long ccurdir, cbBits;
|
|
CURF curf;
|
|
short rgsw[3];
|
|
byte *prgb;
|
|
CURDIR *pcurdir;
|
|
CURH *pcurh;
|
|
PGG pggcurf = pvNil;
|
|
HQ hq = hqNil;
|
|
|
|
floSrc.pfil = pvNil;
|
|
if (!_pchlx->FGetPath(&fni))
|
|
{
|
|
_Error(ertBodyFile);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (pvNil == (floSrc.pfil = FIL::PfilOpen(&fni)))
|
|
{
|
|
_Error(ertReadCursor);
|
|
goto LFail;
|
|
}
|
|
|
|
floSrc.fp = 0;
|
|
floSrc.cb = floSrc.pfil->FpMac();
|
|
if (floSrc.cb < size(rgsw) + size(CURDIR) + size(CURH) + 128 ||
|
|
!floSrc.FReadRgb(rgsw, size(rgsw), 0) ||
|
|
rgsw[0] != 0 || rgsw[1] != 2 ||
|
|
!FIn(ccurdir = rgsw[2], 1, (floSrc.cb - size(rgsw)) / size(CURDIR)))
|
|
{
|
|
_Error(ertReadCursor);
|
|
goto LFail;
|
|
}
|
|
|
|
floSrc.cb -= size(rgsw);
|
|
floSrc.fp = size(rgsw);
|
|
if (!floSrc.FReadHq(&hq))
|
|
{
|
|
_Error(ertOom);
|
|
goto LFail;
|
|
}
|
|
ReleasePpo(&floSrc.pfil);
|
|
|
|
prgb = (byte *)PvLockHq(hq);
|
|
pcurdir = (CURDIR *)prgb;
|
|
if (pvNil == (pggcurf = GG::PggNew(size(CURF), ccurdir)))
|
|
{
|
|
_Error(ertOom);
|
|
goto LFail;
|
|
}
|
|
while (ccurdir-- > 0)
|
|
{
|
|
cbBits = pcurdir->dxp == 32 ? 256 : 128;
|
|
if (pcurdir->dxp != pcurdir->dyp ||
|
|
pcurdir->dxp != 16 && pcurdir->dxp != 32 ||
|
|
pcurdir->bZero1 != 0 || pcurdir->bZero2 != 0 ||
|
|
pcurdir->cb != size(CURH) + cbBits ||
|
|
!FIn(pcurdir->bv -= size(rgsw), LwMul(rgsw[2], size(CURDIR)),
|
|
floSrc.cb - pcurdir->cb + 1) ||
|
|
CbRoundToLong(pcurdir->bv) != pcurdir->bv)
|
|
{
|
|
_Error(ertReadCursor);
|
|
goto LFail;
|
|
}
|
|
curf.curt = curtMonochrome;
|
|
curf.xp = (byte)pcurdir->xp;
|
|
curf.yp = (byte)pcurdir->yp;
|
|
curf.dxp = pcurdir->dxp;
|
|
curf.dyp = pcurdir->dyp;
|
|
|
|
pcurh = (CURH *)PvAddBv(prgb, pcurdir->bv);
|
|
if (pcurh->cbCurh != size(CURH) - 2 * size(long) ||
|
|
pcurh->dxp != pcurdir->dxp || pcurh->dyp != 2 * pcurdir->dyp ||
|
|
pcurh->swOne1 != 1 || pcurh->swOne2 != 1 ||
|
|
pcurh->lwZero1 != 0 ||
|
|
(pcurh->lwZero2 != 0 && pcurh->lwZero2 != pcurdir->cb - size(CURH)) ||
|
|
pcurh->lwZero3 != 0 || pcurh->lwZero4 != 0 ||
|
|
pcurh->lwZero5 != 0 || pcurh->lwZero6 != 0)
|
|
{
|
|
_Error(ertReadCursor);
|
|
goto LFail;
|
|
}
|
|
|
|
//The bits are stored in upside down DIB order!
|
|
ReversePb(pcurh + 1, pcurdir->cb - size(CURH));
|
|
SwapBytesRglw(pcurh + 1, (pcurdir->cb - size(CURH)) / size(long));
|
|
|
|
if (pcurdir->dxp == 16)
|
|
{
|
|
//need to consolidate the bits, because they are stored 4 bytes per
|
|
//row (2 bytes wasted) instead of 2 bytes per row.
|
|
long csw = 32;
|
|
short *pswSrc, *pswDst;
|
|
|
|
pswSrc = pswDst = (short *)(pcurh + 1);
|
|
while (csw-- != 0)
|
|
{
|
|
*pswDst++ = *pswSrc++;
|
|
pswSrc++;
|
|
}
|
|
}
|
|
|
|
if (!pggcurf->FInsert(pggcurf->IvMac(),
|
|
(long)curf.dxp * curf.dyp / 4, pcurh + 1, &curf))
|
|
{
|
|
_Error(ertOom);
|
|
goto LFail;
|
|
}
|
|
pcurdir++;
|
|
}
|
|
|
|
LFail:
|
|
//success comes through here also
|
|
ReleasePpo(&floSrc.pfil);
|
|
if (hqNil != hq)
|
|
{
|
|
UnlockHq(hq);
|
|
FreePhq(&hq);
|
|
}
|
|
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, pggcurf->CbOnFile(), ctg, cno, &blck) ||
|
|
!pggcurf->FWrite(&blck) || !_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
ReleasePpo(&pggcurf);
|
|
|
|
if (_FGetCleanTok(&tok) && ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a data section from the source file. ptok should be pre-loaded
|
|
with the first token and when _FParseData returns it contains the next
|
|
token to be processed. Returns false iff no tokens were consumed.
|
|
***************************************************************************/
|
|
bool CHCM::_FParseData(PTOK ptok)
|
|
{
|
|
enum
|
|
{
|
|
psNil,
|
|
psHaveLw,
|
|
psHaveBOr,
|
|
};
|
|
|
|
AssertThis(0);
|
|
AssertVarMem(ptok);
|
|
|
|
long cbNum;
|
|
long lw;
|
|
long cbNumPrev = _cbNum;
|
|
long ps = psNil;
|
|
bool fRet = fFalse;
|
|
|
|
for (;;)
|
|
{
|
|
switch (ptok->tt)
|
|
{
|
|
case ttBOr:
|
|
if (ps == psNil)
|
|
return fRet;
|
|
if (ps != psHaveLw)
|
|
{
|
|
ptok->tt = ttError;
|
|
return fRet;
|
|
}
|
|
ps = psHaveBOr;
|
|
break;
|
|
|
|
case ttLong:
|
|
if (ps == psHaveLw)
|
|
{
|
|
SwapVars(&_cbNum, &cbNumPrev);
|
|
_AppendNumber(lw);
|
|
_cbNum = cbNumPrev;
|
|
ps = psNil;
|
|
}
|
|
if (ps == psNil)
|
|
{
|
|
lw = ptok->lw;
|
|
cbNumPrev = _cbNum;
|
|
ps = psHaveLw;
|
|
}
|
|
else
|
|
{
|
|
Assert(ps == psHaveBOr, 0);
|
|
if (cbNumPrev != _cbNum)
|
|
{
|
|
ptok->tt = ttError;
|
|
return fRet;
|
|
}
|
|
lw |= ptok->lw;
|
|
ps = psHaveLw;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (ps == psHaveBOr)
|
|
{
|
|
ptok->tt = ttError;
|
|
return fRet;
|
|
}
|
|
if (ps == psHaveLw)
|
|
{
|
|
SwapVars(&_cbNum, &cbNumPrev);
|
|
_AppendNumber(lw);
|
|
_cbNum = cbNumPrev;
|
|
ps = psNil;
|
|
}
|
|
|
|
switch (ptok->tt)
|
|
{
|
|
case ttString:
|
|
_AppendString(&ptok->stn);
|
|
break;
|
|
case ttAlign:
|
|
_ParseBodyAlign();
|
|
break;
|
|
case ttFile:
|
|
_ParseBodyFile();
|
|
break;
|
|
case ttBo:
|
|
//insert the current byte order
|
|
cbNum = _cbNum;
|
|
_cbNum = size(short);
|
|
_AppendNumber(kboCur);
|
|
_cbNum = cbNum;
|
|
break;
|
|
case ttOsk:
|
|
//insert the current osk
|
|
cbNum = _cbNum;
|
|
_cbNum = size(short);
|
|
_AppendNumber(_osk);
|
|
_cbNum = cbNum;
|
|
break;
|
|
default:
|
|
return fRet;
|
|
}
|
|
break;
|
|
}
|
|
|
|
fRet = fTrue;
|
|
if (!_FGetCleanTok(ptok, fTrue))
|
|
{
|
|
ptok->tt = ttError;
|
|
return fRet;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a list structure from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyList(bool fPack, bool fAl, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
PHP rgphp[1];
|
|
long cphp;
|
|
long cbEntry, cb;
|
|
byte *prgb;
|
|
long iv, iiv;
|
|
BLCK blck;
|
|
PGLB pglb = pvNil;
|
|
PGL pglivFree = pvNil;
|
|
|
|
// get size of entry data
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 1, &cphp) || cphp < 1)
|
|
{
|
|
_Error(ertListHead);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (!FIn(cbEntry = rgphp[0].lw, 1, kcbMax))
|
|
{
|
|
_Error(ertListEntrySize);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
pglb = fAl ? (PGLB)AL::PalNew(cbEntry) : (PGLB)GL::PglNew(cbEntry);
|
|
if (pvNil == pglb)
|
|
{
|
|
_Error(ertOom);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
pglb->SetMinGrow(20);
|
|
|
|
//prefetch a token
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
|
|
for (;;)
|
|
{
|
|
//empty the BSF
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
|
|
if (ttFree == tok.tt)
|
|
{
|
|
if (!fAl)
|
|
_Error(ertBadFree);
|
|
else if (!FError())
|
|
{
|
|
iv = pglb->IvMac();
|
|
if (pvNil == pglivFree &&
|
|
pvNil == (pglivFree = GL::PglNew(size(long))) ||
|
|
!pglivFree->FAdd(&iv))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
}
|
|
else if (ttItem != tok.tt)
|
|
break;
|
|
else
|
|
{
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
_FParseData(&tok);
|
|
}
|
|
|
|
if ((cb = _bsf.IbMac()) > cbEntry)
|
|
{
|
|
_Error(ertItemOverflow);
|
|
continue;
|
|
}
|
|
|
|
AssertIn(cb, 0, cbEntry + 1);
|
|
if (FError())
|
|
continue;
|
|
|
|
if (!pglb->FAdd(pvNil, &iv))
|
|
_Error(ertOom);
|
|
else
|
|
{
|
|
Assert(iv == pglb->IvMac() - 1, "what?");
|
|
prgb = (byte *)pglb->PvLock(iv);
|
|
if (cb > 0)
|
|
_bsf.FetchRgb(0, cb, prgb);
|
|
if (cb < cbEntry)
|
|
ClearPb(prgb + cb, cbEntry - cb);
|
|
pglb->Unlock();
|
|
}
|
|
}
|
|
|
|
if (ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
|
|
if (FError())
|
|
goto LFail;
|
|
|
|
if (pvNil != pglivFree)
|
|
{
|
|
Assert(fAl, "why did GL have free entries?");
|
|
for (iiv = pglivFree->IvMac(); iiv-- > 0; )
|
|
{
|
|
pglivFree->Get(iiv, &iv);
|
|
AssertIn(iv, 0, pglb->IvMac());
|
|
pglb->Delete(iv);
|
|
}
|
|
}
|
|
|
|
// write list to disk
|
|
if (!_FPrepWrite(fPack, pglb->CbOnFile(), ctg, cno, &blck) ||
|
|
!pglb->FWrite(&blck, _bo, _osk) ||
|
|
!_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
|
|
LFail:
|
|
ReleasePpo(&pglb);
|
|
ReleasePpo(&pglivFree);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a group structure from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyGroup(bool fPack, bool fAg, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
PHP rgphp[1];
|
|
long cphp;
|
|
long cbFixed, cb;
|
|
byte *prgb;
|
|
long iv, iiv;
|
|
BLCK blck;
|
|
bool fFree;
|
|
PGGB pggb = pvNil;
|
|
PGL pglivFree = pvNil;
|
|
|
|
// get size of fixed data
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 1, &cphp) || cphp < 1)
|
|
{
|
|
_Error(ertGroupHead);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (!FIn(cbFixed = rgphp[0].lw, 0, kcbMax))
|
|
{
|
|
_Error(ertGroupEntrySize);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
pggb = fAg ? (PGGB)AG::PagNew(cbFixed) : (PGGB)GG::PggNew(cbFixed);
|
|
if (pvNil == pggb)
|
|
{
|
|
_Error(ertOom);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
pggb->SetMinGrow(10, 100);
|
|
|
|
//prefetch a token
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
|
|
for (;;)
|
|
{
|
|
//empty the BSF
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
|
|
fFree = (ttFree == tok.tt);
|
|
if (fFree)
|
|
{
|
|
if (!fAg)
|
|
_Error(ertBadFree);
|
|
else if (!FError())
|
|
{
|
|
iv = pggb->IvMac();
|
|
if (pvNil == pglivFree &&
|
|
pvNil == (pglivFree = GL::PglNew(size(long))) ||
|
|
!pglivFree->FAdd(&iv))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
}
|
|
else if (ttItem != tok.tt)
|
|
break;
|
|
else
|
|
{
|
|
//get the fixed part
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
_FParseData(&tok);
|
|
}
|
|
|
|
if ((cb = _bsf.IbMac()) > cbFixed)
|
|
{
|
|
_Error(ertItemOverflow);
|
|
cb = cbFixed;
|
|
}
|
|
|
|
AssertIn(cb, 0, cbFixed + 1);
|
|
if (!FError())
|
|
{
|
|
//add the item
|
|
if (!pggb->FAdd(pvNil, &iv))
|
|
_Error(ertOom);
|
|
else
|
|
{
|
|
Assert(iv == pggb->IvMac() - 1, "what?");
|
|
prgb = (byte *)pggb->PvFixedLock(iv);
|
|
if (cb > 0)
|
|
_bsf.FetchRgb(0, cb, prgb);
|
|
if (cb < cbFixed)
|
|
ClearPb(prgb + cb, cbFixed - cb);
|
|
pggb->Unlock();
|
|
}
|
|
}
|
|
|
|
//check for a variable part
|
|
if (fFree || ttVar != tok.tt)
|
|
continue;
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
_FParseData(&tok);
|
|
|
|
if (FError() || (cb = _bsf.IbMac()) <= 0)
|
|
continue;
|
|
|
|
Assert(iv == pggb->IvMac() - 1, "iv wrong");
|
|
if (!pggb->FInsertRgb(iv, 0, cb, pvNil))
|
|
_Error(ertOom);
|
|
else
|
|
{
|
|
prgb = (byte *)pggb->PvLock(iv);
|
|
_bsf.FetchRgb(0, cb, prgb);
|
|
pggb->Unlock();
|
|
}
|
|
}
|
|
|
|
if (ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
|
|
if (FError())
|
|
goto LFail;
|
|
|
|
if (pvNil != pglivFree)
|
|
{
|
|
Assert(fAg, "why did GG have free entries?");
|
|
for (iiv = pglivFree->IvMac(); iiv-- > 0; )
|
|
{
|
|
pglivFree->Get(iiv, &iv);
|
|
AssertIn(iv, 0, pggb->IvMac());
|
|
pggb->Delete(iv);
|
|
}
|
|
}
|
|
|
|
// write list to disk
|
|
if (!_FPrepWrite(fPack, pggb->CbOnFile(), ctg, cno, &blck) ||
|
|
!pggb->FWrite(&blck, _bo, _osk) ||
|
|
!_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
|
|
LFail:
|
|
ReleasePpo(&pggb);
|
|
ReleasePpo(&pglivFree);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a string table from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyStringTable(bool fPack, bool fAst, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
PHP rgphp[1];
|
|
long cphp;
|
|
long cbExtra, cb;
|
|
long iv, iiv;
|
|
STN stn;
|
|
BLCK blck;
|
|
bool fFree;
|
|
PGSTB pgstb = pvNil;
|
|
PGL pglivFree = pvNil;
|
|
void *pvExtra = pvNil;
|
|
|
|
// get size of attached data
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 1, &cphp) || cphp < 1)
|
|
{
|
|
_Error(ertGstHead);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
if (!FIn(cbExtra = rgphp[0].lw, 0, kcbMax) || cbExtra % size(long) != 0)
|
|
{
|
|
_Error(ertGstEntrySize);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
pgstb = fAst ? (PGSTB)AST::PastNew(cbExtra) : (PGSTB)GST::PgstNew(cbExtra);
|
|
if (pvNil == pgstb || cbExtra > 0 &&
|
|
!FAllocPv(&pvExtra, cbExtra, fmemNil, mprNormal))
|
|
{
|
|
_Error(ertOom);
|
|
_SkipPastTok(ttEndChunk);
|
|
goto LFail;
|
|
}
|
|
pgstb->SetMinGrow(10, 100);
|
|
|
|
//prefetch a token
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
|
|
for (;;)
|
|
{
|
|
fFree = (ttFree == tok.tt);
|
|
if (fFree)
|
|
{
|
|
if (!fAst)
|
|
_Error(ertBadFree);
|
|
else if (!FError())
|
|
{
|
|
iv = pgstb->IvMac();
|
|
if (pvNil == pglivFree &&
|
|
pvNil == (pglivFree = GL::PglNew(size(long))) ||
|
|
!pglivFree->FAdd(&iv))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
stn.SetNil();
|
|
}
|
|
else if (ttItem != tok.tt)
|
|
break;
|
|
else
|
|
{
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
if (ttString != tok.tt)
|
|
{
|
|
_Error(ertNeedString);
|
|
_SkipPastTok(ttEndChunk);
|
|
goto LFail;
|
|
}
|
|
stn = tok.stn;
|
|
if (!_FGetCleanTok(&tok))
|
|
goto LFail;
|
|
}
|
|
|
|
if (!FError() && !pgstb->FAddStn(&stn, pvNil, &iv))
|
|
_Error(ertOom);
|
|
|
|
if (cbExtra <= 0 || fFree)
|
|
continue;
|
|
|
|
//empty the BSF and get the extra data
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
_FParseData(&tok);
|
|
|
|
if ((cb = _bsf.IbMac()) > cbExtra)
|
|
{
|
|
_Error(ertItemOverflow);
|
|
cb = cbExtra;
|
|
}
|
|
|
|
AssertIn(cb, 0, cbExtra + 1);
|
|
if (!FError())
|
|
{
|
|
//add the item
|
|
Assert(iv == pgstb->IvMac() - 1, "what?");
|
|
if (cb > 0)
|
|
_bsf.FetchRgb(0, cb, pvExtra);
|
|
if (cb < cbExtra)
|
|
ClearPb(PvAddBv(pvExtra, cb), cbExtra - cb);
|
|
pgstb->PutExtra(iv, pvExtra);
|
|
}
|
|
}
|
|
|
|
if (ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
|
|
if (FError())
|
|
goto LFail;
|
|
|
|
if (pvNil != pglivFree)
|
|
{
|
|
Assert(fAst, "why did GST have free entries?");
|
|
for (iiv = pglivFree->IvMac(); iiv-- > 0; )
|
|
{
|
|
pglivFree->Get(iiv, &iv);
|
|
AssertIn(iv, 0, pgstb->IvMac());
|
|
pgstb->Delete(iv);
|
|
}
|
|
}
|
|
|
|
// write list to disk
|
|
if (!_FPrepWrite(fPack, pgstb->CbOnFile(), ctg, cno, &blck) ||
|
|
!pgstb->FWrite(&blck, _bo, _osk) ||
|
|
!_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
|
|
LFail:
|
|
ReleasePpo(&pgstb);
|
|
ReleasePpo(&pglivFree);
|
|
FreePpv(&pvExtra);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a script from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyScript(bool fPack, bool fInfix, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
SCCG sccg;
|
|
PSCPT pscpt;
|
|
|
|
if (pvNil == (pscpt = sccg.PscptCompileLex(_pchlx, fInfix, _pmsnkError,
|
|
ttEndChunk)))
|
|
{
|
|
_Error(ertScript);
|
|
return;
|
|
}
|
|
|
|
if (!pscpt->FSaveToChunk(_pcfl, ctg, cno, fPack))
|
|
_Error(ertOom);
|
|
|
|
ReleasePpo(&pscpt);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a script from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseBodyPackedFile(bool *pfPacked)
|
|
{
|
|
AssertThis(0);
|
|
long lw, lwSwapped;
|
|
TOK tok;
|
|
|
|
_ParseBodyFile();
|
|
if (_bsf.IbMac() < size(long))
|
|
{
|
|
_Error(ertPackedFile, PszLit("bad packed file"));
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
_bsf.FetchRgb(0, size(long), &lw);
|
|
lwSwapped = lw;
|
|
SwapBytesRglw(&lwSwapped, 1);
|
|
if (lw == klwSigPackedFile || lwSwapped == klwSigPackedFile)
|
|
*pfPacked = fTrue;
|
|
else if (lw == klwSigUnpackedFile || lwSwapped == klwSigUnpackedFile)
|
|
*pfPacked = fFalse;
|
|
else
|
|
{
|
|
_Error(ertPackedFile, PszLit("not a packed file"));
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
|
|
_bsf.FReplace(pvNil, 0, 0, size(long));
|
|
|
|
if (!_FGetCleanTok(&tok) || ttEndChunk != tok.tt)
|
|
{
|
|
_Error(ertNeedEndChunk);
|
|
_SkipPastTok(ttEndChunk);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Start a sub file.
|
|
***************************************************************************/
|
|
void CHCM::_StartSubFile(bool fPack, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
CSFC csfc;
|
|
|
|
if (pvNil == _pglcsfc && pvNil == (_pglcsfc = GL::PglNew(size(CSFC))))
|
|
goto LFail;
|
|
|
|
csfc.pcfl = _pcfl;
|
|
csfc.ctg = ctg;
|
|
csfc.cno = cno;
|
|
csfc.fPack = FPure(fPack);
|
|
|
|
if (!_pglcsfc->FPush(&csfc))
|
|
goto LFail;
|
|
|
|
if (pvNil == (_pcfl = CFL::PcflCreateTemp()))
|
|
{
|
|
_pglcsfc->FPop();
|
|
_pcfl = csfc.pcfl;
|
|
LFail:
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
End a sub file.
|
|
***************************************************************************/
|
|
void CHCM::_EndSubFile(void)
|
|
{
|
|
AssertThis(0);
|
|
CSFC csfc;
|
|
|
|
if (pvNil == _pglcsfc || !_pglcsfc->FPop(&csfc))
|
|
{
|
|
_Error(ertSyntax);
|
|
return;
|
|
}
|
|
|
|
AssertPo(csfc.pcfl, 0);
|
|
|
|
if (!FError())
|
|
{
|
|
long icki;
|
|
CKI cki;
|
|
long cbTot, cbT;
|
|
FP fpDst;
|
|
PFIL pfilDst = pvNil;
|
|
bool fRet = fFalse;
|
|
|
|
// get the size of the data
|
|
cbTot = 0;
|
|
for (icki = 0; _pcfl->FGetCki(icki, &cki); icki++)
|
|
{
|
|
if (_pcfl->CckiRef(cki.ctg, cki.cno) > 0)
|
|
continue;
|
|
if (!_pcfl->FWriteChunkTree(cki.ctg, cki.cno, pvNil, 0, &cbT))
|
|
goto LFail;
|
|
cbTot += cbT;
|
|
}
|
|
|
|
// setup pfilDst and fpDst for writing the chunk trees
|
|
if (csfc.fPack)
|
|
{
|
|
pfilDst = FIL::PfilCreateTemp();
|
|
fpDst = 0;
|
|
}
|
|
else
|
|
{
|
|
FLO floDst;
|
|
|
|
// resize the chunk
|
|
if (!csfc.pcfl->FPut(cbTot, csfc.ctg, csfc.cno))
|
|
goto LFail;
|
|
csfc.pcfl->FFindFlo(csfc.ctg, csfc.cno, &floDst);
|
|
|
|
pfilDst = floDst.pfil;
|
|
pfilDst->AddRef();
|
|
fpDst = floDst.fp;
|
|
}
|
|
|
|
// write the data to (pfilDst, fpDst)
|
|
for (icki = 0; _pcfl->FGetCki(icki, &cki); icki++)
|
|
{
|
|
if (_pcfl->CckiRef(cki.ctg, cki.cno) > 0)
|
|
continue;
|
|
if (!_pcfl->FWriteChunkTree(cki.ctg, cki.cno, pfilDst, fpDst, &cbT))
|
|
goto LFail;
|
|
fpDst += cbT;
|
|
Debug( cbTot -= cbT; )
|
|
}
|
|
Assert(cbTot == 0, "FWriteChunkTree messed up!");
|
|
|
|
if (csfc.fPack)
|
|
{
|
|
BLCK blck(pfilDst, 0, fpDst);
|
|
|
|
// pack the data and put it in the chunk.
|
|
blck.FPackData();
|
|
if (!csfc.pcfl->FPutBlck(&blck, csfc.ctg, csfc.cno))
|
|
goto LFail;
|
|
}
|
|
|
|
csfc.pcfl->SetForest(csfc.ctg, csfc.cno, fTrue);
|
|
fRet = fTrue;
|
|
|
|
LFail:
|
|
if (!fRet)
|
|
_Error(ertOom);
|
|
|
|
ReleasePpo(&pfilDst);
|
|
}
|
|
|
|
ReleasePpo(&_pcfl);
|
|
_pcfl = csfc.pcfl;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse a PACKFMT command, which is used to specify the packing format
|
|
to use.
|
|
***************************************************************************/
|
|
void CHCM::_ParsePackFmt(void)
|
|
{
|
|
AssertThis(0);
|
|
PHP rgphp[1];
|
|
long cphp;
|
|
long cfmt;
|
|
|
|
// get the format
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 1, &cphp) || cphp < 1)
|
|
{
|
|
_Error(ertSyntax);
|
|
return;
|
|
}
|
|
|
|
cfmt = rgphp[0].lw;
|
|
if (!vpcodmUtil->FCanDo(cfmt, fTrue))
|
|
_Error(ertBadPackFmt);
|
|
else
|
|
vpcodmUtil->SetCfmtDefault(cfmt);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse the chunk body from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseChunkBody(CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
TOK tok;
|
|
BLCK blck;
|
|
CKI cki;
|
|
bool fFetch;
|
|
bool fPack, fPrePacked;
|
|
|
|
//empty the BSF
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
|
|
fFetch = fTrue;
|
|
fPack = fPrePacked = fFalse;
|
|
for (;;)
|
|
{
|
|
if (fFetch && !_FGetCleanTok(&tok))
|
|
return;
|
|
|
|
fFetch = fTrue;
|
|
switch (tok.tt)
|
|
{
|
|
default:
|
|
if (!_FParseData(&tok))
|
|
{
|
|
_Error(ertBadToken);
|
|
_SkipPastTok(ttEndChunk);
|
|
return;
|
|
}
|
|
//don't fetch next token
|
|
fFetch = fFalse;
|
|
break;
|
|
case ttChild:
|
|
_ParseBodyChild(ctg, cno);
|
|
break;
|
|
case ttParent:
|
|
_ParseBodyParent(ctg, cno);
|
|
break;
|
|
case ttLoner:
|
|
if (pvNil != _pglcsfc && 0 < _pglcsfc->IvMac())
|
|
{
|
|
_Error(ertLonerInSub);
|
|
break;
|
|
}
|
|
cki.ctg = ctg;
|
|
cki.cno = cno;
|
|
if (pvNil == _pglckiLoner &&
|
|
pvNil == (_pglckiLoner = GL::PglNew(size(CKI))) ||
|
|
!_pglckiLoner->FPush(&cki))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
break;
|
|
|
|
case ttPrePacked:
|
|
fPrePacked = fTrue;
|
|
break;
|
|
|
|
case ttPack:
|
|
fPack = fTrue;
|
|
break;
|
|
|
|
case ttPackFmt:
|
|
_ParsePackFmt();
|
|
break;
|
|
|
|
// We're done after all the cases below
|
|
case ttPackedFile:
|
|
fPack = fFalse;
|
|
_ErrorOnData(PszLit("Packed File"));
|
|
_ParseBodyPackedFile(&fPrePacked);
|
|
// fall thru
|
|
case ttEndChunk:
|
|
if (!FError())
|
|
{
|
|
if (!_FPrepWrite(fPack, _bsf.IbMac(), ctg, cno, &blck) ||
|
|
!_bsf.FWriteRgb(&blck) ||
|
|
!_FEndWrite(fPack, ctg, cno, &blck))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
if (!FError() && fPrePacked)
|
|
_pcfl->SetPacked(ctg, cno, fTrue);
|
|
return;
|
|
case ttMeta:
|
|
_ErrorOnData(PszLit("Metafile"));
|
|
_ParseBodyMeta(fPack, ctg, cno);
|
|
return;
|
|
case ttBitmap:
|
|
case ttMask:
|
|
_ErrorOnData(PszLit("Bitmap"));
|
|
_ParseBodyBitmap(fPack, ttMask == tok.tt, ctg, cno);
|
|
return;
|
|
case ttPalette:
|
|
_ErrorOnData(PszLit("Palette"));
|
|
_ParseBodyPalette(fPack, ctg, cno);
|
|
return;
|
|
case ttMidi:
|
|
_ErrorOnData(PszLit("Midi"));
|
|
_ParseBodyMidi(fPack, ctg, cno);
|
|
return;
|
|
case ttCursor:
|
|
_ErrorOnData(PszLit("Cursor"));
|
|
_ParseBodyCursor(fPack, ctg, cno);
|
|
return;
|
|
case ttAl:
|
|
case ttGl:
|
|
_ErrorOnData(PszLit("List"));
|
|
_ParseBodyList(fPack, ttAl == tok.tt, ctg, cno);
|
|
return;
|
|
case ttAg:
|
|
case ttGg:
|
|
_ErrorOnData(PszLit("Group"));
|
|
_ParseBodyGroup(fPack, ttAg == tok.tt, ctg, cno);
|
|
return;
|
|
case ttAst:
|
|
case ttGst:
|
|
_ErrorOnData(PszLit("String Table"));
|
|
_ParseBodyStringTable(fPack, ttAst == tok.tt, ctg, cno);
|
|
return;
|
|
case ttScript:
|
|
case ttScriptP:
|
|
_ErrorOnData(PszLit("Script"));
|
|
_ParseBodyScript(fPack, ttScript == tok.tt, ctg, cno);
|
|
return;
|
|
case ttSubFile:
|
|
_ErrorOnData(PszLit("Sub File"));
|
|
_StartSubFile(fPack, ctg, cno);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Parse an adopt parenthesized header from the source file.
|
|
***************************************************************************/
|
|
void CHCM::_ParseAdopt(void)
|
|
{
|
|
AssertThis(0);
|
|
CTG ctgParent, ctgChild;
|
|
CNO cnoParent, cnoChild;
|
|
CHID chid;
|
|
PHP rgphp[5];
|
|
long cphp;
|
|
|
|
ClearPb(rgphp, size(rgphp));
|
|
if (!_FParseParenHeader(rgphp, 5, &cphp) || cphp < 4)
|
|
{
|
|
_Error(ertAdoptHead);
|
|
return;
|
|
}
|
|
|
|
ctgParent = rgphp[0].lw;
|
|
cnoParent = rgphp[1].lw;
|
|
ctgChild = rgphp[2].lw;
|
|
cnoChild = rgphp[3].lw;
|
|
chid = rgphp[4].lw;
|
|
|
|
// check if parent exists
|
|
if (!_pcfl->FFind(ctgParent, cnoParent))
|
|
{
|
|
_Error(ertParentMissing);
|
|
return;
|
|
}
|
|
|
|
// check if child exists
|
|
if (!_pcfl->FFind(ctgChild, cnoChild))
|
|
{
|
|
_Error(ertChildMissing);
|
|
return;
|
|
}
|
|
|
|
// check if cycle would be created
|
|
if (_pcfl->TIsDescendent(ctgChild, cnoChild, ctgParent, cnoParent) != tNo)
|
|
_Error(ertCycle);
|
|
else if (!FError() && !_pcfl->FAdoptChild(ctgParent, cnoParent,
|
|
ctgChild, cnoChild, chid, fTrue))
|
|
{
|
|
_Error(ertOom);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Compile the given file.
|
|
***************************************************************************/
|
|
PCFL CHCM::PcflCompile(PFNI pfniSrc, PFNI pfniDst, PMSNK pmsnk)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pfniSrc, ffniFile);
|
|
AssertPo(pfniDst, ffniFile);
|
|
AssertPo(pmsnk, 0);
|
|
BSF bsfSrc;
|
|
STN stnFile;
|
|
FLO flo;
|
|
bool fRet;
|
|
|
|
if (pvNil == (flo.pfil = FIL::PfilOpen(pfniSrc)))
|
|
{
|
|
pmsnk->ReportLine(PszLit("opening source file failed"));
|
|
return pvNil;
|
|
}
|
|
|
|
flo.fp = 0;
|
|
flo.cb = flo.pfil->FpMac();
|
|
fRet = flo.FTranslate(oskNil) && bsfSrc.FReplaceFlo(&flo, fFalse, 0, 0);
|
|
ReleasePpo(&flo.pfil);
|
|
if (!fRet)
|
|
return pvNil;
|
|
|
|
pfniSrc->GetStnPath(&stnFile);
|
|
return PcflCompile(&bsfSrc, &stnFile, pfniDst, pmsnk);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Compile the given BSF, using initial file name given by pstnFile.
|
|
***************************************************************************/
|
|
PCFL CHCM::PcflCompile(PBSF pbsfSrc, PSTN pstnFile, PFNI pfniDst, PMSNK pmsnk)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbsfSrc, ffniFile);
|
|
AssertPo(pstnFile, 0);
|
|
AssertPo(pfniDst, ffniFile);
|
|
AssertPo(pmsnk, 0);
|
|
TOK tok;
|
|
CTG ctg;
|
|
CNO cno;
|
|
PCFL pcfl;
|
|
bool fReportBadTok;
|
|
|
|
if (pvNil == (_pchlx = NewObj CHLX(pbsfSrc, pstnFile)))
|
|
{
|
|
pmsnk->ReportLine(PszLit("Memory failure"));
|
|
return pvNil;
|
|
}
|
|
|
|
if (pvNil == (_pcfl = CFL::PcflCreate(pfniDst, fcflWriteEnable)))
|
|
{
|
|
pmsnk->ReportLine(PszLit("Couldn't create destination file"));
|
|
ReleasePpo(&_pchlx);
|
|
return pvNil;
|
|
}
|
|
|
|
_pmsnkError = pmsnk;
|
|
_sm = smStz;
|
|
_cbNum = size(long);
|
|
_bo = kboCur;
|
|
_osk = koskCur;
|
|
|
|
fReportBadTok = fTrue;
|
|
while (_FGetCleanTok(&tok, fTrue))
|
|
{
|
|
switch(tok.tt)
|
|
{
|
|
case ttChunk:
|
|
_ParseChunkHeader(&ctg, &cno);
|
|
_ParseChunkBody(ctg, cno);
|
|
fReportBadTok = fTrue;
|
|
break;
|
|
|
|
case ttEndChunk:
|
|
// ending a sub file
|
|
_EndSubFile();
|
|
fReportBadTok = fTrue;
|
|
break;
|
|
|
|
case ttAdopt:
|
|
_ParseAdopt();
|
|
fReportBadTok = fTrue;
|
|
break;
|
|
|
|
case ttPackFmt:
|
|
_ParsePackFmt();
|
|
fReportBadTok = fTrue;
|
|
break;
|
|
|
|
default:
|
|
if (fReportBadTok)
|
|
{
|
|
_Error(ertNeedChunk);
|
|
fReportBadTok = fFalse;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (_cactError > 100)
|
|
{
|
|
pmsnk->ReportLine(PszLit("Too many errors - compilation aborted"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// empty the BSF
|
|
_bsf.FReplace(pvNil, 0, 0, _bsf.IbMac());
|
|
|
|
// make sure we're not in any subfiles
|
|
if (pvNil != _pglcsfc && _pglcsfc->IvMac() > 0)
|
|
{
|
|
CSFC csfc;
|
|
|
|
_Error(ertNoEndSubFile);
|
|
while (_pglcsfc->FPop(&csfc))
|
|
{
|
|
ReleasePpo(&_pcfl);
|
|
_pcfl = csfc.pcfl;
|
|
}
|
|
}
|
|
|
|
if (!FError() && pvNil != _pglckiLoner)
|
|
{
|
|
CKI cki;
|
|
long icki;
|
|
|
|
for (icki = _pglckiLoner->IvMac(); icki-- > 0; )
|
|
{
|
|
_pglckiLoner->Get(icki, &cki);
|
|
_pcfl->SetLoner(cki.ctg, cki.cno, fTrue);
|
|
}
|
|
}
|
|
|
|
if (!FError() && !_pcfl->FSave(kctgChkCmp, pvNil))
|
|
_Error(ertOom);
|
|
|
|
if (FError())
|
|
{
|
|
ReleasePpo(&_pcfl);
|
|
pfniDst->FDelete();
|
|
}
|
|
ReleasePpo(&_pchlx);
|
|
ReleasePpo(&_pglckiLoner);
|
|
|
|
pcfl = _pcfl;
|
|
_pcfl = pvNil;
|
|
_pmsnkError = pvNil;
|
|
return pcfl;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Keyword-tokentype mappings
|
|
***************************************************************************/
|
|
static KEYTT _rgkeytt[] =
|
|
{
|
|
PszLit("ITEM"), ttItem,
|
|
PszLit("FREE"), ttFree,
|
|
PszLit("VAR"), ttVar,
|
|
PszLit("BYTE"), ttModeByte,
|
|
PszLit("SHORT"), ttModeShort,
|
|
PszLit("LONG"), ttModeLong,
|
|
PszLit("CHUNK"), ttChunk,
|
|
PszLit("ENDCHUNK"), ttEndChunk,
|
|
PszLit("ADOPT"), ttAdopt,
|
|
PszLit("CHILD"), ttChild,
|
|
PszLit("PARENT"), ttParent,
|
|
PszLit("BO"), ttBo,
|
|
PszLit("OSK"), ttOsk,
|
|
PszLit("STN"), ttModeStn,
|
|
PszLit("STZ"), ttModeStz,
|
|
PszLit("SZ"), ttModeSz,
|
|
PszLit("ST"), ttModeSt,
|
|
PszLit("ALIGN"), ttAlign,
|
|
PszLit("FILE"), ttFile,
|
|
PszLit("PACKEDFILE"), ttPackedFile,
|
|
PszLit("META"), ttMeta,
|
|
PszLit("BITMAP"), ttBitmap,
|
|
PszLit("MASK"), ttMask,
|
|
PszLit("MIDI"), ttMidi,
|
|
PszLit("SCRIPT"), ttScript,
|
|
PszLit("SCRIPTPF"), ttScriptP,
|
|
PszLit("GL"), ttGl,
|
|
PszLit("AL"), ttAl,
|
|
PszLit("GG"), ttGg,
|
|
PszLit("AG"), ttAg,
|
|
PszLit("GST"), ttGst,
|
|
PszLit("AST"), ttAst,
|
|
PszLit("MACBO"), ttMacBo,
|
|
PszLit("WINBO"), ttWinBo,
|
|
PszLit("MACOSK"), ttMacOsk,
|
|
PszLit("WINOSK"), ttWinOsk,
|
|
PszLit("LONER"), ttLoner,
|
|
PszLit("CURSOR"), ttCursor,
|
|
PszLit("PALETTE"), ttPalette,
|
|
PszLit("PREPACKED"), ttPrePacked,
|
|
PszLit("PACK"), ttPack,
|
|
PszLit("PACKFMT"), ttPackFmt,
|
|
PszLit("SUBFILE"), ttSubFile,
|
|
};
|
|
|
|
#define kckeytt (size(_rgkeytt)/size(_rgkeytt[0]))
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the chunky compiler lexer.
|
|
***************************************************************************/
|
|
CHLX::CHLX(PBSF pbsf, PSTN pstnFile) : CHLX_PAR(pbsf, pstnFile)
|
|
{
|
|
_pgstVariables = pvNil;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the chunky compiler lexer.
|
|
***************************************************************************/
|
|
CHLX::~CHLX(void)
|
|
{
|
|
ReleasePpo(&_pgstVariables);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads in the next token. Resolves certain names to keyword tokens.
|
|
***************************************************************************/
|
|
bool CHLX::FGetTok(PTOK ptok)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ptok);
|
|
long ikeytt;
|
|
long istn;
|
|
|
|
for (;;)
|
|
{
|
|
if (!CHLX_PAR::FGetTok(ptok))
|
|
return fFalse;
|
|
|
|
if (ttName != ptok->tt)
|
|
return fTrue;
|
|
|
|
//check for a keyword
|
|
for (ikeytt = 0; ikeytt < kckeytt; ikeytt++)
|
|
{
|
|
if (ptok->stn.FEqualSz(_rgkeytt[ikeytt].pszKeyword))
|
|
{
|
|
ptok->tt = _rgkeytt[ikeytt].tt;
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
//if the token isn't SET, check for a variable
|
|
if (!ptok->stn.FEqualSz(PszLit("SET")))
|
|
{
|
|
//check for a variable
|
|
if (pvNil != _pgstVariables &&
|
|
_pgstVariables->FFindStn(&ptok->stn, &istn, fgstSorted))
|
|
{
|
|
ptok->tt = ttLong;
|
|
_pgstVariables->GetExtra(istn, &ptok->lw);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//handle a SET
|
|
if (!_FDoSet(ptok))
|
|
{
|
|
ptok->tt = ttError;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads in the next token. Skips semicolons and commas.
|
|
***************************************************************************/
|
|
bool CHLX::FGetTokSkipSemi(PTOK ptok)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ptok);
|
|
|
|
// skip comma and semicolon separators
|
|
while (FGetTok(ptok))
|
|
{
|
|
if (ttComma != ptok->tt && ttSemi != ptok->tt)
|
|
return fTrue;
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads a path and builds an FNI.
|
|
***************************************************************************/
|
|
bool CHLX::FGetPath(FNI *pfni)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pfni, 0);
|
|
achar ch;
|
|
STN stn;
|
|
|
|
if (!_FSkipWhiteSpace())
|
|
{
|
|
_fSkipToNextLine = fTrue;
|
|
return fFalse;
|
|
}
|
|
|
|
if (!_FFetchRgch(&ch) || '"' != ch)
|
|
return fFalse;
|
|
_Advance();
|
|
|
|
stn.SetNil();
|
|
for (;;)
|
|
{
|
|
if (!_FFetchRgch(&ch))
|
|
return fFalse;
|
|
_Advance();
|
|
if ('"' == ch)
|
|
break;
|
|
if (kchReturn == ch)
|
|
return fFalse;
|
|
stn.FAppendCh(ch);
|
|
}
|
|
|
|
#ifdef WIN
|
|
static achar _szInclude[1024];
|
|
SZ szT;
|
|
|
|
if (_szInclude[0] == 0)
|
|
{
|
|
GetEnvironmentVariable(PszLit("include"), _szInclude,
|
|
CvFromRgv(_szInclude) - 1);
|
|
}
|
|
|
|
if (0 != SearchPath(_szInclude, stn.Psz(), pvNil, kcchMaxSz, szT, pvNil))
|
|
stn = szT;
|
|
return pfni->FBuildFromPath(&stn);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
return pfni->FBuild(0, 0, &stn, kftgText);
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a set command.
|
|
***************************************************************************/
|
|
bool CHLX::_FDoSet(PTOK ptok)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ptok);
|
|
long tt;
|
|
long lw;
|
|
long istn;
|
|
bool fNegate;
|
|
|
|
if (!CHLX_PAR::FGetTok(ptok) || ttName != ptok->tt)
|
|
return fFalse;
|
|
|
|
lw = 0;
|
|
istn = ivNil;
|
|
if (pvNil != _pgstVariables ||
|
|
pvNil != (_pgstVariables = GST::PgstNew(size(long))))
|
|
{
|
|
if (_pgstVariables->FFindStn(&ptok->stn, &istn, fgstSorted))
|
|
_pgstVariables->GetExtra(istn, &lw);
|
|
else if (!_pgstVariables->FInsertStn(istn, &ptok->stn, &lw))
|
|
istn = ivNil;
|
|
}
|
|
|
|
if (!CHLX_PAR::FGetTok(ptok))
|
|
return fFalse;
|
|
|
|
switch (ptok->tt)
|
|
{
|
|
case ttInc:
|
|
lw++;
|
|
break;
|
|
case ttDec:
|
|
lw--;
|
|
break;
|
|
|
|
case ttAssign:
|
|
case ttAAdd:
|
|
case ttASub:
|
|
case ttAMul:
|
|
case ttADiv:
|
|
case ttAMod:
|
|
case ttABOr:
|
|
case ttABAnd:
|
|
case ttABXor:
|
|
case ttAShr:
|
|
case ttAShl:
|
|
tt = ptok->tt;
|
|
fNegate = fFalse;
|
|
for (;;)
|
|
{
|
|
if (!CHLX_PAR::FGetTok(ptok))
|
|
return fFalse;
|
|
if (ttLong == ptok->tt)
|
|
break;
|
|
if (ttName == ptok->tt)
|
|
{
|
|
long istnT;
|
|
if (!_pgstVariables->FFindStn(&ptok->stn, &istnT, fgstSorted))
|
|
return fFalse;
|
|
_pgstVariables->GetExtra(istnT, &ptok->lw);
|
|
ptok->tt = ttLong;
|
|
break;
|
|
}
|
|
if (ttSub != ptok->tt)
|
|
return fFalse;
|
|
fNegate = !fNegate;
|
|
}
|
|
if (fNegate)
|
|
ptok->lw = -ptok->lw;
|
|
|
|
switch (tt)
|
|
{
|
|
case ttAssign:
|
|
lw = ptok->lw;
|
|
break;
|
|
case ttAAdd:
|
|
lw += ptok->lw;
|
|
break;
|
|
case ttASub:
|
|
lw -= ptok->lw;
|
|
break;
|
|
case ttAMul:
|
|
lw *= ptok->lw;
|
|
break;
|
|
case ttADiv:
|
|
if (ptok->lw == 0)
|
|
return fFalse;
|
|
lw /= ptok->lw;
|
|
break;
|
|
case ttAMod:
|
|
if (ptok->lw == 0)
|
|
return fFalse;
|
|
lw %= ptok->lw;
|
|
break;
|
|
case ttABOr:
|
|
lw |= ptok->lw;
|
|
break;
|
|
case ttABAnd:
|
|
lw &= ptok->lw;
|
|
break;
|
|
case ttABXor:
|
|
lw ^= ptok->lw;
|
|
break;
|
|
case ttAShr:
|
|
// do logical shift
|
|
lw = (ulong)lw >> ptok->lw;
|
|
break;
|
|
case ttAShl:
|
|
lw <<= ptok->lw;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return fFalse;
|
|
}
|
|
|
|
if (ivNil != istn)
|
|
_pgstVariables->PutExtra(istn, &lw);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert that the CHLX is a valid object.
|
|
***************************************************************************/
|
|
void CHLX::AssertValid(ulong grf)
|
|
{
|
|
CHLX_PAR::AssertValid(grf);
|
|
AssertNilOrPo(_pgstVariables, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the CHLX object.
|
|
***************************************************************************/
|
|
void CHLX::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
CHLX_PAR::MarkMem();
|
|
MarkMemObj(_pgstVariables);
|
|
}
|
|
#endif
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the CHDC class. This is the chunky decompiler.
|
|
***************************************************************************/
|
|
CHDC::CHDC(void)
|
|
{
|
|
_ert = ertNil;
|
|
_pcfl = pvNil;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the CHDC class.
|
|
***************************************************************************/
|
|
CHDC::~CHDC(void)
|
|
{
|
|
ReleasePpo(&_pcfl);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CHDC.
|
|
***************************************************************************/
|
|
void CHDC::AssertValid(ulong grf)
|
|
{
|
|
CHDC_PAR::AssertValid(0);
|
|
AssertNilOrPo(_pcfl, 0);
|
|
AssertPo(&_bsf, 0);
|
|
AssertPo(&_chse, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the CHDC.
|
|
***************************************************************************/
|
|
void CHDC::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
CHDC_PAR::MarkMem();
|
|
MarkMemObj(&_bsf);
|
|
MarkMemObj(&_chse);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Decompile a chunky file.
|
|
***************************************************************************/
|
|
bool CHDC::FDecompile(PCFL pcflSrc, PMSNK pmsnk, PMSNK pmsnkError)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcflSrc, 0);
|
|
long icki, ikid, ckid;
|
|
CTG ctg;
|
|
CKI cki;
|
|
KID kid;
|
|
BLCK blck;
|
|
|
|
_pcfl = pcflSrc;
|
|
_ert = ertNil;
|
|
_chse.Init(pmsnk, pmsnkError);
|
|
|
|
_bo = kboCur;
|
|
_osk = koskCur;
|
|
|
|
_chse.DumpSz(PszLit("BYTE"));
|
|
_chse.DumpSz(PszLit(""));
|
|
for (icki = 0; _pcfl->FGetCki(icki, &cki, pvNil, &blck); icki++)
|
|
{
|
|
STN stnName;
|
|
|
|
// don't dump these, because they're embedded in the script
|
|
if (cki.ctg == kctgScriptStrs)
|
|
continue;
|
|
|
|
_pcfl->FGetName(cki.ctg, cki.cno, &stnName);
|
|
_chse.DumpHeader(cki.ctg, cki.cno, &stnName);
|
|
|
|
// look for special CTGs
|
|
ctg = cki.ctg;
|
|
|
|
//handle 4 character ctg's
|
|
switch (ctg)
|
|
{
|
|
case kctgScript:
|
|
if (_FDumpScript(&cki))
|
|
goto LEndChunk;
|
|
_pcfl->FGetCki(icki, &cki, pvNil, &blck);
|
|
break;
|
|
}
|
|
|
|
//handle 3 character ctg's
|
|
ctg = ctg & 0xFFFFFF00L | 0x00000020L;
|
|
switch (ctg)
|
|
{
|
|
case kctgGst:
|
|
case kctgAst:
|
|
if (_FDumpStringTable(&blck, kctgAst == ctg))
|
|
goto LEndChunk;
|
|
_pcfl->FGetCki(icki, &cki, pvNil, &blck);
|
|
break;
|
|
}
|
|
|
|
//handle 2 character ctg's
|
|
ctg = ctg & 0xFFFF0000L | 0x00002020L;
|
|
switch (ctg)
|
|
{
|
|
case kctgGl:
|
|
case kctgAl:
|
|
if (_FDumpList(&blck, kctgAl == ctg))
|
|
goto LEndChunk;
|
|
_pcfl->FGetCki(icki, &cki, pvNil, &blck);
|
|
break;
|
|
case kctgGg:
|
|
case kctgAg:
|
|
if (_FDumpGroup(&blck, kctgAg == ctg))
|
|
goto LEndChunk;
|
|
_pcfl->FGetCki(icki, &cki, pvNil, &blck);
|
|
break;
|
|
}
|
|
|
|
_chse.DumpBlck(&blck);
|
|
|
|
LEndChunk:
|
|
_chse.DumpSz(PszLit("ENDCHUNK"));
|
|
_chse.DumpSz(PszLit(""));
|
|
}
|
|
|
|
// now output parent-child relationships
|
|
for (icki = 0; _pcfl->FGetCki(icki++, &cki, &ckid); )
|
|
{
|
|
for (ikid = 0; ikid < ckid; )
|
|
{
|
|
AssertDo(_pcfl->FGetKid(cki.ctg, cki.cno, ikid++, &kid), 0);
|
|
if (kid.cki.ctg == kctgScriptStrs && cki.ctg == kctgScript)
|
|
continue;
|
|
_chse.DumpAdoptCmd(&cki, &kid);
|
|
}
|
|
}
|
|
_pcfl = pvNil;
|
|
_chse.Uninit();
|
|
|
|
return !FError();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Disassemble the script and dump it.
|
|
***************************************************************************/
|
|
bool CHDC::_FDumpScript(CKI *pcki)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcki);
|
|
PSCPT pscpt;
|
|
bool fRet;
|
|
SCCG sccg;
|
|
long cfmt;
|
|
bool fPacked;
|
|
BLCK blck;
|
|
|
|
_pcfl->FFind(pcki->ctg, pcki->cno, &blck);
|
|
fPacked = blck.FPacked(&cfmt);
|
|
|
|
if (pvNil == (pscpt = SCPT::PscptRead(_pcfl, pcki->ctg, pcki->cno)))
|
|
return fFalse;
|
|
|
|
if (fPacked)
|
|
_WritePack(cfmt);
|
|
|
|
fRet = _chse.FDumpScript(pscpt, &sccg);
|
|
|
|
ReleasePpo(&pscpt);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Try to read the chunk as a list and dump it out. If the chunk isn't
|
|
a list, return false so it can be dumped in hex.
|
|
***************************************************************************/
|
|
bool CHDC::_FDumpList(PBLCK pblck, bool fAl)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, fblckReadable);
|
|
|
|
PGLB pglb;
|
|
short bo, osk;
|
|
long cfmt;
|
|
bool fPacked = pblck->FPacked(&cfmt);
|
|
|
|
pglb = fAl ? (PGLB)AL::PalRead(pblck, &bo, &osk) :
|
|
(PGLB)GL::PglRead(pblck, &bo, &osk);
|
|
if (pvNil == pglb)
|
|
return fFalse;
|
|
|
|
if (fPacked)
|
|
_WritePack(cfmt);
|
|
|
|
if (bo != _bo)
|
|
{
|
|
_chse.DumpSz(MacWin(bo != kboCur, bo == kboCur) ?
|
|
PszLit("WINBO") : PszLit("MACBO"));
|
|
_bo = bo;
|
|
}
|
|
if (osk != _osk)
|
|
{
|
|
_chse.DumpSz(osk == koskWin ? PszLit("WINOSK") : PszLit("MACOSK"));
|
|
_osk = osk;
|
|
}
|
|
|
|
_chse.DumpList(pglb);
|
|
ReleasePpo(&pglb);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Try to read the chunk as a group and dump it out. If the chunk isn't
|
|
a group, return false so it can be dumped in hex.
|
|
***************************************************************************/
|
|
bool CHDC::_FDumpGroup(PBLCK pblck, bool fAg)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, fblckReadable);
|
|
|
|
PGGB pggb;
|
|
short bo, osk;
|
|
long cfmt;
|
|
bool fPacked = pblck->FPacked(&cfmt);
|
|
|
|
pggb = fAg ? (PGGB)AG::PagRead(pblck, &bo, &osk) :
|
|
(PGGB)GG::PggRead(pblck, &bo, &osk);
|
|
if (pvNil == pggb)
|
|
return fFalse;
|
|
|
|
if (fPacked)
|
|
_WritePack(cfmt);
|
|
|
|
if (bo != _bo)
|
|
{
|
|
_chse.DumpSz(MacWin(bo != kboCur, bo == kboCur) ?
|
|
PszLit("WINBO") : PszLit("MACBO"));
|
|
_bo = bo;
|
|
}
|
|
if (osk != _osk)
|
|
{
|
|
_chse.DumpSz(osk == koskWin ? PszLit("WINOSK") : PszLit("MACOSK"));
|
|
_osk = osk;
|
|
}
|
|
|
|
_chse.DumpGroup(pggb);
|
|
ReleasePpo(&pggb);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Try to read the chunk as a string table and dump it out. If the chunk
|
|
isn't a string table, return false so it can be dumped in hex.
|
|
***************************************************************************/
|
|
bool CHDC::_FDumpStringTable(PBLCK pblck, bool fAst)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pblck, fblckReadable);
|
|
|
|
PGSTB pgstb;
|
|
short bo, osk;
|
|
long cfmt;
|
|
bool fPacked = pblck->FPacked(&cfmt);
|
|
bool fRet;
|
|
|
|
pgstb = fAst ? (PGSTB)AST::PastRead(pblck, &bo, &osk) :
|
|
(PGSTB)GST::PgstRead(pblck, &bo, &osk);
|
|
if (pvNil == pgstb)
|
|
return fFalse;
|
|
|
|
if (fPacked)
|
|
_WritePack(cfmt);
|
|
|
|
if (bo != _bo)
|
|
{
|
|
_chse.DumpSz(MacWin(bo != kboCur, bo == kboCur) ?
|
|
PszLit("WINBO") : PszLit("MACBO"));
|
|
_bo = bo;
|
|
}
|
|
if (osk != _osk)
|
|
{
|
|
_chse.DumpSz(osk == koskWin ? PszLit("WINOSK") : PszLit("MACOSK"));
|
|
_osk = osk;
|
|
}
|
|
|
|
fRet = _chse.FDumpStringTable(pgstb);
|
|
ReleasePpo(&pgstb);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Write out the PACKFMT and PACK commands
|
|
***************************************************************************/
|
|
void CHDC::_WritePack(long cfmt)
|
|
{
|
|
AssertThis(0);
|
|
STN stn;
|
|
|
|
if (cfmtNil == cfmt)
|
|
_chse.DumpSz(PszLit("PACK"));
|
|
else
|
|
{
|
|
stn.FFormatSz(PszLit("PACKFMT (0x%x) PACK"), cfmt);
|
|
_chse.DumpSz(stn.Psz());
|
|
}
|
|
}
|
|
|