Microsoft-3D-Movie-Maker/kauai/SRC/SCREXE.CPP

1757 lines
38 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
Script interpreter. See scrcom.cpp for an explanation of the format
of compiled scripts.
***************************************************************************/
#include "util.h"
ASSERTNAME
RTCLASS(SCEB)
RTCLASS(SCPT)
RTCLASS(STRG)
#ifdef DEBUG
// these strings are for debug only error messages
static STN _stn;
#endif //DEBUG
/***************************************************************************
Constructor for the script interpreter.
***************************************************************************/
SCEB::SCEB(PRCA prca, PSTRG pstrg)
{
AssertNilOrPo(prca, 0);
AssertNilOrPo(pstrg, 0);
_pgllwStack = pvNil;
_pglrtvm = pvNil;
_pscpt = pvNil;
_fPaused = fFalse;
_prca = prca;
if (pvNil != _prca)
_prca->AddRef();
_pstrg = pstrg;
if (pvNil != _pstrg)
_pstrg->AddRef();
AssertThis(0);
}
/***************************************************************************
Destructor for the script interpreter.
***************************************************************************/
SCEB::~SCEB(void)
{
Free();
ReleasePpo(&_prca);
ReleasePpo(&_pstrg);
}
/***************************************************************************
Free our claim to all this stuff.
***************************************************************************/
void SCEB::Free(void)
{
AssertThis(0);
// nuke literal strings left in the global string table
if (pvNil != _pscpt && pvNil != _pstrg && pvNil != _pscpt->_pgstLiterals &&
pvNil != _pglrtvm)
{
RTVN rtvn;
long stid;
rtvn.lu1 = 0;
for (rtvn.lu2 = _pscpt->_pgstLiterals->IvMac(); rtvn.lu2-- > 0; )
{
if (FFindRtvm(_pglrtvm, &rtvn, &stid, pvNil))
_pstrg->Delete(stid);
}
}
ReleasePpo(&_pgllwStack);
ReleasePpo(&_pglrtvm);
ReleasePpo(&_pscpt);
_fPaused = fFalse;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a SCEB.
***************************************************************************/
void SCEB::AssertValid(ulong grfsceb)
{
SCEB_PAR::AssertValid(0);
if (grfsceb & fscebRunnable)
{
Assert(pvNil != _pgllwStack, "nil stack");
Assert(pvNil != _pscpt, "nil script");
Assert(_ilwMac == _pscpt->_pgllw->IvMac(), 0);
AssertIn(_ilwCur, 1, _ilwMac + 1);
Assert(!_fError, 0);
}
AssertNilOrPo(_pgllwStack, 0);
AssertNilOrPo(_pscpt, 0);
AssertNilOrPo(_pglrtvm, 0);
AssertNilOrPo(_pstrg, 0);
AssertNilOrPo(_prca, 0);
}
/***************************************************************************
Mark memory for the SCEB.
***************************************************************************/
void SCEB::MarkMem(void)
{
AssertValid(0);
SCEB_PAR::MarkMem();
MarkMemObj(_pgllwStack);
MarkMemObj(_pscpt);
MarkMemObj(_pglrtvm);
MarkMemObj(_prca);
}
#endif //DEBUG
/***************************************************************************
Run the given script. (prglw, clw) is the list of parameters for the
script.
***************************************************************************/
bool SCEB::FRunScript(PSCPT pscpt, long *prglw, long clw,
long *plwReturn, bool *pfPaused)
{
AssertThis(0);
return FAttachScript(pscpt, prglw, clw) && FResume(plwReturn, pfPaused);
}
/***************************************************************************
Attach a script to this SCEB and pause the script.
***************************************************************************/
bool SCEB::FAttachScript(PSCPT pscpt, long *prglw, long clw)
{
AssertThis(0);
AssertPo(pscpt, 0);
AssertIn(clw, 0, kcbMax);
AssertPvCb(prglw, LwMul(clw, size(long)));
long lw;
DVER dver;
Free();
_lwReturn = 0;
_fError = fFalse;
//create the stack GL
if (pvNil == (_pgllwStack = GL::PglNew(size(long), 10)))
goto LFail;
_pgllwStack->SetMinGrow(10);
//stake our claim on the code GL.
_pscpt = pscpt;
_pscpt->AddRef();
//check the version
//get and check the version number
if ((_ilwMac = _pscpt->_pgllw->IvMac()) < 1)
{
Bug("No version info on script");
goto LFail;
}
_pscpt->_pgllw->Get(0, &lw);
dver.Set(SwHigh(lw), SwLow(lw));
if (!dver.FReadable(_SwCur(), _SwMin()))
{
Bug("Script version doesn't match script interpreter version");
goto LFail;
}
//add the parameters and literal strings
if (clw > 0)
_AddParameters(prglw, clw);
if (pvNil != _pscpt->_pgstLiterals && !_fError)
_AddStrings(_pscpt->_pgstLiterals);
if (_fError)
{
LFail:
Free();
return fFalse;
}
//set the pc and claim we're paused
_ilwCur = 1;
_fPaused = fTrue;
AssertThis(fscebRunnable);
return fTrue;
}
/***************************************************************************
Resume a paused script.
***************************************************************************/
bool SCEB::FResume(long *plwReturn, bool *pfPaused)
{
AssertThis(fscebRunnable);
AssertNilOrVarMem(plwReturn);
AssertNilOrVarMem(pfPaused);
RTVN rtvn;
long ilw, clwPush;
long lw;
long op;
TrashVar(plwReturn);
TrashVar(pfPaused);
if (!_fPaused || _fError)
{
Bug("script not paused");
goto LFail;
}
AssertIn(_ilwCur, 1, _ilwMac + 1);
for (_fPaused = fFalse; _ilwCur < _ilwMac && !_fError && !_fPaused; )
{
lw = *(long *)_pscpt->_pgllw->QvGet(_ilwCur++);
clwPush = B2Lw(lw);
if (!FIn(clwPush, 0, _ilwMac - _ilwCur + 1))
{
Bug("bad instruction");
goto LFail;
}
ilw = _ilwCur;
if (opNil != (op = B3Lw(lw)))
{
//this instruction acts on a variable
if (clwPush == 0)
{
Bug("bad var instruction");
goto LFail;
}
clwPush--;
rtvn.lu1 = (ulong)lw & 0x0000FFFF;
rtvn.lu2 = *(ulong *)_pscpt->_pgllw->QvGet(_ilwCur++);
ilw = _ilwCur;
if (!_FExecVarOp(op, &rtvn))
goto LFail;
}
else if (opNil != (op = SuLow(lw)))
{
//normal opcode
if (!_FExecOp(op))
goto LFail;
}
//push the stack stuff (if we didn't do a jump)
if (clwPush > 0 && ilw == _ilwCur)
{
ilw = _pgllwStack->IvMac();
if (!_pgllwStack->FSetIvMac(ilw + clwPush))
{
LFail:
_Error(fFalse);
break;
}
CopyPb(_pscpt->_pgllw->QvGet(_ilwCur), _pgllwStack->QvGet(ilw),
LwMul(clwPush, size(long)));
_ilwCur += clwPush;
}
}
if (_ilwCur >= _ilwMac || _fError)
_fPaused = fFalse;
if (!_fPaused)
Free();
if (!_fError && pvNil != plwReturn)
*plwReturn = _lwReturn;
if (pvNil != pfPaused)
*pfPaused = _fPaused;
AssertThis(0);
return !_fError;
}
/***************************************************************************
Put the parameters in the local variable list.
***************************************************************************/
void SCEB::_AddParameters(long *prglw, long clw)
{
AssertThis(0);
AssertIn(clw, 1, kcbMax);
AssertPvCb(prglw, LwMul(clw, size(long)));
STN stn;
long ilw;
RTVN rtvn;
//put the parameters in the local variable gl
stn = PszLit("_cparm");
rtvn.SetFromStn(&stn);
_AssignVar(&_pglrtvm, &rtvn, clw);
stn = PszLit("_parm");
rtvn.SetFromStn(&stn);
for (ilw = 0; ilw < clw; ilw++)
{
rtvn.lu1 = LwHighLow(SwLow(ilw), SwLow(rtvn.lu1));
_AssignVar(&_pglrtvm, &rtvn, prglw[ilw]);
}
}
/***************************************************************************
Put the literal strings into the registry. And assign the string id's
to the internal string variables.
***************************************************************************/
void SCEB::_AddStrings(PGST pgst)
{
AssertThis(0);
AssertPo(pgst, 0);
RTVN rtvn;
long stid;
STN stn;
if (pvNil == _pstrg)
{
_Error(fFalse);
return;
}
rtvn.lu1 = 0;
for (rtvn.lu2 = 0; (long)rtvn.lu2 < pgst->IvMac(); rtvn.lu2++)
{
pgst->GetStn(rtvn.lu2, &stn);
if (!_pstrg->FAdd(&stid, &stn))
{
_Error(fFalse);
break;
}
_AssignVar(&_pglrtvm, &rtvn, stid);
if (_fError)
{
_pstrg->Delete(stid);
break;
}
}
}
/***************************************************************************
Return the current version number of the script compiler.
***************************************************************************/
short SCEB::_SwCur(void)
{
AssertBaseThis(0);
return kswCurSccb;
}
/***************************************************************************
Return the min version number of the script compiler. Read can read
scripts back to this version.
***************************************************************************/
short SCEB::_SwMin(void)
{
AssertBaseThis(0);
return kswMinSccb;
}
/***************************************************************************
Execute an instruction that has a variable as an argument.
***************************************************************************/
bool SCEB::_FExecVarOp(long op, RTVN *prtvn)
{
AssertThis(0);
AssertVarMem(prtvn);
long lw;
if (FIn(op, kopMinArray, kopLimArray))
{
// an array access, munge the rtvn
lw = _LwPop();
if (_fError)
return fFalse;
prtvn->lu1 = LwHighLow(SwLow(lw), SwLow(prtvn->lu1));
op += kopPushLocVar - kopPushLocArray;
}
switch (op)
{
case kopPushLocVar:
_PushVar(_pglrtvm, prtvn);
break;
case kopPopLocVar:
_AssignVar(&_pglrtvm, prtvn, _LwPop());
break;
case kopPushThisVar:
_PushVar(_PglrtvmThis(), prtvn);
break;
case kopPopThisVar:
_AssignVar(_PpglrtvmThis(), prtvn, _LwPop());
break;
case kopPushGlobalVar:
_PushVar(_PglrtvmGlobal(), prtvn);
break;
case kopPopGlobalVar:
_AssignVar(_PpglrtvmGlobal(), prtvn, _LwPop());
break;
case kopPushRemoteVar:
lw = _LwPop();
if (!_fError)
_PushVar(_PglrtvmRemote(lw), prtvn);
break;
case kopPopRemoteVar:
lw = _LwPop();
if (!_fError)
_AssignVar(_PpglrtvmRemote(lw), prtvn, _LwPop());
break;
default:
_Error(fTrue);
break;
}
return !_fError;
}
/***************************************************************************
Execute an instruction.
***************************************************************************/
bool SCEB::_FExecOp(long op)
{
AssertThis(0);
double dou;
long lw1, lw2, lw3;
// OP's that don't have any arguments
switch (op)
{
case kopExit:
//jump to the end
_ilwCur = _ilwMac;
return fTrue;
case kopNextCard:
_Push(vsflUtil.LwNext(0));
return fTrue;
case kopPause:
_fPaused = fTrue;
return fTrue;
}
// OP's that have at least one argument
lw1 = _LwPop();
switch (op)
{
case kopAdd:
_Push(_LwPop() + lw1);
break;
case kopSub:
_Push(_LwPop() - lw1);
break;
case kopMul:
_Push(_LwPop() * lw1);
break;
case kopDiv:
if (lw1 == 0)
_Error(fTrue);
else
_Push(_LwPop() / lw1);
break;
case kopMod:
if (lw1 == 0)
_Error(fTrue);
else
_Push(_LwPop() % lw1);
break;
case kopNeg:
_Push(-lw1);
break;
case kopInc:
_Push(lw1 + 1);
break;
case kopDec:
_Push(lw1 - 1);
break;
case kopShr:
_Push((ulong)_LwPop() >> lw1);
break;
case kopShl:
_Push((ulong)_LwPop() << lw1);
break;
case kopBOr:
_Push(_LwPop() | lw1);
break;
case kopBAnd:
_Push(_LwPop() & lw1);
break;
case kopBXor:
_Push(_LwPop() ^ lw1);
break;
case kopBNot:
_Push(~lw1);
break;
case kopLXor:
_Push(FPure(_LwPop()) != FPure(lw1));
break;
case kopLNot:
_Push(!lw1);
break;
case kopEq:
_Push(_LwPop() == lw1);
break;
case kopNe:
_Push(_LwPop() != lw1);
break;
case kopGt:
_Push(_LwPop() > lw1);
break;
case kopLt:
_Push(_LwPop() < lw1);
break;
case kopGe:
_Push(_LwPop() >= lw1);
break;
case kopLe:
_Push(_LwPop() <= lw1);
break;
case kopAbs:
_Push(LwAbs(lw1));
break;
case kopRnd:
if (lw1 <= 0)
_Error(fTrue);
else
_Push(vrndUtil.LwNext(lw1));
break;
case kopMulDiv:
lw2 = _LwPop();
lw3 = _LwPop();
if (lw3 == 0)
_Error(fTrue);
else
{
dou = (double)lw1 * lw2 / lw3;
if (dou < (double)klwMin || dou > (double)klwMax)
_Error(fTrue);
else
_Push((long)dou);
}
break;
case kopDup:
_Push(lw1);
_Push(lw1);
break;
case kopPop:
break;
case kopSwap:
lw2 = _LwPop();
_Push(lw1);
_Push(lw2);
break;
case kopRot:
lw2 = _LwPop();
_Rotate(lw2, lw1);
break;
case kopRev:
_Reverse(lw1);
break;
case kopDupList:
_DupList(lw1);
break;
case kopPopList:
_PopList(lw1);
break;
case kopRndList:
_RndList(lw1);
break;
case kopSelect:
_Select(lw1, _LwPop());
break;
case kopGoEq:
lw1 = (_LwPop() == lw1);
goto LGoNz;
case kopGoNe:
lw1 = (_LwPop() != lw1);
goto LGoNz;
case kopGoGt:
lw1 = (_LwPop() > lw1);
goto LGoNz;
case kopGoLt:
lw1 = (_LwPop() < lw1);
goto LGoNz;
case kopGoGe:
lw1 = (_LwPop() >= lw1);
goto LGoNz;
case kopGoLe:
lw1 = (_LwPop() <= lw1);
goto LGoNz;
case kopGoZ:
lw1 = !lw1;
//fall through
case kopGoNz:
LGoNz:
lw2 = _LwPop();
//labels should have their high byte equal to kbLabel
if (B3Lw(lw2) != kbLabel || (lw2 &= 0x00FFFFFF) > _ilwMac)
_Error(fTrue);
else if (lw1 != 0)
{
//perform the goto
_ilwCur = lw2;
}
break;
case kopGo:
//labels should have their high byte equal to kbLabel
if (B3Lw(lw1) != kbLabel || (lw1 &= 0x00FFFFFF) > _ilwMac)
_Error(fTrue);
else
{
//perform the goto
_ilwCur = lw1;
}
break;
case kopReturn:
//jump to the end
_ilwCur = _ilwMac;
//fall through
case kopSetReturn:
_lwReturn = lw1;
break;
case kopShuffle:
if (lw1 <= 0)
_Error(fTrue);
else
vsflUtil.Shuffle(lw1);
break;
case kopShuffleList:
if (lw1 <= 0)
_Error(fTrue);
else
{
long *prglw;
_pgllwStack->Lock();
prglw = _QlwGet(lw1);
if (pvNil != prglw)
vsflUtil.ShuffleRglw(lw1, prglw);
_pgllwStack->Unlock();
}
break;
case kopMatch:
_Match(lw1);
break;
case kopCopyStr:
_CopySubStr(lw1, 0, kcchMaxStn, _LwPop());
break;
case kopMoveStr:
if (pvNil == _pstrg)
_Error(fTrue);
else if (_pstrg->FMove(lw1, lw2 = _LwPop()))
_Push(lw2);
else
_Push(stidNil);
break;
case kopNukeStr:
if (pvNil == _pstrg)
_Error(fTrue);
else
_pstrg->Delete(lw1);
break;
case kopMergeStrs:
_MergeStrings(lw1, _LwPop());
break;
case kopScaleTime:
vpusac->Scale(lw1);
break;
case kopNumToStr:
_NumToStr(lw1, _LwPop());
break;
case kopStrToNum:
lw2 = _LwPop();
_StrToNum(lw1, lw2, _LwPop());
break;
case kopConcatStrs:
lw2 = _LwPop();
_ConcatStrs(lw1, lw2, _LwPop());
break;
case kopLenStr:
_LenStr(lw1);
break;
case kopCopySubStr:
lw2 = _LwPop();
lw3 = _LwPop();
_CopySubStr(lw1, lw2, lw3, _LwPop());
break;
default:
_Error(fTrue);
break;
}
return !_fError;
}
/***************************************************************************
Pop a long off the stack.
***************************************************************************/
long SCEB::_LwPop(void)
{
long lw, ilw;
if (_fError)
return 0;
//this is faster than just doing FPop
if ((ilw = _pgllwStack->IvMac()) == 0)
{
_Error(fTrue);
return 0;
}
lw = *(long *)_pgllwStack->QvGet(--ilw);
AssertDo(_pgllwStack->FSetIvMac(ilw), 0);
return lw;
}
/***************************************************************************
Get a pointer to the element that is clw elements down from the top.
***************************************************************************/
long *SCEB::_QlwGet(long clw)
{
long ilwMac;
if (_fError)
return pvNil;
ilwMac = _pgllwStack->IvMac();
if (!FIn(clw, 1, ilwMac + 1))
{
_Error(fTrue);
return pvNil;
}
return (long *)_pgllwStack->QvGet(ilwMac - clw);
}
/***************************************************************************
Register an error.
***************************************************************************/
void SCEB::_Error(bool fAssert)
{
AssertThis(0);
if (!_fError)
{
Assert(!fAssert, "Runtime error in script");
Debug(_WarnSz(PszLit("Runtime error")));
_fError = fTrue;
}
}
#ifdef DEBUG
/***************************************************************************
Emits a warning with the given format string and optional parameters.
***************************************************************************/
void SCEB::_WarnSz(PSZ psz, ...)
{
AssertThis(0);
AssertSz(psz);
STN stn1, stn2;
SZS szs;
stn1.FFormatRgch(psz, CchSz(psz), (ulong *)(&psz + 1));
stn2.FFormatSz(PszLit("Script ('%f', 0x%x, %d): %s"),
_pscpt->Ctg(), _pscpt->Cno(), _ilwCur, &stn1);
stn2.GetSzs(szs);
Warn(szs);
}
#endif //DEBUG
/***************************************************************************
Rotate clwTot entries on the stack left by clwShift positions.
***************************************************************************/
void SCEB::_Rotate(long clwTot, long clwShift)
{
AssertThis(0);
long *qlw;
if (clwTot == 0 || clwTot == 1)
return;
qlw = _QlwGet(clwTot);
if (qlw != pvNil)
{
clwShift %= clwTot;
if (clwShift < 0)
clwShift += clwTot;
AssertIn(clwShift, 0, clwTot);
if (clwShift != 0)
{
SwapBlocks(qlw, LwMul(clwShift, size(long)),
LwMul(clwTot - clwShift, size(long)));
}
}
}
/***************************************************************************
Reverse clw entries on the stack.
***************************************************************************/
void SCEB::_Reverse(long clw)
{
AssertThis(0);
long *qlw, *qlw2;
long lw;
if (clw == 0 || clw == 1)
return;
qlw = _QlwGet(clw);
if (qlw != pvNil)
{
for (qlw2 = qlw + clw - 1; qlw2 > qlw; )
{
lw = *qlw;
*qlw++ = *qlw2;
*qlw2-- = lw;
}
}
}
/***************************************************************************
Duplicate clw entries on the stack.
***************************************************************************/
void SCEB::_DupList(long clw)
{
AssertThis(0);
long *qlw;
if (clw == 0)
return;
//_QlwGet checks for bad values of clw
if (_QlwGet(clw) == pvNil)
return;
if (!_pgllwStack->FSetIvMac(_pgllwStack->IvMac() + clw))
_Error(fFalse);
else
{
qlw = _QlwGet(clw * 2);
Assert(qlw != pvNil, "why did _QlwGet fail?");
CopyPb(qlw, qlw + clw, LwMul(clw, size(long)));
}
}
/***************************************************************************
Removes clw entries from the stack.
***************************************************************************/
void SCEB::_PopList(long clw)
{
AssertThis(0);
long ilwMac;
if (clw == 0 || _fError)
return;
ilwMac = _pgllwStack->IvMac();
if (!FIn(clw, 1, ilwMac + 1))
_Error(fTrue);
else
AssertDo(_pgllwStack->FSetIvMac(ilwMac - clw), "why fail?");
}
/***************************************************************************
Select the ilw'th entry from the top clw entries. ilw is indexed from
the top entry in and is zero based.
***************************************************************************/
void SCEB::_Select(long clw, long ilw)
{
AssertThis(0);
long *qlw;
if (pvNil == (qlw = _QlwGet(clw)))
return;
if (!FIn(ilw, 0, clw))
_Error(fTrue);
else if (clw > 1)
{
qlw[0] = qlw[clw - 1 - ilw];
_PopList(clw - 1);
}
}
/***************************************************************************
The top value is the key, the next is the default return, then come
clw pairs of test values and return values. If the key matches a
test value, push the correspongind return value. Otherwise, push
the default return value.
***************************************************************************/
void SCEB::_Match(long clw)
{
AssertThis(0);
long *qrglw;
long lwKey, lwPush, ilwTest;
lwKey = _LwPop();
lwPush = _LwPop();
if (pvNil == (qrglw = _QlwGet(2 * clw)))
return;
// start at high memory (top of the stack).
for (ilwTest = 2 * clw - 1; ilwTest > 0; ilwTest -= 2)
{
if (qrglw[ilwTest] == lwKey)
{
lwPush = qrglw[ilwTest - 1];
break;
}
}
qrglw[0] = lwPush;
_PopList(2 * clw - 1);
}
/***************************************************************************
Generates a random entry from a list of numbers on the stack.
***************************************************************************/
void SCEB::_RndList(long clw)
{
AssertThis(0);
if (clw <= 0)
_Error(fTrue);
else
_Select(clw, vrndUtil.LwNext(clw));
}
/***************************************************************************
Copy the string from stidSrc to stidDst.
***************************************************************************/
void SCEB::_CopySubStr(long stidSrc, long ichMin, long cch, long stidDst)
{
AssertThis(0);
STN stn;
if (pvNil == _pstrg)
_Error(fTrue);
if (_fError)
return;
if (!_pstrg->FGet(stidSrc, &stn))
Debug(_WarnSz(PszLit("Source string doesn't exist (stid = %d)"), stidSrc));
if (ichMin > 0)
stn.Delete(0, ichMin);
if (cch < stn.Cch())
stn.Delete(cch);
if (!_pstrg->FPut(stidDst, &stn))
{
Debug(_WarnSz(PszLit("Setting dst string failed")));
_Push(stidNil);
}
else
_Push(stidDst);
}
/***************************************************************************
Concatenate two strings and put the result in a third. Push the id
of the destination.
***************************************************************************/
void SCEB::_ConcatStrs(long stidSrc1, long stidSrc2, long stidDst)
{
AssertThis(0);
STN stn1, stn2;
if (pvNil == _pstrg)
_Error(fTrue);
if (_fError)
return;
if (!_pstrg->FGet(stidSrc1, &stn1))
{
Debug(_WarnSz(PszLit("Source string 1 doesn't exist (stid = %d)"),
stidSrc1));
}
if (!_pstrg->FGet(stidSrc2, &stn2))
{
Debug(_WarnSz(PszLit("Source string 2 doesn't exist (stid = %d)"),
stidSrc2));
}
stn1.FAppendStn(&stn2);
if (!_pstrg->FPut(stidDst, &stn1))
{
Debug(_WarnSz(PszLit("Setting dst string failed")));
_Push(stidNil);
}
else
_Push(stidDst);
}
/***************************************************************************
Push the length of the given string.
***************************************************************************/
void SCEB::_LenStr(long stid)
{
AssertThis(0);
STN stn;
if (pvNil == _pstrg)
_Error(fTrue);
if (!_pstrg->FGet(stid, &stn))
{
Debug(_WarnSz(PszLit("Source string doesn't exist (stid = %d)"),
stid));
}
_Push(stn.Cch());
}
/***************************************************************************
CRF reader function to read a string registry string table.
***************************************************************************/
bool _FReadStringReg(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
PBACO *ppbaco, long *pcb)
{
AssertPo(pcrf, 0);
AssertPo(pblck, fblckReadable);
AssertNilOrVarMem(ppbaco);
AssertVarMem(pcb);
PGST pgst;
PCABO pcabo;
short bo;
*pcb = pblck->Cb(fTrue);
if (pvNil == ppbaco)
return fTrue;
if (!pblck->FUnpackData())
return fFalse;
*pcb = pblck->Cb();
if (pvNil == (pgst = GST::PgstRead(pblck, &bo)) ||
pgst->CbExtra() != size(long))
{
goto LFail;
}
if (kboOther == bo)
{
long istn, stid;
for (istn = pgst->IvMac(); istn-- > 0; )
{
pgst->GetExtra(istn, &stid);
SwapBytesRglw(&stid, 1);
pgst->PutExtra(istn, &stid);
}
}
if (pvNil == (pcabo = NewObj CABO(pgst)))
{
LFail:
ReleasePpo(&pgst);
TrashVar(pcb);
TrashVar(ppbaco);
return fFalse;
}
*ppbaco = pcabo;
return fTrue;
}
/***************************************************************************
Merge a string table into the string registry.
***************************************************************************/
void SCEB::_MergeStrings(CNO cno, RSC rsc)
{
AssertThis(0);
PCABO pcabo;
PGST pgst;
long istn, stid;
STN stn;
bool fFail;
if (_fError)
return;
if (pvNil == _pstrg)
{
Bug("no string registry to put the string in");
_Error(fFalse);
return;
}
if (pvNil == _prca)
{
Bug("no rca to read string table from");
_Error(fFalse);
return;
}
if (pvNil == (pcabo = (PCABO)_prca->PbacoFetch(kctgStringReg, cno,
&_FReadStringReg, pvNil, rsc)))
{
Debug(_WarnSz(PszLit("Reading string table failed (cno = 0x%x)"),
cno));
return;
}
Assert(pcabo->po->FIs(kclsGST), 0);
pgst = (PGST)pcabo->po;
Assert(pgst->CbExtra() == size(long), 0);
fFail = fFalse;
for (istn = pgst->IvMac(); istn-- > 0; )
{
pgst->GetStn(istn, &stn);
pgst->GetExtra(istn, &stid);
fFail |= !_pstrg->FPut(stid, &stn);
}
#ifdef DEBUG
if (fFail)
_WarnSz(PszLit("Merging string table failed"));
#endif //DEBUG
pcabo->SetCrep(crepTossFirst);
ReleasePpo(&pcabo);
}
/***************************************************************************
Convert a number to a string and add the string to the registry.
***************************************************************************/
void SCEB::_NumToStr(long lw, long stid)
{
AssertThis(0);
STN stn;
if (pvNil == _pstrg)
_Error(fTrue);
if (_fError)
return;
stn.FFormatSz(PszLit("%d"), lw);
if (!_pstrg->FPut(stid, &stn))
{
Debug(_WarnSz(PszLit("Putting string in registry failed")));
stid = stidNil;
}
_Push(stid);
}
/***************************************************************************
Convert a string to a number and push the result. If the string is
empty, push lwEmpty; if there is an error, push lwError.
***************************************************************************/
void SCEB::_StrToNum(long stid, long lwEmpty, long lwError)
{
AssertThis(0);
STN stn;
long lw;
if (pvNil == _pstrg)
_Error(fTrue);
if (_fError)
return;
_pstrg->FGet(stid, &stn);
if (0 == stn.Cch())
lw = lwEmpty;
else if (!stn.FGetLw(&lw, 0))
lw = lwError;
_Push(lw);
}
/***************************************************************************
Push the value of a variable onto the runtime stack.
***************************************************************************/
void SCEB::_PushVar(PGL pglrtvm, RTVN *prtvn)
{
AssertThis(0);
AssertVarMem(prtvn);
AssertNilOrPo(pglrtvm, 0);
long lw;
if (_fError)
return;
if (pvNil == pglrtvm || !FFindRtvm(pglrtvm, prtvn, &lw, pvNil))
{
#ifdef DEBUG
prtvn->GetStn(&_stn);
_WarnSz(PszLit("Pushing uninitialized script variable: %s"),
&_stn);
#endif //DEBUG
_Push(0);
}
else
_Push(lw);
}
/***************************************************************************
Pop the top value off the runtime stack into a variable.
***************************************************************************/
void SCEB::_AssignVar(PGL *ppglrtvm, RTVN *prtvn, long lw)
{
AssertThis(0);
AssertVarMem(prtvn);
AssertNilOrVarMem(ppglrtvm);
if (_fError)
return;
if (pvNil == ppglrtvm)
{
_Error(fTrue);
return;
}
if (!FAssignRtvm(ppglrtvm, prtvn, lw))
_Error(fFalse);
}
/***************************************************************************
Get the variable map for "this" object.
***************************************************************************/
PGL SCEB::_PglrtvmThis(void)
{
PGL *ppgl = _PpglrtvmThis();
if (pvNil == ppgl)
return pvNil;
return *ppgl;
}
/***************************************************************************
Get the adress of the variable map master pointer for "this" object
(so we can create the variable map if need be).
***************************************************************************/
PGL *SCEB::_PpglrtvmThis(void)
{
return pvNil;
}
/***************************************************************************
Get the variable map for "global" variables.
***************************************************************************/
PGL SCEB::_PglrtvmGlobal(void)
{
PGL *ppgl = _PpglrtvmGlobal();
if (pvNil == ppgl)
return pvNil;
return *ppgl;
}
/***************************************************************************
Get the adress of the variable map master pointer for "global" variables
(so we can create the variable map if need be).
***************************************************************************/
PGL *SCEB::_PpglrtvmGlobal(void)
{
return pvNil;
}
/***************************************************************************
Get the variable map for a remote object.
***************************************************************************/
PGL SCEB::_PglrtvmRemote(long lw)
{
PGL *ppgl = _PpglrtvmRemote(lw);
if (pvNil == ppgl)
return pvNil;
return *ppgl;
}
/***************************************************************************
Get the adress of the variable map master pointer for a remote object
(so we can create the variable map if need be).
***************************************************************************/
PGL *SCEB::_PpglrtvmRemote(long lw)
{
return pvNil;
}
/***************************************************************************
Find a RTVM in the pglrtvm. Assumes the pglrtvm is sorted by rtvn.
If the RTVN is not in the GL, sets *pirtvm to where it would be if
it were.
***************************************************************************/
bool FFindRtvm(PGL pglrtvm, RTVN *prtvn, long *plw, long *pirtvm)
{
AssertPo(pglrtvm, 0);
AssertVarMem(prtvn);
AssertNilOrVarMem(plw);
AssertNilOrVarMem(pirtvm);
RTVM *qrgrtvm, *qrtvm;
long irtvm, irtvmMin, irtvmLim;
qrgrtvm = (RTVM *)pglrtvm->QvGet(0);
for (irtvmMin = 0, irtvmLim = pglrtvm->IvMac(); irtvmMin < irtvmLim; )
{
irtvm = (irtvmMin + irtvmLim) / 2;
qrtvm = qrgrtvm + irtvm;
if (qrtvm->rtvn.lu1 < prtvn->lu1)
irtvmMin = irtvm + 1;
else if (qrtvm->rtvn.lu1 > prtvn->lu1)
irtvmLim = irtvm;
else if (qrtvm->rtvn.lu2 < prtvn->lu2)
irtvmMin = irtvm + 1;
else if (qrtvm->rtvn.lu2 > prtvn->lu2)
irtvmLim = irtvm;
else
{
//we found it
if (pvNil != plw)
*plw = qrtvm->lwValue;
if (pvNil != pirtvm)
*pirtvm = irtvm;
return fTrue;
}
}
TrashVar(plw);
if (pvNil != pirtvm)
*pirtvm = irtvmMin;
return fFalse;
}
/***************************************************************************
Put the given value into a runtime variable.
***************************************************************************/
bool FAssignRtvm(PGL *ppglrtvm, RTVN *prtvn, long lw)
{
AssertVarMem(ppglrtvm);
AssertNilOrPo(*ppglrtvm, 0);
AssertVarMem(prtvn);
RTVM rtvm;
long irtvm;
rtvm.lwValue = lw;
rtvm.rtvn = *prtvn;
if (pvNil == *ppglrtvm)
{
if (pvNil == (*ppglrtvm = GL::PglNew(size(RTVM))))
return fFalse;
(*ppglrtvm)->SetMinGrow(10);
irtvm = 0;
}
else if (FFindRtvm(*ppglrtvm, prtvn, pvNil, &irtvm))
{
(*ppglrtvm)->Put(irtvm, &rtvm);
return fTrue;
}
return (*ppglrtvm)->FInsert(irtvm, &rtvm);
}
/***************************************************************************
A chunky resource reader to read a script.
***************************************************************************/
bool SCPT::FReadScript(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
PBACO *ppbaco, long *pcb)
{
AssertPo(pcrf, 0);
AssertPo(pblck, fblckReadable);
AssertNilOrVarMem(ppbaco);
*pcb = pblck->Cb(fTrue);
if (pvNil == ppbaco)
return fTrue;
*ppbaco = PscptRead(pcrf->Pcfl(), ctg, cno);
return pvNil != *ppbaco;
}
/***************************************************************************
Static method to read a script.
***************************************************************************/
PSCPT SCPT::PscptRead(PCFL pcfl, CTG ctg, CNO cno)
{
AssertPo(pcfl, 0);
short bo;
KID kid;
BLCK blck;
PSCPT pscpt = pvNil;
PGL pgllw = pvNil;
PGST pgst = pvNil;
if (!pcfl->FFind(ctg, cno, &blck))
goto LFail;
if (pvNil == (pgllw = GL::PglRead(&blck, &bo)) ||
pgllw->CbEntry() != size(long))
{
goto LFail;
}
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgScriptStrs, &kid))
{
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck) ||
pvNil == (pgst = GST::PgstRead(&blck)))
{
goto LFail;
}
}
if (pvNil == (pscpt = NewObj SCPT))
{
LFail:
ReleasePpo(&pgllw);
ReleasePpo(&pgst);
return pvNil;
}
if (kboOther == bo)
SwapBytesRglw(pgllw->QvGet(0), pgllw->IvMac());
pscpt->_pgllw = pgllw;
pscpt->_pgstLiterals = pgst;
return pscpt;
}
/***************************************************************************
Destructor for a script.
***************************************************************************/
SCPT::~SCPT(void)
{
AssertBaseThis(0);
ReleasePpo(&_pgllw);
ReleasePpo(&_pgstLiterals);
}
/***************************************************************************
Save the script to the given chunky file.
***************************************************************************/
bool SCPT::FSaveToChunk(PCFL pcfl, CTG ctg, CNO cno, bool fPack)
{
AssertThis(0);
AssertPo(pcfl, 0);
BLCK blck;
CNO cnoT, cnoStrs;
long cb;
// write the script chunk
cb = _pgllw->CbOnFile();
if (!blck.FSetTemp(cb) || !_pgllw->FWrite(&blck))
return fFalse;
if (fPack)
blck.FPackData();
if (!pcfl->FFind(ctg, cno))
{
// chunk doesn't exist, just write it
if (!pcfl->FPutBlck(&blck, ctg, cnoT = cno))
return fFalse;
}
else
{
// chunk already exists - add a new temporary one
if (!pcfl->FAddBlck(&blck, ctg, &cnoT))
return fFalse;
}
// write the string table if there is one. The cno for this is allocated
// via FAdd.
if (pvNil != _pgstLiterals)
{
cb = _pgstLiterals->CbOnFile();
if (!blck.FSetTemp(cb) || !_pgstLiterals->FWrite(&blck))
goto LFail;
if (fPack)
blck.FPackData();
if (!pcfl->FAddBlck(&blck, kctgScriptStrs, &cnoStrs))
goto LFail;
if (!pcfl->FAdoptChild(ctg, cnoT, kctgScriptStrs, cnoStrs))
{
pcfl->Delete(kctgScriptStrs, cnoStrs);
LFail:
pcfl->Delete(ctg, cnoT);
return fFalse;
}
}
// swap the data and children of the temporary chunk and the destination
if (cno != cnoT)
{
pcfl->SwapData(ctg, cno, ctg, cnoT);
pcfl->SwapChildren(ctg, cno, ctg, cnoT);
pcfl->Delete(ctg, cnoT);
}
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a SCPT.
***************************************************************************/
void SCPT::AssertValid(ulong grf)
{
SCPT_PAR::AssertValid(0);
AssertPo(_pgllw, 0);
AssertNilOrPo(_pgstLiterals, 0);
}
/***************************************************************************
Mark memory for the SCPT.
***************************************************************************/
void SCPT::MarkMem(void)
{
AssertValid(0);
SCPT_PAR::MarkMem();
MarkMemObj(_pgllw);
MarkMemObj(_pgstLiterals);
}
#endif //DEBUG
/***************************************************************************
Constructor for the runtime string registry.
***************************************************************************/
STRG::STRG(void)
{
_pgst = pvNil;
AssertThis(0);
}
/***************************************************************************
Constructor for the runtime string registry.
***************************************************************************/
STRG::~STRG(void)
{
AssertThis(0);
ReleasePpo(&_pgst);
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a STRG.
***************************************************************************/
void STRG::AssertValid(ulong grf)
{
STRG_PAR::AssertValid(0);
AssertNilOrPo(_pgst, 0);
}
/***************************************************************************
Mark memory for the STRG.
***************************************************************************/
void STRG::MarkMem(void)
{
AssertValid(0);
STRG_PAR::MarkMem();
MarkMemObj(_pgst);
}
#endif //DEBUG
/***************************************************************************
Put the string in the registry with the given string id.
***************************************************************************/
bool STRG::FPut(long stid, PSTN pstn)
{
AssertThis(0);
AssertPo(pstn, 0);
long istn;
if (!_FEnsureGst())
return fFalse;
if (_FFind(stid, &istn))
return _pgst->FPutStn(istn, pstn);
return _pgst->FInsertStn(istn, pstn, &stid);
}
/***************************************************************************
Get the string with the given string id. If the string isn't in the
registry, sets pstn to an empty string and returns false.
***************************************************************************/
bool STRG::FGet(long stid, PSTN pstn)
{
AssertThis(0);
AssertPo(pstn, 0);
long istn;
if (!_FFind(stid, &istn))
{
pstn->SetNil();
return fFalse;
}
_pgst->GetStn(istn, pstn);
return fTrue;
}
/***************************************************************************
Add a string to the registry, assigning it an unused id. The assigned
id's are not repeated in the near future. All assigned id's have their
high bit set.
***************************************************************************/
bool STRG::FAdd(long *pstid, PSTN pstn)
{
AssertThis(0);
AssertVarMem(pstid);
AssertPo(pstn, 0);
long istn;
for (;;)
{
_stidLast = (_stidLast + 1) | 0x80000000L;
if (!_FFind(_stidLast, &istn))
break;
}
*pstid = _stidLast;
return FPut(_stidLast, pstn);
}
/***************************************************************************
Delete a string from the registry.
***************************************************************************/
void STRG::Delete(long stid)
{
AssertThis(0);
long istn;
if (_FFind(stid, &istn))
_pgst->Delete(istn);
}
/***************************************************************************
Change the id of a string from stidSrc to stidDst. If stidDst already
exists, it is replaced. Returns false if the source string doesn't
exist. Can't fail if the source does exist.
***************************************************************************/
bool STRG::FMove(long stidSrc, long stidDst)
{
AssertThis(0);
long istnSrc, istnDst;
if (stidSrc == stidDst)
return _FFind(stidSrc, &istnSrc);
if (!_FFind(stidDst, &istnSrc))
return fFalse;
if (_FFind(stidDst, &istnDst))
{
_pgst->Delete(istnDst);
if (istnSrc > istnDst)
istnSrc--;
}
_pgst->PutExtra(istnSrc, &stidDst);
_pgst->Move(istnSrc, istnDst);
return fTrue;
}
/***************************************************************************
Do a binary search for the string id. Returns true iff the string is
in the registry. In either case, sets *pistn with where the string
should go.
***************************************************************************/
bool STRG::_FFind(long stid, long *pistn)
{
AssertThis(0);
AssertVarMem(pistn);
long ivMin, ivLim, iv;
long stidT;
if (pvNil == _pgst)
{
*pistn = 0;
return fFalse;
}
for (ivMin = 0, ivLim = _pgst->IvMac(); ivMin < ivLim; )
{
iv = (ivMin + ivLim) / 2;
_pgst->GetExtra(iv, &stidT);
if (stidT < stid)
ivMin = iv + 1;
else if (stidT > stid)
ivLim = iv;
else
{
*pistn = iv;
return fTrue;
}
}
*pistn = ivMin;
return fFalse;
}
/***************************************************************************
Make sure the GST exists.
***************************************************************************/
bool STRG::_FEnsureGst(void)
{
AssertThis(0);
if (pvNil != _pgst)
return fTrue;
_pgst = GST::PgstNew(size(long));
AssertThis(0);
return pvNil != _pgst;
}