1757 lines
38 KiB
C++
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;
|
|
}
|
|
|
|
|