1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- idCVar swf_debug( "swf_debug", "0", CVAR_INTEGER|CVAR_ARCHIVE, "debug swf scripts. 1 shows traces/errors. 2 also shows warnings. 3 also shows disassembly. 4 shows parameters in the disassembly." );
- idCVar swf_debugInvoke( "swf_debugInvoke", "0", CVAR_INTEGER, "debug swf functions being called from game." );
- idSWFConstantPool::idSWFConstantPool() {
- }
- /*
- ========================
- idSWFConstantPool::Clear
- ========================
- */
- void idSWFConstantPool::Clear() {
- for ( int i = 0; i < pool.Num(); i++ ) {
- pool[i]->Release();
- }
- pool.Clear();
- }
- /*
- ========================
- idSWFConstantPool::Copy
- ========================
- */
- void idSWFConstantPool::Copy( const idSWFConstantPool & other ) {
- Clear();
- pool.SetNum( other.pool.Num() );
- for ( int i = 0; i < pool.Num(); i++ ) {
- pool[i] = other.pool[i];
- pool[i]->AddRef();
- }
- }
- /*
- ========================
- idSWFScriptFunction_Script::~idSWFScriptFunction_Script
- ========================
- */
- idSWFScriptFunction_Script::~idSWFScriptFunction_Script() {
- for ( int i = 0; i < scope.Num(); i++ ) {
- if ( verify( scope[i] ) ) {
- scope[i]->Release();
- }
- }
- if ( prototype ) {
- prototype->Release();
- }
- }
- /*
- ========================
- idSWFScriptFunction_Script::Call
- ========================
- */
- void idSWFScriptFunction_Script::SetScope( idList<idSWFScriptObject *> & newScope ) {
- assert( scope.Num() == 0 );
- for ( int i = 0; i < scope.Num(); i++ ) {
- if ( verify( scope[i] ) ) {
- scope[i]->Release();
- }
- }
- scope.Clear();
- scope.Append( newScope );
- for ( int i = 0; i < newScope.Num(); i++ ) {
- if ( verify( scope[i] ) ) {
- scope[i]->AddRef();
- }
- }
- }
- /*
- ========================
- idSWFScriptFunction_Script::Call
- ========================
- */
- idSWFScriptVar idSWFScriptFunction_Script::Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ) {
- idSWFBitStream bitstream( data, length, false );
- // We assume scope[0] is the global scope
- assert( scope.Num() > 0 );
-
- if ( thisObject == NULL ) {
- thisObject = scope[0];
- }
- idSWFScriptObject * locals = idSWFScriptObject::Alloc();
- idSWFStack stack;
- stack.SetNum( parms.Num() + 1 );
- for ( int i = 0; i < parms.Num(); i++ ) {
- stack[ parms.Num() - i - 1 ] = parms[i];
- // Unfortunately at this point we don't have the function name anymore, so our warning messages aren't very detailed
- if ( i < parameters.Num() ) {
- if ( parameters[i].reg > 0 && parameters[i].reg < registers.Num() ) {
- registers[ parameters[i].reg ] = parms[i];
- }
- locals->Set( parameters[i].name, parms[i] );
- }
- }
- // Set any additional parameters to undefined
- for ( int i = parms.Num(); i < parameters.Num(); i++ ) {
- if ( parameters[i].reg > 0 && parameters[i].reg < registers.Num() ) {
- registers[ parameters[i].reg ].SetUndefined();
- }
- locals->Set( parameters[i].name, idSWFScriptVar() );
- }
- stack.A().SetInteger( parms.Num() );
- int preloadReg = 1;
- if ( flags & BIT( 0 ) ) {
- // load "this" into a register
- registers[ preloadReg ].SetObject( thisObject );
- preloadReg++;
- }
- if ( ( flags & BIT( 1 ) ) == 0 ) {
- // create "this"
- locals->Set( "this", idSWFScriptVar( thisObject ) );
- }
- if ( flags & BIT( 2 ) ) {
- idSWFScriptObject * arguments = idSWFScriptObject::Alloc();
- // load "arguments" into a register
- arguments->MakeArray();
- int numElements = parms.Num();
- for ( int i = 0; i < numElements; i++ ) {
- arguments->Set( i, parms[i] );
- }
- registers[ preloadReg ].SetObject( arguments );
- preloadReg++;
- arguments->Release();
- }
- if ( ( flags & BIT( 3 ) ) == 0 ) {
- idSWFScriptObject * arguments = idSWFScriptObject::Alloc();
- // create "arguments"
- arguments->MakeArray();
- int numElements = parms.Num();
- for ( int i = 0; i < numElements; i++ ) {
- arguments->Set( i, parms[i] );
- }
- locals->Set( "arguments", idSWFScriptVar( arguments ) );
- arguments->Release();
- }
- if ( flags & BIT( 4 ) ) {
- // load "super" into a register
- registers[ preloadReg ].SetObject( thisObject->GetPrototype() );
- preloadReg++;
- }
- if ( ( flags & BIT( 5 ) ) == 0 ) {
- // create "super"
- locals->Set( "super", idSWFScriptVar( thisObject->GetPrototype() ) );
- }
- if ( flags & BIT( 6 ) ) {
- // preload _root
- registers[ preloadReg ] = scope[0]->Get( "_root" );
- preloadReg++;
- }
- if ( flags & BIT( 7 ) ) {
- // preload _parent
- if ( thisObject->GetSprite() != NULL && thisObject->GetSprite()->parent != NULL ) {
- registers[ preloadReg ].SetObject( thisObject->GetSprite()->parent->scriptObject );
- } else {
- registers[ preloadReg ].SetNULL();
- }
- preloadReg++;
- }
- if ( flags & BIT( 8 ) ) {
- // load "_global" into a register
- registers[ preloadReg ].SetObject( scope[0] );
- preloadReg++;
- }
- int scopeSize = scope.Num();
- scope.Append( locals );
- locals->AddRef();
- idSWFScriptVar retVal = Run( thisObject, stack, bitstream );
- assert( scope.Num() == scopeSize + 1 );
- for ( int i = scopeSize; i < scope.Num(); i++ ) {
- if ( verify( scope[i] ) ) {
- scope[i]->Release();
- }
- }
- scope.SetNum( scopeSize );
- locals->Release();
- locals = NULL;
- return retVal;
- }
- /*
- ========================
- <anonymous>::Split
- ========================
- */
- namespace {
- const char * GetPropertyName( int index ) {
- switch ( index ) {
- case 0: return "_x";
- case 1: return "_y";
- case 2: return "_xscale";
- case 3: return "_yscale";
- case 4: return "_currentframe";
- case 5: return "_totalframes";
- case 6: return "_alpha";
- case 7: return "_visible";
- case 8: return "_width";
- case 9: return "_height";
- case 10: return "_rotation";
- case 11: return "_target";
- case 12: return "_framesloaded";
- case 13: return "_name";
- case 14: return "_droptarget";
- case 15: return "_url";
- case 16: return "_highquality";
- case 17: return "_focusrect";
- case 18: return "_soundbuftime";
- case 19: return "_quality";
- case 20: return "_mousex";
- case 21: return "_mousey";
- }
- return "";
- }
- const char *GetSwfActionName(swfAction_t code)
- {
- switch (code)
- {
- case Action_End: return "Action_End";
- // swf 3
- case Action_NextFrame: return "Action_NextFrame";
- case Action_PrevFrame: return "Action_PrevFrame";
- case Action_Play: return "Action_Play";
- case Action_Stop: return "Action_Stop";
- case Action_ToggleQuality: return "Action_ToggleQuality";
- case Action_StopSounds: return "Action_StopSounds";
- case Action_GotoFrame: return "Action_GotoFrame";
- case Action_GetURL: return "Action_GetURL";
- case Action_WaitForFrame: return "Action_WaitForFrame";
- case Action_SetTarget: return "Action_SetTarget";
- case Action_GoToLabel: return "Action_GoToLabel";
- // swf 4
- case Action_Add: return "Action_Add";
- case Action_Subtract: return "Action_Subtract";
- case Action_Multiply: return "Action_Multiply";
- case Action_Divide: return "Action_Divide";
- case Action_Equals: return "Action_Equals";
- case Action_Less: return "Action_Less";
- case Action_And: return "Action_And";
- case Action_Or: return "Action_Or";
- case Action_Not: return "Action_Not";
- case Action_StringEquals: return "Action_StringEquals";
- case Action_StringLength: return "Action_StringLength";
- case Action_StringExtract: return "Action_StringExtract";
- case Action_Pop: return "Action_Pop";
- case Action_ToInteger: return "Action_ToInteger";
- case Action_GetVariable: return "Action_GetVariable";
- case Action_SetVariable: return "Action_SetVariable";
- case Action_SetTarget2: return "Action_SetTarget2";
- case Action_StringAdd: return "Action_StringAdd";
- case Action_GetProperty: return "Action_GetProperty";
- case Action_SetProperty: return "Action_SetProperty";
- case Action_CloneSprite: return "Action_CloneSprite";
- case Action_RemoveSprite: return "Action_RemoveSprite";
- case Action_Trace: return "Action_Trace";
- case Action_StartDrag: return "Action_StartDrag";
- case Action_EndDrag: return "Action_EndDrag";
- case Action_StringLess: return "Action_StringLess";
- case Action_RandomNumber: return "Action_RandomNumber";
- case Action_MBStringLength: return "Action_MBStringLength";
- case Action_CharToAscii: return "Action_CharToAscii";
- case Action_AsciiToChar: return "Action_AsciiToChar";
- case Action_GetTime: return "Action_GetTime";
- case Action_MBStringExtract: return "Action_MBStringExtract";
- case Action_MBCharToAscii: return "Action_MBCharToAscii";
- case Action_MBAsciiToChar: return "Action_MBAsciiToChar";
- case Action_WaitForFrame2: return "Action_WaitForFrame2";
- case Action_Push: return "Action_Push";
- case Action_Jump: return "Action_Jump";
- case Action_GetURL2: return "Action_GetURL2";
- case Action_If: return "Action_If";
- case Action_Call: return "Action_Call";
- case Action_GotoFrame2: return "Action_GotoFrame2";
- // swf 5
- case Action_Delete: return "Action_Delete";
- case Action_Delete2: return "Action_Delete2";
- case Action_DefineLocal: return "Action_DefineLocal";
- case Action_CallFunction: return "Action_CallFunction";
- case Action_Return: return "Action_Return";
- case Action_Modulo: return "Action_Modulo";
- case Action_NewObject: return "Action_NewObject";
- case Action_DefineLocal2: return "Action_DefineLocal2";
- case Action_InitArray: return "Action_InitArray";
- case Action_InitObject: return "Action_InitObject";
- case Action_TypeOf: return "Action_TypeOf";
- case Action_TargetPath: return "Action_TargetPath";
- case Action_Enumerate: return "Action_Enumerate";
- case Action_Add2: return "Action_Add2";
- case Action_Less2: return "Action_Less2";
- case Action_Equals2: return "Action_Equals2";
- case Action_ToNumber: return "Action_ToNumber";
- case Action_ToString: return "Action_ToString";
- case Action_PushDuplicate: return "Action_PushDuplicate";
- case Action_StackSwap: return "Action_StackSwap";
- case Action_GetMember: return "Action_GetMember";
- case Action_SetMember: return "Action_SetMember";
- case Action_Increment: return "Action_Increment";
- case Action_Decrement: return "Action_Decrement";
- case Action_CallMethod: return "Action_CallMethod";
- case Action_NewMethod: return "Action_NewMethod";
- case Action_BitAnd: return "Action_BitAnd";
- case Action_BitOr: return "Action_BitOr";
- case Action_BitXor: return "Action_BitXor";
- case Action_BitLShift: return "Action_BitLShift";
- case Action_BitRShift: return "Action_BitRShift";
- case Action_BitURShift: return "Action_BitURShift";
- case Action_StoreRegister: return "Action_StoreRegister";
- case Action_ConstantPool: return "Action_ConstantPool";
- case Action_With: return "Action_With";
- case Action_DefineFunction: return "Action_DefineFunction";
- // swf 6
- case Action_InstanceOf: return "Action_InstanceOf";
- case Action_Enumerate2: return "Action_Enumerate2";
- case Action_StrictEquals: return "Action_StrictEquals";
- case Action_Greater: return "Action_Greater";
- case Action_StringGreater: return "Action_StringGreater";
- // swf 7
- case Action_Extends: return "Action_Extends";
- case Action_CastOp: return "Action_CastOp";
- case Action_ImplementsOp: return "Action_ImplementsOp";
- case Action_Throw: return "Action_Throw";
- case Action_Try: return "Action_Try";
- case Action_DefineFunction2: return "Action_DefineFunction2";
- default:
- return "UNKNOWN CODE";
- }
- }
- }
- /*
- ========================
- idSWFScriptFunction_Script::Run
- ========================
- */
- idSWFScriptVar idSWFScriptFunction_Script::Run( idSWFScriptObject * thisObject, idSWFStack & stack, idSWFBitStream & bitstream ) {
- static int callstackLevel = -1;
- idSWFSpriteInstance * thisSprite = thisObject->GetSprite();
- idSWFSpriteInstance * currentTarget = thisSprite;
- if ( currentTarget == NULL ) {
- thisSprite = currentTarget = defaultSprite;
- }
- callstackLevel++;
- while ( bitstream.Tell() < bitstream.Length() ) {
- swfAction_t code = (swfAction_t)bitstream.ReadU8();
- uint16 recordLength = 0;
- if ( code >= 0x80 ) {
- recordLength = bitstream.ReadU16();
- }
- if ( swf_debug.GetInteger() >= 3 ) {
- // stack[0] is always 0 so don't read it
- if ( swf_debug.GetInteger() >= 4 ) {
- for ( int i = stack.Num()-1; i >= 0 ; i-- ) {
- idLib::Printf(" %c: %s (%s)\n", (char)(64 + stack.Num() - i), stack[i].ToString().c_str(), stack[i].TypeOf());
- }
- for ( int i = 0; i < registers.Num(); i++ ) {
- if ( !registers[i].IsUndefined() ) {
- idLib::Printf(" R%d: %s (%s)\n", i, registers[i].ToString().c_str(), registers[i].TypeOf());
- }
- }
- }
- idLib::Printf( "SWF%d: code %s\n", callstackLevel, GetSwfActionName(code) );
- }
- switch ( code ) {
- case Action_Return:
- callstackLevel--;
- return stack.A();
- case Action_End:
- callstackLevel--;
- return idSWFScriptVar();
- case Action_NextFrame:
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->NextFrame();
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for nextFrame\n" );
- }
- break;
- case Action_PrevFrame:
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->PrevFrame();
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for prevFrame\n" );
- }
- break;
- case Action_Play:
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->Play();
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for play\n" );
- }
- break;
- case Action_Stop:
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->Stop();
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for stop\n" );
- }
- break;
- case Action_ToggleQuality: break;
- case Action_StopSounds: break;
- case Action_GotoFrame: {
- assert( recordLength == 2 );
- int frameNum = bitstream.ReadU16() + 1;
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->RunTo( frameNum );
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for runTo %d\n", frameNum );
- }
- break;
- }
- case Action_SetTarget: {
- const char * targetName = (const char *)bitstream.ReadData( recordLength );
- if ( verify( thisSprite != NULL ) ) {
- currentTarget = thisSprite->ResolveTarget( targetName );
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for setTarget %s\n", targetName );
- }
- break;
- }
- case Action_GoToLabel: {
- const char * targetName = (const char *)bitstream.ReadData( recordLength );
- if ( verify( currentTarget != NULL ) ) {
- currentTarget->RunTo( currentTarget->FindFrame( targetName ) );
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for runTo %s\n", targetName );
- }
- break;
- }
- case Action_Push: {
- idSWFBitStream pushstream( bitstream.ReadData( recordLength ), recordLength, false );
- while ( pushstream.Tell() < pushstream.Length() ) {
- uint8 type = pushstream.ReadU8();
- switch ( type ) {
- case 0: stack.Alloc().SetString( pushstream.ReadString() ); break;
- case 1: stack.Alloc().SetFloat( pushstream.ReadFloat() ); break;
- case 2: stack.Alloc().SetNULL(); break;
- case 3: stack.Alloc().SetUndefined(); break;
- case 4: stack.Alloc() = registers[ pushstream.ReadU8() ]; break;
- case 5: stack.Alloc().SetBool( pushstream.ReadU8() != 0 ); break;
- case 6: stack.Alloc().SetFloat( (float)pushstream.ReadDouble() ); break;
- case 7: stack.Alloc().SetInteger( pushstream.ReadS32() ); break;
- case 8: stack.Alloc().SetString( constants.Get( pushstream.ReadU8() ) ); break;
- case 9: stack.Alloc().SetString( constants.Get( pushstream.ReadU16() ) ); break;
- }
- }
- break;
- }
- case Action_Pop:
- stack.Pop( 1 );
- break;
- case Action_Add:
- stack.B().SetFloat( stack.B().ToFloat() + stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_Subtract:
- stack.B().SetFloat( stack.B().ToFloat() - stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_Multiply:
- stack.B().SetFloat( stack.B().ToFloat() * stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_Divide:
- stack.B().SetFloat( stack.B().ToFloat() / stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_Equals:
- stack.B().SetBool( stack.B().ToFloat() == stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_Less:
- stack.B().SetBool( stack.B().ToFloat() < stack.A().ToFloat() );
- stack.Pop( 1 );
- break;
- case Action_And:
- stack.B().SetBool( stack.B().ToBool() && stack.A().ToBool() );
- stack.Pop( 1 );
- break;
- case Action_Or:
- stack.B().SetBool( stack.B().ToBool() || stack.A().ToBool() );
- stack.Pop( 1 );
- break;
- case Action_Not:
- stack.A().SetBool( !stack.A().ToBool() );
- break;
- case Action_StringEquals:
- stack.B().SetBool( stack.B().ToString() == stack.A().ToString() );
- stack.Pop( 1 );
- break;
- case Action_StringLength:
- stack.A().SetInteger( stack.A().ToString().Length() );
- break;
- case Action_StringAdd:
- stack.B().SetString( stack.B().ToString() + stack.A().ToString() );
- stack.Pop( 1 );
- break;
- case Action_StringExtract:
- stack.C().SetString( stack.C().ToString().Mid( stack.B().ToInteger(), stack.A().ToInteger() ) );
- stack.Pop( 2 );
- break;
- case Action_StringLess:
- stack.B().SetBool( stack.B().ToString() < stack.A().ToString() );
- stack.Pop( 1 );
- break;
- case Action_StringGreater:
- stack.B().SetBool( stack.B().ToString() > stack.A().ToString() );
- stack.Pop( 1 );
- break;
- case Action_ToInteger:
- stack.A().SetInteger( stack.A().ToInteger() );
- break;
- case Action_CharToAscii:
- stack.A().SetInteger( stack.A().ToString()[0] );
- break;
- case Action_AsciiToChar:
- stack.A().SetString( va( "%c", stack.A().ToInteger() ) );
- break;
- case Action_Jump:
- bitstream.Seek( bitstream.ReadS16() );
- break;
- case Action_If: {
- int16 offset = bitstream.ReadS16();
- if ( stack.A().ToBool() ) {
- bitstream.Seek( offset );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_GetVariable: {
- idStr variableName = stack.A().ToString();
- for ( int i = scope.Num() - 1; i >= 0; i-- ) {
- stack.A() = scope[i]->Get( variableName );
- if ( !stack.A().IsUndefined() ) {
- break;
- }
- }
- if ( stack.A().IsUndefined() && swf_debug.GetInteger() > 1 ) {
- idLib::Printf( "SWF: unknown variable %s\n", variableName.c_str() );
- }
- break;
- }
- case Action_SetVariable: {
- idStr variableName = stack.B().ToString();
- bool found = false;
- for ( int i = scope.Num() - 1; i >= 0; i-- ) {
- if ( scope[i]->HasProperty( variableName ) ) {
- scope[i]->Set( variableName, stack.A() );
- found = true;
- break;
- }
- }
- if ( !found ) {
- thisObject->Set( variableName, stack.A() );
- }
- stack.Pop( 2 );
- break;
- }
- case Action_GotoFrame2: {
- uint32 frameNum = 0;
- uint8 flags = bitstream.ReadU8();
- if ( flags & 2 ) {
- frameNum += bitstream.ReadU16();
- }
- if ( verify( thisSprite != NULL ) ) {
- if ( stack.A().IsString() ) {
- frameNum += thisSprite->FindFrame( stack.A().ToString() );
- } else {
- frameNum += (uint32)stack.A().ToInteger();
- }
- if ( ( flags & 1 ) != 0 ){
- thisSprite->Play();
- } else {
- thisSprite->Stop();
- }
- thisSprite->RunTo( frameNum );
- } else if ( swf_debug.GetInteger() > 0 ) {
- if ( ( flags & 1 ) != 0 ){
- idLib::Printf( "SWF: no target movie clip for gotoAndPlay\n" );
- } else {
- idLib::Printf( "SWF: no target movie clip for gotoAndStop\n" );
- }
- }
- stack.Pop( 1 );
- break;
- }
- case Action_GetProperty: {
- if ( verify( thisSprite != NULL ) ) {
- idSWFSpriteInstance * target = thisSprite->ResolveTarget( stack.B().ToString() );
- stack.B() = target->scriptObject->Get( GetPropertyName( stack.A().ToInteger() ) );
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for getProperty\n" );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_SetProperty: {
- if ( verify( thisSprite != NULL ) ) {
- idSWFSpriteInstance * target = thisSprite->ResolveTarget( stack.C().ToString() );
- target->scriptObject->Set( GetPropertyName( stack.B().ToInteger() ), stack.A() );
- } else if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: no target movie clip for setProperty\n" );
- }
- stack.Pop( 3 );
- break;
- }
- case Action_Trace:
- idLib::PrintfIf( swf_debug.GetInteger() > 0, "SWF Trace: %s\n", stack.A().ToString().c_str() );
- stack.Pop( 1 );
- break;
- case Action_GetTime:
- stack.Alloc().SetInteger( Sys_Milliseconds() );
- break;
- case Action_RandomNumber:
- assert( thisSprite && thisSprite->sprite && thisSprite->sprite->GetSWF() );
- stack.A().SetInteger( thisSprite->sprite->GetSWF()->GetRandom().RandomInt( stack.A().ToInteger() ) );
- break;
- case Action_CallFunction: {
- idStr functionName = stack.A().ToString();
- idSWFScriptVar function;
- idSWFScriptObject * object = NULL;
- for ( int i = scope.Num() - 1; i >= 0; i-- ) {
- function = scope[i]->Get( functionName );
- if ( !function.IsUndefined() ) {
- object = scope[i];
- break;
- }
- }
- stack.Pop( 1 );
- idSWFParmList parms;
- parms.SetNum( stack.A().ToInteger() );
- stack.Pop( 1 );
- for ( int i = 0; i < parms.Num(); i++ ) {
- parms[i] = stack.A();
- stack.Pop( 1 );
- }
- if ( function.IsFunction() && verify( object ) ) {
- stack.Alloc() = function.GetFunction()->Call( object, parms );
- } else {
- idLib::PrintfIf( swf_debug.GetInteger() > 0, "SWF: unknown function %s\n", functionName.c_str() );
- stack.Alloc().SetUndefined();
- }
- break;
- }
- case Action_CallMethod: {
- idStr functionName = stack.A().ToString();
- // If the top stack is undefined but there is an object, it's calling the constructor
- if ( functionName.IsEmpty() || stack.A().IsUndefined() || stack.A().IsNULL() ) {
- functionName = "__constructor__";
- }
- idSWFScriptObject * object = NULL;
- idSWFScriptVar function;
- if ( stack.B().IsObject() ) {
- object = stack.B().GetObject();
- function = object->Get( functionName );
- if ( !function.IsFunction() ) {
- idLib::PrintfIf( swf_debug.GetInteger() > 1, "SWF: unknown method %s on %s\n", functionName.c_str(), object->DefaultValue( true ).ToString().c_str() );
- }
- } else {
- idLib::PrintfIf( swf_debug.GetInteger() > 1, "SWF: NULL object for method %s\n", functionName.c_str() );
- }
- stack.Pop( 2 );
- idSWFParmList parms;
- parms.SetNum( stack.A().ToInteger() );
- stack.Pop( 1 );
- for ( int i = 0; i < parms.Num(); i++ ) {
- parms[i] = stack.A();
- stack.Pop( 1 );
- }
- if ( function.IsFunction() ) {
- stack.Alloc() = function.GetFunction()->Call( object, parms );
- } else {
- stack.Alloc().SetUndefined();
- }
- break;
- }
- case Action_ConstantPool: {
- constants.Clear();
- uint16 numConstants = bitstream.ReadU16();
- for ( int i = 0; i < numConstants; i++ ) {
- constants.Append( idSWFScriptString::Alloc( bitstream.ReadString() ) );
- }
- break;
- }
- case Action_DefineFunction: {
- idStr functionName = bitstream.ReadString();
- idSWFScriptFunction_Script * newFunction = idSWFScriptFunction_Script::Alloc();
- newFunction->SetScope( scope );
- newFunction->SetConstants( constants );
- newFunction->SetDefaultSprite( defaultSprite );
- uint16 numParms = bitstream.ReadU16();
- newFunction->AllocParameters( numParms );
- for ( int i = 0; i < numParms; i++ ) {
- newFunction->SetParameter( i, 0, bitstream.ReadString() );
- }
- uint16 codeSize = bitstream.ReadU16();
- newFunction->SetData( bitstream.ReadData( codeSize ), codeSize );
- if ( functionName.IsEmpty() ) {
- stack.Alloc().SetFunction( newFunction );
- } else {
- thisObject->Set( functionName, idSWFScriptVar( newFunction ) );
- }
- newFunction->Release();
- break;
- }
- case Action_DefineFunction2: {
- idStr functionName = bitstream.ReadString();
- idSWFScriptFunction_Script * newFunction = idSWFScriptFunction_Script::Alloc();
- newFunction->SetScope( scope );
- newFunction->SetConstants( constants );
- newFunction->SetDefaultSprite( defaultSprite );
- uint16 numParms = bitstream.ReadU16();
- // The number of registers is from 0 to 255, although valid values are 1 to 256.
- // There must always be at least one register for DefineFunction2, to hold "this" or "super" when required.
- uint8 numRegs = bitstream.ReadU8() + 1;
- // Note that SWF byte-ordering causes the flag bits to be reversed per-byte
- // from how the swf_file_format_spec_v10.pdf document describes the ordering in ActionDefineFunction2.
- // PreloadThisFlag is byte 0, not 7, PreloadGlobalFlag is 8, not 15.
- uint16 flags = bitstream.ReadU16();
- newFunction->AllocParameters( numParms );
- newFunction->AllocRegisters( numRegs );
- newFunction->SetFlags( flags );
- for ( int i = 0; i < numParms; i++ ) {
- uint8 reg = bitstream.ReadU8();
- const char * name = bitstream.ReadString();
- if ( reg >= numRegs ) {
- idLib::Warning( "SWF: Parameter %s in function %s bound to out of range register %d", name, functionName.c_str(), reg );
- reg = 0;
- }
- newFunction->SetParameter( i, reg, name );
- }
- uint16 codeSize = bitstream.ReadU16();
- newFunction->SetData( bitstream.ReadData( codeSize ), codeSize );
- if ( functionName.IsEmpty() ) {
- stack.Alloc().SetFunction( newFunction );
- } else {
- thisObject->Set( functionName, idSWFScriptVar( newFunction ) );
- }
- newFunction->Release();
- break;
- }
- case Action_Enumerate: {
- idStr variableName = stack.A().ToString();
- for ( int i = scope.Num() - 1; i >= 0; i-- ) {
- stack.A() = scope[i]->Get( variableName );
- if ( !stack.A().IsUndefined() ) {
- break;
- }
- }
- if ( !stack.A().IsObject() ) {
- stack.A().SetNULL();
- } else {
- idSWFScriptObject * object = stack.A().GetObject();
- object->AddRef();
- stack.A().SetNULL();
- for ( int i = 0; i < object->NumVariables(); i++ ) {
- stack.Alloc().SetString( object->EnumVariable( i ) );
- }
- object->Release();
- }
- break;
- }
- case Action_Enumerate2: {
- if ( !stack.A().IsObject() ) {
- stack.A().SetNULL();
- } else {
- idSWFScriptObject * object = stack.A().GetObject();
- object->AddRef();
- stack.A().SetNULL();
- for ( int i = 0; i < object->NumVariables(); i++ ) {
- stack.Alloc().SetString( object->EnumVariable( i ) );
- }
- object->Release();
- }
- break;
- }
- case Action_Equals2: {
- stack.B().SetBool( stack.A().AbstractEquals( stack.B() ) );
- stack.Pop( 1 );
- break;
- }
- case Action_StrictEquals: {
- stack.B().SetBool( stack.A().StrictEquals( stack.B() ) );
- stack.Pop( 1 );
- break;
- }
- case Action_GetMember: {
- if ( ( stack.B().IsUndefined() || stack.B().IsNULL() ) && swf_debug.GetInteger() > 1 ) {
- idLib::Printf( "SWF: tried to get member %s on an invalid object in sprite '%s'\n", stack.A().ToString().c_str(), thisSprite != NULL ? thisSprite->GetName() : "" );
- }
- if ( stack.B().IsObject() ) {
- idSWFScriptObject * object = stack.B().GetObject();
- if ( stack.A().IsNumeric() ) {
- stack.B() = object->Get( stack.A().ToInteger() );
- } else {
- stack.B() = object->Get( stack.A().ToString() );
- }
- if ( stack.B().IsUndefined() && swf_debug.GetInteger() > 1 ) {
- idLib::Printf( "SWF: unknown member %s\n", stack.A().ToString().c_str() );
- }
- } else if ( stack.B().IsString() ) {
- idStr propertyName = stack.A().ToString();
- if ( propertyName.Cmp( "length" ) == 0 ) {
- stack.B().SetInteger( stack.B().ToString().Length() );
- } else if ( propertyName.Cmp( "value" ) == 0 ) {
- // Do nothing
- } else {
- stack.B().SetUndefined();
- }
- } else if ( stack.B().IsFunction() ) {
- idStr propertyName = stack.A().ToString();
- if ( propertyName.Cmp( "prototype" ) == 0 ) {
- // if this is a function, it's a class definition function, and it just wants the prototype object
- // create it if it hasn't been already, and return it
- idSWFScriptFunction * sfs = stack.B().GetFunction();
- idSWFScriptObject * object = sfs->GetPrototype();
- if ( object == NULL ) {
- object = idSWFScriptObject::Alloc();
- // Set the __proto__ to the main Object prototype
- idSWFScriptVar baseObjConstructor = scope[0]->Get( "Object" );
- idSWFScriptFunction *baseObj = baseObjConstructor.GetFunction();
- object->Set( "__proto__", baseObj->GetPrototype() );
- sfs->SetPrototype( object );
- }
- stack.B() = idSWFScriptVar( object );
- } else {
- stack.B().SetUndefined();
- }
- } else {
- stack.B().SetUndefined();
- }
- stack.Pop( 1 );
- break;
- }
- case Action_SetMember: {
- if ( stack.C().IsObject() ) {
- idSWFScriptObject * object = stack.C().GetObject();
- if ( stack.B().IsNumeric() ) {
- object->Set( stack.B().ToInteger(), stack.A() );
- } else {
- object->Set( stack.B().ToString(), stack.A() );
- }
- }
- stack.Pop( 3 );
- break;
- }
- case Action_InitArray: {
- idSWFScriptObject * object = idSWFScriptObject::Alloc();
- object->MakeArray();
- int numElements = stack.A().ToInteger();
- stack.Pop( 1 );
- for ( int i = 0; i < numElements; i++ ) {
- object->Set( i, stack.A() );
- stack.Pop( 1 );
- }
- stack.Alloc().SetObject( object );
- object->Release();
- break;
- }
- case Action_InitObject: {
- idSWFScriptObject * object = idSWFScriptObject::Alloc();
- int numElements = stack.A().ToInteger();
- stack.Pop( 1 );
- for ( int i = 0; i < numElements; i++ ) {
- object->Set( stack.B().ToString(), stack.A() );
- stack.Pop( 2 );
- }
- stack.Alloc().SetObject( object );
- object->Release();
- break;
- }
- case Action_NewObject: {
- idSWFScriptObject * object = idSWFScriptObject::Alloc();
- idStr functionName = stack.A().ToString();
- stack.Pop( 1 );
- if ( functionName.Cmp( "Array" ) == 0 ) {
- object->MakeArray();
- int numElements = stack.A().ToInteger();
- stack.Pop( 1 );
- for ( int i = 0; i < numElements; i++ ) {
- object->Set( i, stack.A() );
- stack.Pop( 1 );
- }
- idSWFScriptVar baseObjConstructor = scope[0]->Get( "Object" );
- idSWFScriptFunction *baseObj = baseObjConstructor.GetFunction();
- object->Set( "__proto__", baseObj->GetPrototype() );
- // object prototype is not set here because it will be auto created from Object later
- } else {
- idSWFParmList parms;
- parms.SetNum( stack.A().ToInteger() );
- stack.Pop( 1 );
- for ( int i = 0; i < parms.Num(); i++ ) {
- parms[i] = stack.A();
- stack.Pop( 1 );
- }
- idSWFScriptVar objdef = scope[0]->Get( functionName );
- if ( objdef.IsFunction() ) {
- idSWFScriptFunction * constructorFunction = objdef.GetFunction();
- object->Set( "__proto__", constructorFunction->GetPrototype() );
- object->SetPrototype( constructorFunction->GetPrototype() );
- constructorFunction->Call( object, parms );
- } else {
- idLib::Warning( "SWF: Unknown class definition %s", functionName.c_str() );
- }
- }
- stack.Alloc().SetObject( object );
- object->Release();
- break;
- }
- case Action_Extends: {
- idSWFScriptFunction * superclassConstructorFunction = stack.A().GetFunction();
- idSWFScriptFunction *subclassConstructorFunction = stack.B().GetFunction();
- stack.Pop( 2 );
- idSWFScriptObject * scriptObject = idSWFScriptObject::Alloc();
- scriptObject->SetPrototype( superclassConstructorFunction->GetPrototype() );
- scriptObject->Set( "__proto__", idSWFScriptVar( superclassConstructorFunction->GetPrototype() ) );
- scriptObject->Set( "__constructor__", idSWFScriptVar( superclassConstructorFunction ) );
- subclassConstructorFunction->SetPrototype( scriptObject );
- scriptObject->Release();
- break;
- }
- case Action_TargetPath: {
- if ( !stack.A().IsObject() ) {
- stack.A().SetUndefined();
- } else {
- idSWFScriptObject * object = stack.A().GetObject();
- if ( object->GetSprite() == NULL ) {
- stack.A().SetUndefined();
- } else {
- idStr dotName = object->GetSprite()->name.c_str();
- for ( idSWFSpriteInstance * target = object->GetSprite()->parent; target != NULL; target = target->parent ) {
- dotName = target->name + "." + dotName;
- }
- stack.A().SetString( dotName );
- }
- }
- break;
- }
- case Action_With: {
- int withSize = bitstream.ReadU16();
- idSWFBitStream bitstream2( bitstream.ReadData( withSize ), withSize, false );
- if ( stack.A().IsObject() ) {
- idSWFScriptObject * withObject = stack.A().GetObject();
- withObject->AddRef();
- stack.Pop( 1 );
- scope.Append( withObject );
- Run( thisObject, stack, bitstream2 );
- scope.SetNum( scope.Num() - 1 );
- withObject->Release();
- } else {
- if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: with() invalid object specified\n" );
- }
- stack.Pop( 1 );
- }
- break;
- }
- case Action_ToNumber:
- stack.A().SetFloat( stack.A().ToFloat() );
- break;
- case Action_ToString:
- stack.A().SetString( stack.A().ToString() );
- break;
- case Action_TypeOf:
- stack.A().SetString( stack.A().TypeOf() );
- break;
- case Action_Add2: {
- if ( stack.A().IsString() || stack.B().IsString() ) {
- stack.B().SetString( stack.B().ToString() + stack.A().ToString() );
- } else {
- stack.B().SetFloat( stack.B().ToFloat() + stack.A().ToFloat() );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_Less2: {
- if ( stack.A().IsString() && stack.B().IsString() ) {
- stack.B().SetBool( stack.B().ToString() < stack.A().ToString() );
- } else {
- stack.B().SetBool( stack.B().ToFloat() < stack.A().ToFloat() );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_Greater: {
- if ( stack.A().IsString() && stack.B().IsString() ) {
- stack.B().SetBool( stack.B().ToString() > stack.A().ToString() );
- } else {
- stack.B().SetBool( stack.B().ToFloat() > stack.A().ToFloat() );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_Modulo: {
- int32 a = stack.A().ToInteger();
- int32 b = stack.B().ToInteger();
- if ( a == 0 ) {
- stack.B().SetUndefined();
- } else {
- stack.B().SetInteger( b % a );
- }
- stack.Pop( 1 );
- break;
- }
- case Action_BitAnd:
- stack.B().SetInteger( stack.B().ToInteger() & stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_BitLShift:
- stack.B().SetInteger( stack.B().ToInteger() << stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_BitOr:
- stack.B().SetInteger( stack.B().ToInteger() | stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_BitRShift:
- stack.B().SetInteger( stack.B().ToInteger() >> stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_BitURShift:
- stack.B().SetInteger( (uint32)stack.B().ToInteger() >> stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_BitXor:
- stack.B().SetInteger( stack.B().ToInteger() ^ stack.A().ToInteger() );
- stack.Pop( 1 );
- break;
- case Action_Decrement:
- stack.A().SetFloat( stack.A().ToFloat() - 1.0f );
- break;
- case Action_Increment:
- stack.A().SetFloat( stack.A().ToFloat() + 1.0f );
- break;
- case Action_PushDuplicate: {
- idSWFScriptVar dup = stack.A();
- stack.Alloc() = dup;
- break;
- }
- case Action_StackSwap: {
- idSWFScriptVar temp = stack.A();
- stack.A() = stack.B();
- stack.A() = temp;
- break;
- }
- case Action_StoreRegister: {
- uint8 registerNumber = bitstream.ReadU8();
- registers[ registerNumber ] = stack.A();
- break;
- }
- case Action_DefineLocal: {
- scope[scope.Num()-1]->Set( stack.B().ToString(), stack.A() );
- stack.Pop( 2 );
- break;
- }
- case Action_DefineLocal2: {
- scope[scope.Num()-1]->Set( stack.A().ToString(), idSWFScriptVar() );
- stack.Pop( 1 );
- break;
- }
- case Action_Delete: {
- if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: Delete ignored\n" );
- }
- // We no longer support deleting variables because the performance cost of updating the hash tables is not worth it
- stack.Pop( 2 );
- break;
- }
- case Action_Delete2: {
- if ( swf_debug.GetInteger() > 0 ) {
- idLib::Printf( "SWF: Delete2 ignored\n" );
- }
- // We no longer support deleting variables because the performance cost of updating the hash tables is not worth it
- stack.Pop( 1 );
- break;
- }
- // These are functions we just don't support because we never really needed to
- case Action_CloneSprite:
- case Action_RemoveSprite:
- case Action_Call:
- case Action_SetTarget2:
- case Action_NewMethod:
- default:
- idLib::Warning( "SWF: Unhandled Action %s", idSWF::GetActionName( code ) );
- // We have to abort here because the rest of the script is basically meaningless now
- assert( false );
- callstackLevel--;
- return idSWFScriptVar();
- }
- }
- callstackLevel--;
- return idSWFScriptVar();
- }
- /*
- ========================
- idSWF::Invoke
- ========================
- */
- void idSWF::Invoke( const char * functionName, const idSWFParmList & parms ) {
- idSWFScriptObject * obj = mainspriteInstance->GetScriptObject();
- idSWFScriptVar scriptVar = obj->Get( functionName );
- if ( swf_debugInvoke.GetBool() ) {
- idLib::Printf( "SWF: Invoke %s with %d parms (%s)\n", functionName, parms.Num(), GetName() );
- }
- if ( scriptVar.IsFunction() ) {
- scriptVar.GetFunction()->Call( NULL, parms );
- }
- }
- /*
- ========================
- idSWF::Invoke
- ========================
- */
- void idSWF::Invoke( const char * functionName, const idSWFParmList & parms, idSWFScriptVar & scriptVar ) {
- if ( scriptVar.IsFunction() ) {
- scriptVar.GetFunction()->Call( NULL, parms );
- } else {
- idSWFScriptObject * obj = mainspriteInstance->GetScriptObject();
- scriptVar = obj->Get( functionName );
- if ( scriptVar.IsFunction() ) {
- scriptVar.GetFunction()->Call( NULL, parms );
- }
- }
- }
- /*
- ========================
- idSWF::Invoke
- ========================
- */
- void idSWF::Invoke( const char * functionName, const idSWFParmList & parms, bool & functionExists ) {
- idSWFScriptObject * obj = mainspriteInstance->GetScriptObject();
- idSWFScriptVar scriptVar = obj->Get( functionName );
- if ( swf_debugInvoke.GetBool() ) {
- idLib::Printf( "SWF: Invoke %s with %d parms (%s)\n", functionName, parms.Num(), GetName() );
- }
- if ( scriptVar.IsFunction() ) {
- scriptVar.GetFunction()->Call( NULL, parms );
- functionExists = true;
- } else {
- functionExists = false;
- }
- }
|