diff --git a/neo/d3xp/menus/MenuHandler_Shell.cpp b/neo/d3xp/menus/MenuHandler_Shell.cpp index 40010ed841..630cc4bf93 100644 --- a/neo/d3xp/menus/MenuHandler_Shell.cpp +++ b/neo/d3xp/menus/MenuHandler_Shell.cpp @@ -60,7 +60,7 @@ void idMenuHandler_Shell::Update() ClearWidgetActionRepeater(); } - if( nextState != state ) + if( nextState != state && gui->IsContructed() ) { if( introGui != NULL && introGui->IsActive() ) @@ -162,7 +162,7 @@ void idMenuHandler_Shell::Update() } } - if( activeScreen != nextScreen ) + if( activeScreen != nextScreen && gui->IsContructed() ) { ClearWidgetActionRepeater(); diff --git a/neo/swf/SWF.h b/neo/swf/SWF.h index 260f84c096..bb53487c55 100644 --- a/neo/swf/SWF.h +++ b/neo/swf/SWF.h @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013 Robert Beckebans +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -41,8 +42,10 @@ If you have questions concerning this license or the applicable additional terms #include "SWF_ParmList.h" #include "SWF_ScriptFunction.h" #include "SWF_SpriteInstance.h" +#include "SWF_EventDispatcher.h" #include "SWF_ShapeParser.h" #include "SWF_TextInstance.h" +#include "SWF_Abc.h" class idSWFDictionaryEntry { @@ -65,6 +68,9 @@ class idSWFDictionaryEntry // the compressed images are normalize to reduce compression artifacts, // color must be scaled down by this idVec4 channelScale; + idSWFScriptVar scriptClass; + bool resolved; + idStrPtr name; }; struct purgableSwfImage_t @@ -153,6 +159,11 @@ class idSWF return *( mainspriteInstance->GetScriptObject() ); } + bool IsContructed() + { + return mainspriteInstance->constructed; + } + void Invoke( const char* functionName, const idSWFParmList& parms ); void Invoke( const char* functionName, const idSWFParmList& parms, idSWFScriptVar& scriptVar ); void Invoke( const char* functionName, const idSWFParmList& parms, bool& functionExists ); @@ -215,6 +226,8 @@ class idSWF idSWFScriptObject* HitTest( idSWFSpriteInstance* spriteInstance, const swfRenderState_t& renderState, int x, int y, idSWFScriptObject* parentObject ); + SWF_AbcFile abcFile; + static bool isMouseInClientArea; private: idStr filename; ID_TIME_T timestamp; @@ -251,7 +264,7 @@ class idSWF static int mouseX; // mouse x coord for all flash files static int mouseY; // mouse y coord for all flash files - static bool isMouseInClientArea; + idSWFScriptObject* mouseObject; idSWFScriptObject* hoverObject; @@ -269,44 +282,75 @@ class idSWF idBlockAlloc< idSWFSpriteInstance, 16 > spriteInstanceAllocator; idBlockAlloc< idSWFTextInstance, 16 > textInstanceAllocator; +#define SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( x ) \ + class idSWFScriptFunction_##x : public idSWFScriptFunction_Nested< idSWF > { \ + public: \ + idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ + } scriptFunction_##x; + +#define SWF_NATIVE_FUNCTION_SWF_ASAPI_DECLARE( x ) \ + class idSWFScriptFunction_##x : public idSWFScriptFunction_Nested< idSWF > { \ + public: \ + idSWFScriptFunction_##x(){ static bool once = false; if (!once) {idSwfActionScriptAPI::actionScriptAPIs.Alloc() = this; once = true;}}\ + idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ + const char * GetActionScriptAPI( idStr & out ) ;\ + } scriptFunction_##x; + +#define SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( x, type, val ) \ + class idSWFScriptFunction_##x : public idSWFScriptFunction_Nested< idSWF > { \ + public: \ + idSWFScriptFunction_##x(){ static bool once = false; if (!once) {idSwfActionScriptAPI::actionScriptAPIs.Alloc() = this; once = true;}}\ + idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ + const char * GetActionScriptAPI( idStr & out ) { out = "/** \n * Generated by RBDoom3BFG \n*/\npackage{\n\tpublic function "; out+= #x;out+= +"( ... parms) : ";out+=#type;out+=" { trace(\""; out+= #x; out+="( \" + parms + \" )\");return ";out+= #val;out+=";};\n}"; return #x;} \ + } scriptFunction_##x; + #define SWF_NATIVE_FUNCTION_SWF_DECLARE( x ) \ class idSWFScriptFunction_##x : public idSWFScriptFunction_Nested< idSWF > { \ public: \ + idSWFScriptFunction_##x(){ static bool once = false; if (!once) {idSwfActionScriptAPI::actionScriptAPIs.Alloc() = this; once = true;}}\ idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ + const char * GetActionScriptAPI( idStr & out ) { out = "/** \n * Generated by RBDoom3BFG \n*/\npackage{\n\tpublic function "; out+= #x;out+= +"( ... parms) :void { trace(\""; out+= #x; out+="( \" + parms + \" )\")};\n}"; return #x;} \ } scriptFunction_##x; SWF_NATIVE_FUNCTION_SWF_DECLARE( shortcutKeys_clear ); SWF_NATIVE_FUNCTION_SWF_DECLARE( deactivate ); SWF_NATIVE_FUNCTION_SWF_DECLARE( inhibitControl ); SWF_NATIVE_FUNCTION_SWF_DECLARE( useInhibit ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( precacheSound ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( precacheSound, String, parms[0] ); SWF_NATIVE_FUNCTION_SWF_DECLARE( playSound ); SWF_NATIVE_FUNCTION_SWF_DECLARE( stopSounds ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( getPlatform ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( getTruePlatform ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( getLocalString ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( swapPS3Buttons ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( getCVarInteger ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( setCVarInteger ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( strReplace ); - - SWF_NATIVE_FUNCTION_SWF_DECLARE( acos ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( cos ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( sin ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( round ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( pow ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( sqrt ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( abs ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( rand ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( floor ); - SWF_NATIVE_FUNCTION_SWF_DECLARE( ceil ); - - SWF_NATIVE_FUNCTION_SWF_DECLARE( toUpper ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( getPlatform, Number, 2 ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( getTruePlatform, Number, 2 ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( getLocalString, String, parms[0] ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( swapPS3Buttons, Boolean, false ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( getCVarInteger, Number , 1 ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( setCVarInteger ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( strReplace, String, parms[0].replace( parms[1], parms[2] ) ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( trace ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( ArrayToString ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( registerUserMouse ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( String ); + + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( acos, Number, Math.acos( parms[0] ) ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( cos, Number, Math.cos( parms[0] ) ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( sin ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( round ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( pow ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( sqrt ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( abs ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( rand ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( floor ); + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( ceil ); + SWF_NATIVE_FUNCTION_SWF_ASAPI_RETURNVAL_DECLARE( random, Number, Math.random( parms ) ); + + SWF_NATIVE_FUNCTION_SWF_NOASAPI_DECLARE( toUpper ); SWF_NATIVE_VAR_DECLARE_NESTED_READONLY( platform, idSWFScriptFunction_getPlatform, Call( object, idSWFParmList() ) ); SWF_NATIVE_VAR_DECLARE_NESTED( blackbars, idSWF ); SWF_NATIVE_VAR_DECLARE_NESTED( crop, idSWF ); + class idSWFScriptFunction_Object; + SWF_NATIVE_VAR_DECLARE_NESTED_READONLY( Object, idSWFScriptFunction_Object, Call( object, idSWFParmList() ) ); class idSWFScriptFunction_Object : public idSWFScriptFunction { public: @@ -516,6 +560,12 @@ class idSWF // SWF_Zlib.cpp //---------------------------------- bool Inflate( const byte* input, int inputSize, byte* output, int outputSize ); + //---------------------------------- + // SWF_Abc.cpp + //---------------------------------- + void DoABC( idSWFBitStream& bitstream ) ; + void SymbolClass( idSWFBitStream& bitstream ) ; + // RB begin bool Deflate( const byte* input, int inputSize, byte* output, int& outputSize ); // RB end @@ -531,6 +581,8 @@ class idSWF static const char* GetTagName( swfTag_t tag ); static const char* GetActionName( swfAction_t action ); + void CreateAbcObjects( idSWFScriptObject* globals ); + SWF_SymbolClass symbolClasses; }; #endif // !__SWF_H__ diff --git a/neo/swf/SWF_Abc.cpp b/neo/swf/SWF_Abc.cpp new file mode 100644 index 0000000000..bbeccf0089 --- /dev/null +++ b/neo/swf/SWF_Abc.cpp @@ -0,0 +1,879 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2023 Harrie van Ginneken + +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 . + +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. + +=========================================================================== +*/ + +//.AS test +//trace( "hello" ) +//trace( "world" ) +//var clickCount = 0; +//function playAnimation( event:MouseEvent ) :void { +// clickCount++; +// trace( text1.text ) +// text1.text = "clicked me " + clickCount; +//} +//// Register the function as a listener with the button. +//addEventListener( MouseEvent.CLICK, playAnimation ); + +#include "precompiled.h" +#pragma hdrstop + +#include "SWF_Abc.h" + +idCVar swf_abc_verbose( "swf_abc_verbose", "0", CVAR_INTEGER, "1 : writes out all abc data read \n 2 : print bytecode " ); + +#pragma warning( disable: 4189 ) // local variable is initialized but not referenced + +void trace( const char* fmt, ... ) +{ + if( swf_abc_verbose.GetInteger() == 1 ) + { + //common->PrintPrefix("[SWF]"); + va_list argptr; + va_start( argptr, fmt ); + common->VPrintf( fmt, argptr ); + va_end( argptr ); + //common->PrintPrefix(""); + } +} + +#define toString(x) asString(x,constant_pool) + +idStr SWF_AbcFile::asString( swfConstantKind_t kind, swfConstant_pool_info& constant_pool ) +{ + idStr type; +#define switchTrace( n ) case n: type = #n;break; + switch( kind ) + { + switchTrace( unused_0x00 ); + switchTrace( Utf8 ); + switchTrace( Int ); + switchTrace( UInt ); + switchTrace( PrivateNs ); + switchTrace( Double ); + switchTrace( Qname ); + switchTrace( Namespace ); + switchTrace( False ); + switchTrace( True ); + switchTrace( Null ); + switchTrace( QnameA ); + switchTrace( RTQname ); + switchTrace( RTQnameA ); + switchTrace( RTQnameL ); + switchTrace( RTQnameLA ); + switchTrace( Multiname ); + switchTrace( MultinameA ); + switchTrace( MultinameL ); + switchTrace( MultinameLA ); + switchTrace( PackageNamespace ); + switchTrace( PackageInternalNs ); + switchTrace( ProtectedNamespace ); + switchTrace( ExplicitNamespace ); + switchTrace( StaticProtectedNs ); + } + return type; +#undef switchTrace +} + +idStr SWF_AbcFile::asString( swfMultiname* mn, swfConstant_pool_info& constant_pool, bool prefix /*= true*/ ) +{ + idStr ret; + if( prefix ) + { + ret += toString( mn->type ); + } + switch( mn->type ) + { + case RTQnameL: + case RTQnameLA: + ret += "null::null"; + break; + case Qname: + case QnameA: + ret += " "; + ret += *constant_pool.namespaceNames[mn->index]; + if( mn->index != 1 ) + { + ret += "."; + } + ret += constant_pool.utf8Strings[mn->nameIndex]; + break; + + case RTQname: + case RTQnameA: + ret += " "; + ret += constant_pool.utf8Strings[mn->nameIndex]; + break; + + case TypeName: + case Multiname: + case MultinameA: + ret += " "; + ret += asString( &constant_pool.multinameInfos[mn->index], constant_pool ); + ret += "."; + ret += constant_pool.utf8Strings[mn->nameIndex]; + break; + + case MultinameL: + case MultinameLA: + ret += " "; + for( auto* str : constant_pool.namespaceSets[mn->index] ) + { + ret += *str; + ret += "."; + }; + break; + case unused_0x00: + ret += " unused_0x00"; + break; + default: + common->FatalError( "Invalid Multiname type" ); + break; + } + + return ret; +} + +void SWF_AbcFile::traceMN( const char* name, swfMultiname* mn, swfConstant_pool_info& constant_pool ) +{ + idStr type = asString( mn, constant_pool ); + trace( "%s %s \n", name, type.c_str() ); +} + +void SWF_AbcFile::traceConstantPool( swfConstant_pool_info& constant_pool ) +{ + int cnt = 0; + + trace( "^8========================================================\n" ); + trace( " constant pool \n" ); + trace( "^8========================================================\n" ); + + trace( "^8integers : ^7%i\n", constant_pool.integers.Num() ); + for( auto& t : constant_pool.integers ) + { + trace( "^8[^7%i^8]\t ^7%i \n", cnt++, t ); + } + trace( "^8uIntegers ^7: %i\n", constant_pool.uIntegers.Num() ); + cnt = 0; + for( auto& t : constant_pool.uIntegers ) + { + trace( "^8[^7%i^8]\t ^7%i \n", cnt++, ( int )t ); + } + trace( "^8doubles ^7: %i\n", constant_pool.doubles.Num() ); + cnt = 0; + for( auto& t : constant_pool.doubles ) + { + trace( "^8[^7%i^8]\t ^7%f \n", cnt++, ( float ) t ); + } + trace( "^8utf8Strings ^7: %i\n", constant_pool.utf8Strings.Num() ); + cnt = 0; + for( auto& t : constant_pool.utf8Strings ) + { + trace( "^8[^7%i^8]\t ^7%s \n", cnt++, t.c_str() ); + } + trace( "^8namespaceNames ^7: %i\n", constant_pool.namespaceNames.Num() ); + cnt = 0; + for( auto& t : constant_pool.namespaceNames ) + { + trace( "^8[^7%i^8]\t ^7%s \n", cnt++, t->c_str() ); + } + + trace( "^8namespaceSets ^7: %i\n", constant_pool.namespaceSets.Num() ); + cnt = 0; + for( auto& t : constant_pool.namespaceSets ) + { + for( auto& ts : t ) + { + trace( "^8[^7%i^8]\t ^7%s \n", cnt++, ts->c_str() ); + } + } + + trace( "^8multinameInfos ^7: %i\n", constant_pool.multinameInfos.Num() ); + cnt = 0; + for( auto& t : constant_pool.multinameInfos ) + { + idStr pre = "^8[^7"; + pre += idStr( cnt++ ); + pre += "^8]^7\t"; + traceMN( pre.c_str(), &t, constant_pool ); + } + + trace( "^8========================================================\n" ); +} + + +void ReadMultiName( idSWFBitStream& bitstream , swfMultiname& target ) +{ + target.type = ( swfConstantKind_t )bitstream.ReadU8(); + target.index = 0; + target.nameIndex = 0; + switch( target.type ) + { + case RTQnameL: + case RTQnameLA: + //0,0 + break; + case Qname: + case QnameA: + target.index = bitstream.ReadEncodedU32(); + target.nameIndex = bitstream.ReadEncodedU32(); + break; + + case RTQname: + case RTQnameA: + target.nameIndex = bitstream.ReadEncodedU32(); + break; + + case TypeName: + target.nameIndex = bitstream.ReadEncodedU32(); + target.nameIndexT = bitstream.ReadEncodedU32(); + target.indexT = bitstream.ReadEncoded(); + break; + case Multiname: + case MultinameA: + target.nameIndex = bitstream.ReadEncodedU32(); + target.index = bitstream.ReadEncodedU32(); + break; + + case MultinameL: + case MultinameLA: + target.index = bitstream.ReadEncodedU32(); + break; + default: + common->FatalError( "Invalid Multiname type" ); + break; + } +} + +void ReadConstantPoolInfo( idSWFBitStream& bitstream , swfConstant_pool_info& target ) +{ + /*cpool_info{}*/ + uint32 int_count = bitstream.ReadEncodedU32() ; + target.integers.Alloc() = 0; + for( uint i = 1; i < int_count; i++ ) + { + target.integers.Alloc() = bitstream.ReadEncoded(); + } + + uint32 uint_count = bitstream.ReadEncodedU32(); + target.uIntegers.Alloc() = 0; + for( uint i = 1; i < uint_count; i++ ) + { + target.uIntegers.Alloc() = bitstream.ReadEncodedU32(); + } + + uint32 double_count = bitstream.ReadEncodedU32(); + target.doubles.Alloc() = 0.0; + for( uint i = 1; i < double_count; i++ ) + { + //WARNING IEEE-754 + target.doubles.Alloc() = *( double* )bitstream.ReadData( 8 ); + } + + uint32 string_count = bitstream.ReadEncodedU32(); + target.utf8Strings.Alloc().Append( "*" ); + for( uint i = 1; i < string_count; i++ ) + { + uint32 str_len = bitstream.ReadEncodedU32(); + target.utf8Strings.Alloc().Append( ( char* ) bitstream.ReadData( str_len ), str_len ); + } + + uint32 namespace_count = bitstream.ReadEncodedU32(); + target.namespaceNames.Alloc() = &target.utf8Strings[0]; + for( uint i = 1; i < namespace_count; i++ ) + { + target.namespaceKinds.Alloc() = ( swfConstantKind_t )bitstream.ReadU8(); + uint32 str_idx = bitstream.ReadEncodedU32(); + target.namespaceNames.Alloc() = &target.utf8Strings[( int )str_idx]; + } + + uint32 namespace_set_count = bitstream.ReadEncodedU32(); + target.namespaceSets.Alloc().Alloc() = target.namespaceNames[0]; + for( uint i = 1; i < namespace_set_count; i++ ) + { + uint32 count = bitstream.ReadEncodedU32(); + auto& newSet = target.namespaceSets.Alloc(); + for( uint j = 0; j < count; j++ ) + { + uint32 idx = bitstream.ReadEncodedU32(); + newSet.Alloc() = target.namespaceNames[( int )idx]; + } + } + + uint32 multiname_count = bitstream.ReadEncodedU32(); + auto& empty = target.multinameInfos.Alloc(); + empty.index = 0; + empty.nameIndex = 0; + empty.type = unused_0x00; + for( uint i = 1; i < multiname_count; i++ ) + { + auto& newMn = target.multinameInfos.Alloc(); + ReadMultiName( bitstream, newMn ); + } +} + +void SWF_AbcFile::ReadOptionInfo( idSWFBitStream& bitstream, swfOption_info& newOption ) +{ + newOption.option_count = bitstream.ReadEncodedU32(); + for( uint i = 0; i < newOption.option_count; i++ ) + { + auto& newItem = newOption.options.Alloc(); + newItem.val = bitstream.ReadEncodedU32(); + newItem.kind = constant_pool.namespaceKinds[bitstream.ReadEncodedU32()]; + } +} + +void SWF_AbcFile::ReadMetaDataInfo( idSWFBitStream& bitstream , swfMetadata_info& newMetadata ) +{ + newMetadata.name = &constant_pool.utf8Strings[bitstream.ReadEncodedU32()]; + uint32 item_count = bitstream.ReadEncodedU32(); + for( uint i = 0; i < item_count; i++ ) + { + auto& newItem = newMetadata.items.Alloc(); + newItem.key = &constant_pool.utf8Strings[bitstream.ReadEncodedU32()]; + newItem.value = &constant_pool.utf8Strings[bitstream.ReadEncodedU32()]; + } +} + +void SWF_AbcFile::ReadTraitData( idSWFBitStream& bitstream, swfTraits_info& newTraitsData ) +{ + swfTraits_info::Type kind = ( swfTraits_info::Type )( ( newTraitsData.kind << 4 ) >> 4 ); // snip upper half, lower 4 bits should remain. + uint8 attrib = ( newTraitsData.kind >> 4 ); // snip lower half, upper 4 bits should remain. + + switch( kind ) + { + case swfTraits_info::Trait_Slot: + case swfTraits_info::Trait_Const: + { + newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_slot ) , TAG_SWF ); + swfTrait_slot& slot = *( ( swfTrait_slot* )( newTraitsData.data ) ); + slot.slot_id = bitstream.ReadEncodedU32(); + slot.type_name = &constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + slot.vindex = bitstream.ReadEncodedU32(); + if( slot.vindex != 0 ) + { + slot.vkind = bitstream.ReadU8(); + } + } + break; + case swfTraits_info::Trait_Method: + case swfTraits_info::Trait_Getter: + case swfTraits_info::Trait_Setter: + { + newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_method ), TAG_SWF ); + swfTrait_method& method = *( swfTrait_method* )( newTraitsData.data ); + method.disp_id = bitstream.ReadEncodedU32(); + method.method = &methods[bitstream.ReadEncodedU32()]; + } + break; + case swfTraits_info::Trait_Class: + { + newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_class ), TAG_SWF ); + swfTrait_class& tclass = *( swfTrait_class* )( newTraitsData.data ); + tclass.slot_id = bitstream.ReadEncodedU32(); + tclass.classi = &classes[bitstream.ReadEncodedU32()]; + } + break; + case swfTraits_info::Trait_Function: + { + newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_function ), TAG_SWF ); + swfTrait_function& func = *( swfTrait_function* )( newTraitsData.data ); + func.slot_id = bitstream.ReadEncodedU32(); + func.func = &methods[bitstream.ReadEncodedU32()]; + } + break; + + default: + common->FatalError( "Unknown trait data" ); + break; + } +} + +//struct idSWFScriptObject::swfNamedVar_t ; + +template<> +idSWFScriptObject::swfNamedVar_t* SWF_AbcFile::GetTrait( const swfTraits_info& trait, idSWFScriptObject* globals ) +{ + swfTraits_info::Type kind = ( swfTraits_info::Type )( ( trait.kind << 4 ) >> 4 ); // snip upper half, lower 4 bits should remain. + uint8 attrib = ( trait.kind >> 4 ); // snip lower half, upper 4 bits should remain. + + //idSWFScriptObject* newObj = new idSWFScriptObject(); + idSWFScriptObject::swfNamedVar_t* newVar = new idSWFScriptObject::swfNamedVar_t(); + newVar->value.traitsInfo = &trait; + switch( kind ) + { + case swfTraits_info::Trait_Slot: + case swfTraits_info::Trait_Const: + { + //member variable. + swfTrait_slot& slot = *( ( swfTrait_slot* )( trait.data ) ); + newVar->name = constant_pool.utf8Strings[trait.name->nameIndex]; + //if ( slot.slot_id ) + //{ + // newVar = newObj->GetVariable(slot.slot_id,true); + // newVar->name = constant_pool.utf8Strings[trait.name->nameIndex]; + //}else + // newVar = newObj->GetVariable(constant_pool.utf8Strings[trait.name->nameIndex].c_str(),true); + + if( slot.vindex != 0 ) + switch( slot.vkind ) + { + case Utf8: + newVar->value.SetString( constant_pool.utf8Strings[slot.vindex] ); + break; + case Int: + newVar->value.SetInteger( constant_pool.integers[slot.vindex] ); + break; + case UInt: + newVar->value.SetInteger( ( int32 )( constant_pool.uIntegers[slot.vindex] ) ); + break; + case Double: // crap. + newVar->value.SetFloat( ( float )( constant_pool.doubles[slot.vindex] ) ); + break; + case Qname: + { + + } + case Namespace: + newVar->value.SetString( *constant_pool.namespaceNames[slot.vindex] ); + break; + case Multiname: + break; + case False: + newVar->value.SetBool( false ); + break; + case True: + newVar->value.SetBool( true ); + break; + case Null: + newVar->value.SetUndefined(); + break; + case QnameA: + { + idStr& typeName = constant_pool.utf8Strings[slot.vindex]; + if( globals->HasProperty( typeName.c_str() ) ) + { + newVar->value.SetObject( globals->GetObject( typeName ) ); + } + else + { + newVar->value.SetUndefined(); + } + break; + } + break; + case MultinameA: + break; + case RTQname: + break; + case RTQnameA: + break; + case RTQnameL: + break; + case RTQnameLA: + break; + case NamespaceSet: + break; + case PackageNamespace: + break; + case PackageInternalNs: + break; + case ProtectedNamespace: + break; + case ExplicitNamespace: + break; + case StaticProtectedNs: + break; + case MultinameL: + break; + case MultinameLA: + break; + case TypeName: + break; + default: + break; + } + else + { + idStr& typeName = constant_pool.utf8Strings[slot.type_name->nameIndex]; + if( globals->HasProperty( typeName.c_str() ) ) + { + auto* newobj = idSWFScriptObject::Alloc(); + newobj->SetPrototype( globals->GetObject( typeName.c_str() ) ); + newVar->value.SetObject( newobj ); + } + else + { + newVar->value.SetUndefined(); + } + break; + } + + return newVar; + } + break; + case swfTraits_info::Trait_Method: + case swfTraits_info::Trait_Getter: + case swfTraits_info::Trait_Setter: + { + newVar->name = constant_pool.utf8Strings[trait.name->nameIndex]; + swfTrait_method& method = *( ( swfTrait_method* )( trait.data ) ); + + idStrPtr name = method.method->name; + idStr owner; + //string method owner + //common->FatalError("This is wrong. now dont keep using the debug info but start resolving it properly!!!!"); + //check frame scripts! + int slashPos = name->Find( "/" ); + if( slashPos != -1 ) + { + owner = idStr( name->c_str(), 0, slashPos ); + } + + slashPos = owner.Find( ":" ); + if( slashPos != -1 ) + { + owner = idStr( owner.c_str(), slashPos + 1, owner.Length() ); + } + + if( globals->HasProperty( owner.c_str() ) ) + { + idSWFScriptFunction_Script* func = idSWFScriptFunction_Script::Alloc(); + func->SetAbcFile( this ); + func->SetData( method.method ); + newVar->value = idSWFScriptVar( func ) ; + } + else + { + newVar->value.SetUndefined(); + } + break; + + } + break; + case swfTraits_info::Trait_Class: + { + int a = 0; + //newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_class ) ); + //swfTrait_class &tclass = *( swfTrait_class * ) ( newTraitsData.data ); + //tclass.slot_id = bitstream.ReadEncodedU32(); + //tclass.classi = &classes[bitstream.ReadEncodedU32()]; + } + break; + case swfTraits_info::Trait_Function: + { + int a = 0; + //newTraitsData.data = Mem_ClearedAlloc( sizeof( swfTrait_function ) ); + //swfTrait_function &func = *( swfTrait_function * ) ( newTraitsData.data ); + //func.slot_id = bitstream.ReadEncodedU32(); + //func.func = &methods[bitstream.ReadEncodedU32()]; + } + break; + + default: + common->FatalError( "Unknown trait data" ); + break; + } + + return newVar; +} + +void SWF_AbcFile::ReadTraitsInfo( idSWFBitStream& bitstream, swfTraits_info& newTraitsData ) +{ + newTraitsData.name = &constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + //The kind field contains two four-bit fields. The lower four bits determine the kind of this trait. The + // upper four bits comprise a bit vector providing attributes of the trait. See the following tables and + // sections for full descriptions. + + newTraitsData.kind = bitstream.ReadU8(); + ReadTraitData( bitstream, newTraitsData ); + uint8 attrib = ( newTraitsData.kind >> 4 ); // snip lower half, upper 4 bits should remain. + if( ( attrib & swfTraits_info::Attrib::Metadata ) != 0 ) + { + uint32 meta_count = bitstream.ReadEncodedU32(); + for( uint i = 0 ; i < meta_count; i++ ) + { + newTraitsData.metadatas.Alloc() = &metadatas[bitstream.ReadEncodedU32()]; + //fix this. + } + } +} + +void SWF_AbcFile::ReadClassInfo( idSWFBitStream& bitstream, swfClass_info& newClassData ) +{ + newClassData.cinit = &methods[bitstream.ReadEncodedU32()]; + uint32 trait_count = bitstream.ReadEncodedU32(); + newClassData.traits.AssureSize( trait_count ); + for( uint i = 0; i < trait_count; i++ ) + { + ReadTraitsInfo( bitstream, newClassData.traits[i] ); + } + +} + +//The last entry in that array is the entry point for the ABC file; that is, the last entry�s +//initialization method contains the first bytecode that�s run when the ABC file is executed. +void SWF_AbcFile::ReadScriptInfo( idSWFBitStream& bitstream, swfScript_info& newScriptData ) +{ + uint32 init = bitstream.ReadEncodedU32(); + uint32 trait_count = bitstream.ReadEncodedU32(); + newScriptData.init = &methods[init]; + trace( "%s \n", newScriptData.init->name->c_str() ); + newScriptData.traits.AssureSize( trait_count ); + for( uint i = 0; i < trait_count; i++ ) + { + ReadTraitsInfo( bitstream, newScriptData.traits[i] ); + } +} + +void SWF_AbcFile::ReadMethodBodyInfo( idSWFBitStream& bitstream, swfMethod_body_info& newMethodBody ) +{ + newMethodBody.method = &methods[bitstream.ReadEncodedU32()]; + assert( newMethodBody.method->body == nullptr ); + newMethodBody.method->body = &newMethodBody; + + newMethodBody.max_stack = bitstream.ReadEncodedU32(); + newMethodBody.localCount = bitstream.ReadEncodedU32(); + newMethodBody.initScopeDepth = bitstream.ReadEncodedU32(); + newMethodBody.maxScopeDepth = bitstream.ReadEncodedU32(); + newMethodBody.codeLength = bitstream.ReadEncodedU32(); + newMethodBody.code.Load( bitstream.ReadData( newMethodBody.codeLength ), newMethodBody.codeLength, true ); // ( byte * ) Mem_ClearedAlloc( sizeof( byte ) * newMethodBody.codeLength ); + extern void swf_PrintStream( SWF_AbcFile * file , idSWFBitStream & bitstream ); + if( swf_abc_verbose.GetInteger() == 2 ) + { + common->Printf( "============================\n" ); + common->Printf( "Method %s 's bytecode \n", newMethodBody.method->name->c_str() ); + common->Printf( "============================\n" ); + swf_PrintStream( this, newMethodBody.code ); + } + //memcpy(newMethodBody.code,bitstream.ReadData(newMethodBody.codeLength),newMethodBody.codeLength); + uint32 exception_count = bitstream.ReadEncodedU32(); + for( uint i = 0; i < exception_count; i++ ) + { + auto& newExceptionInfo = newMethodBody.exceptions.Alloc(); + ReadExceptionInfo( bitstream, newExceptionInfo ); + } + uint32 trait_count = bitstream.ReadEncodedU32(); + for( uint i = 0; i < trait_count; i++ ) + { + auto& newTrait = newMethodBody.traits.Alloc(); + ReadTraitsInfo( bitstream, newTrait ); + } +} + +void SWF_AbcFile::ReadExceptionInfo( idSWFBitStream& bitstream, swfException_info& newException ) +{ + newException.from = bitstream.ReadEncodedU32(); + newException.to = bitstream.ReadEncodedU32(); + newException.target = bitstream.ReadEncodedU32(); + newException.exc_type = &constant_pool.utf8Strings[bitstream.ReadEncodedU32()]; + newException.var_name = &constant_pool.utf8Strings[bitstream.ReadEncodedU32()]; +} + +void SWF_AbcFile::ReadInstanceInfo( idSWFBitStream& bitstream, swfInstance_info& newInstancedata ) +{ + newInstancedata.name = &constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + newInstancedata.super_name = &constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + + if( swf_abc_verbose.GetBool() ) + { + traceMN( "newInstancedata.name", newInstancedata.name, constant_pool ); + traceMN( "newInstancedata.super_name", newInstancedata.super_name, constant_pool ); + } + + newInstancedata.flags = ( swfInstanceFlags_t )bitstream.ReadU8(); + if( ( newInstancedata.flags & swfInstanceFlags_t::ClassProtectedNs ) != 0 ) + { + newInstancedata.protectedNs = bitstream.ReadEncodedU32(); + } + uint32 interface_count = bitstream.ReadEncodedU32(); + + for( uint i = 0; i < interface_count; i++ ) + { + newInstancedata.interfaces.Alloc() = &constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + } + + newInstancedata.iinit = &methods[bitstream.ReadEncodedU32()]; + + uint32 trait_count = bitstream.ReadEncodedU32(); + + for( uint i = 0; i < trait_count; i++ ) + { + auto& newTrait = newInstancedata.traits.Alloc(); + ReadTraitsInfo( bitstream, newTrait ); + } + +} + +void SWF_AbcFile::ReadMethodInfo( idSWFBitStream& bitstream , swfMethod_info& newMethod ) +{ + uint32 idx = 0; + newMethod.paramCount = bitstream.ReadEncodedU32(); + idx = bitstream.ReadEncodedU32(); + newMethod.returnType = &constant_pool.multinameInfos[idx]; + + for( uint i = 0; i < newMethod.paramCount; i++ ) + { + idx = bitstream.ReadEncodedU32(); + newMethod.paramTypes.Alloc() = &constant_pool.multinameInfos[idx]; + } + idx = bitstream.ReadEncodedU32(); + newMethod.name = &constant_pool.utf8Strings[idx]; + newMethod.flags = bitstream.ReadU8(); + newMethod.options.option_count = 0; + if( ( newMethod.flags & swfMethod_info::HAS_OPTIONAL ) != 0 ) + { + ReadOptionInfo( bitstream, newMethod.options ); + } + + trace( "newMethod.name %s \n", newMethod.name->c_str() ); + + if( ( newMethod.flags & swfMethod_info::HAS_PARAM_NAMES ) != 0 ) + { + trace( "newMethod.params %i \n", ( int )newMethod.paramCount ); + + for( uint i = 0; i < newMethod.paramCount; i++ ) + { + idx = bitstream.ReadEncodedU32(); + newMethod.paramNames.Alloc() = &constant_pool.utf8Strings[idx]; + + trace( "newMethod.param %s \n", constant_pool.utf8Strings[idx].c_str() ); + } + } + +} + + +void SWF_AbcFile::WriteBinary( idFileLocal& file ) +{ + file->WriteBig( ( int )AbcTagData.Length() ); + file->Write( AbcTagData.Ptr(), AbcTagData.Length() ); +} + +void SWF_AbcFile::LoadBinary( idFile* file ) +{ + uint32 len; + file->ReadBig( len ); + AbcTagData.LoadFromFile( file, len ); +} + +//Remove Accessibility +void idSWF::DoABC( idSWFBitStream& bitstream ) +{ + SWF_AbcFile& newAbcFile = abcFile; + int strmSize = bitstream.Length() + 6; // codeLength(uin16) + recordLength(uin32) + + if( !newAbcFile.AbcTagData.Length() ) + { + newAbcFile.AbcTagData.Load( bitstream.Ptr(), bitstream.Length(), true ); + } + + uint32 flags = bitstream.ReadU32(); + idStr name = bitstream.ReadString(); + int dataSize = bitstream.Length() - name.Length(); + common->Printf( "DoABC %s flags %i tagsize %i bytecode size %i \n", name.c_str(), flags, strmSize, dataSize ); + + bitstream.ReadLittle( newAbcFile.minor_version ); + bitstream.ReadLittle( newAbcFile.major_version ); + + ReadConstantPoolInfo( bitstream, newAbcFile.constant_pool ); + SWF_AbcFile::traceConstantPool( newAbcFile.constant_pool ); + + uint32 method_count = bitstream.ReadEncodedU32() ; + newAbcFile.methods.AssureSize( method_count ); + trace( "method_count %i \n", method_count ); + for( uint i = 0; i < method_count; i++ ) + { + auto& newMethod = newAbcFile.methods[i]; + newAbcFile.ReadMethodInfo( bitstream, newMethod ); + } + + uint32 meta_count = bitstream.ReadEncodedU32(); + newAbcFile.metadatas.AssureSize( meta_count ); + trace( "meta_count %i \n", meta_count ); + for( uint i = 0; i < meta_count; i++ ) + { + auto& newMeta = newAbcFile.metadatas[i]; + newAbcFile.ReadMetaDataInfo( bitstream, newMeta ); + } + + newAbcFile.class_count = bitstream.ReadEncodedU32(); + newAbcFile.instances.AssureSize( newAbcFile.class_count ); + trace( "class_count %i (Instance) \n", newAbcFile.class_count ); + for( uint i = 0; i < newAbcFile.class_count ; i++ ) + { + auto& newInstance = newAbcFile.instances[i]; + newAbcFile.ReadInstanceInfo( bitstream, newInstance ); + } + + trace( "class_count %i (Class) \n", newAbcFile.class_count ); + newAbcFile.classes.AssureSize( newAbcFile.class_count ); + for( uint i = 0; i < newAbcFile.class_count ; i++ ) + { + auto& newClass = newAbcFile.classes[i]; + newAbcFile.ReadClassInfo( bitstream, newClass ); + } + + uint32 script_count = bitstream.ReadEncodedU32(); + newAbcFile.scripts.AssureSize( script_count ); + trace( "script_count %i \n", script_count ); + for( uint i = 0; i < script_count; i++ ) + { + auto& newScript = newAbcFile.scripts[i]; + newAbcFile.ReadScriptInfo( bitstream, newScript ); + } + + uint32 methBody_count = bitstream.ReadEncodedU32(); + newAbcFile.method_bodies.AssureSize( methBody_count ); + trace( "methBody_count %i \n", methBody_count ); + for( uint i = 0; i < methBody_count; i++ ) + { + auto& newMethBody = newAbcFile.method_bodies[i]; + newAbcFile.ReadMethodBodyInfo( bitstream, newMethBody ); + } +} + +void idSWF::SymbolClass( idSWFBitStream& bitstream ) +{ + + //Header RECORDHEADER Tag type = 76 + //NumSymbols UI16 Number of symbols that will be associated by this tag. + //Tag1 U16 The 16 - bit character tag ID for the symbol to associate + //Name1 STRING The fully - qualified name of the ActionScript 3.0 class with which to associate this symbol.The class must have already been declared by a DoABC tag. + //... ... ... + //TagN U16 Tag ID for symbol N + //NameN STRING Fully - qualified class name for symbol N + uint16 numSymbols = bitstream.ReadU16(); + for( uint i = 0 ; i < numSymbols; i++ ) + { + auto& newSymbol = symbolClasses.symbols.Alloc(); + newSymbol.tag = bitstream.ReadU16(); + newSymbol.name = bitstream.ReadString(); + trace( "SymbolClass ^5%i ^7tag ^5%i ^2%s \n", i, newSymbol.tag, newSymbol.name.c_str() ); + } +} + + diff --git a/neo/swf/SWF_Abc.h b/neo/swf/SWF_Abc.h new file mode 100644 index 0000000000..e12e2a8cd6 --- /dev/null +++ b/neo/swf/SWF_Abc.h @@ -0,0 +1,272 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2023 Harrie van Ginneken + +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 . + +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 once + +#include "SWF_Enums.h" + +struct swfMultiname +{ + swfConstantKind_t type; + uint32 nameIndex; + uint32 index; + uint32 nameIndexT; //second Multiname for types + uint32 indexT; // +}; + +struct swfConstant_pool_info +{ + idList integers; //u30 int_count s32 integer[int_count] + idList uIntegers; //u30 uint_count u32 uinteger[uint_count] + idList doubles; //u30 double_count d64 double[double_count] + idStrList utf8Strings; //u30 string_count string_info string[string_count] (char is 16bit code point) + idList namespaceKinds; //u30 namespace_count namespace_info namespace[namespace_count] (namespaceinfo_t.kind) + idStrPtrList namespaceNames; //u30 namespace_count namespace_info namespace[namespace_count] (namespaceinfo_t.name) + idList namespaceSets; //u30 ns_set_count ns_set_info ns_set[ns_set_count] + idList multinameInfos; //u30 multiname_count multiname_info multiname[multiname_count] +}; + +struct swfMetadata_info +{ + struct item //item_info + { + idStrPtr key; //u30 key + idStrPtr value; //u30 value + }; + idStrPtr name; //u30 name + idList items; //u30 item_count item_info items[item_count] +}; + +struct swfOption_info +{ + struct item //option_detail + { + uint32 val; //u30 val + swfConstantKind_t kind; //u8 kind + }; + uint32 option_count; //u30 option_count + idList options; //option_detail option[option_count] +}; + +struct swfMethod_body_info; +struct swfMethod_info +{ + //zero before use. + enum Flags + { + NEED_ARGUMENTS = 0x01, // Suggests to the run - time that an �arguments� object( as specified by the ActionScript 3.0 Language Reference ) be created.Must not be used together with NEED_REST.See Chapter 3. + NEED_ACTIVATION = 0x02, // Must be set if this method uses the newactivation opcode. + NEED_REST = 0x04, // This flag creates an ActionScript 3.0 rest arguments array.Must not be used with NEED_ARGUMENTS.See Chapter 3. + HAS_OPTIONAL = 0x08, // Must be set if this method has optional parameters andthe options field is present in this method_info structure. + IGNORE_REST = 0x10, + NATIVE = 0x20, + SET_DXNS = 0x40, // Must be set if this method uses the dxns or dxnslate opcodes. + HAS_PARAM_NAMES = 0x80, // Must be set when the param_names field is present in this method_info structure. + }; + + uint32 paramCount; //u30 param_count + swfMultiname* returnType; //u30 return_type + idList paramTypes; //u30 param_count //u30 param_type[param_count] + idStrPtr name; //u30 name + uint8 flags; //u8 flags + swfOption_info options; //option_info options + idStrPtrList paramNames; // ( param_info ) param_names u30 param_name[param_count] + + swfMethod_body_info* body = nullptr; +}; + +struct swfTraits_info +{ + enum Attrib // upper 4 bits of kind + { + Final = 0x1, // Is used with Trait_Method, Trait_Getter andTrait_Setter.It marks a method that cannot be overridden by a sub - class + Override = 0x2, // Is used with Trait_Method, Trait_Getter andTrait_Setter.It marks a method that has been overridden in this class + Metadata = 0x4, // Is used to signal that the fields metadata_count and metadata follow the data field in the traits_info entry + }; + enum Type + { + Trait_Slot = 0, + Trait_Method = 1, + Trait_Getter = 2, + Trait_Setter = 3, + Trait_Class = 4, + Trait_Function = 5, + Trait_Const = 6, + Trait_Count = Trait_Const + 1, + Trait_Mask = 15 + }; + swfMultiname* name; //u30 name + uint8 kind; //u8 kind + void* data; //u8 data[] + idList metadatas; //u30 metadata_count u30 metadata[metadata_count] +}; + +struct swfInstance_info +{ + swfMultiname* name; //u30 name + swfMultiname* super_name; //u30 super_name + swfInstanceFlags_t flags; //u8 flags + uint32 protectedNs; //u30 protectedNs + idList interfaces; //u30 intrf_count u30 interface[intrf_count] + swfMethod_info* iinit; //u30 iinit + idList traits; //u30 trait_count traits_info trait[trait_count] +}; + +struct swfTrait_slot +{ + uint32 slot_id; //u30 slot_id + swfMultiname* type_name; //u30 type_name + uint32 vindex; //u30 vindex + uint8 vkind; //u8 vkind +}; + +struct swfClass_info +{ + swfMethod_info* cinit; //u30 cinit + idList traits; //u30 trait_count traits_info traits[trait_count] +}; + +struct swfTrait_class +{ + uint32 slot_id; //u30 slot_id + swfClass_info* classi; //u30 classi +}; + +struct swfTrait_function +{ + uint32 slot_id; //u30 slot_id + swfMethod_info* func; //u30 function +}; + +struct swfTrait_method +{ + uint32 disp_id; //u30 disp_id + swfMethod_info* method; //u30 method +}; + +struct swfScript_info +{ + swfMethod_info* init; //u30 init + idList traits; //u30 trait_count traits_info traits[trait_count] +}; + +struct swfException_info +{ + uint32 from; //u30 from + uint32 to; //u30 to + uint32 target; //u30 target + idStrPtr exc_type; //u30 exc_type + idStrPtr var_name; //u30 var_name +}; + +struct swfMethod_body_info +{ + swfMethod_info* method; //u30 method + uint32 max_stack; //u30 max_stack + uint32 localCount; //u30 local_count + uint32 initScopeDepth; //u30 init_scope_depth + uint32 maxScopeDepth; //u30 max_scope_depth + uint32 codeLength; //u30 code_length + idSWFBitStream code; //u8 code[code_length] + idList exceptions; //u30 exception_count exception_info exception[exception_count] + idList traits; //u30 trait_count traits_info traits[trait_count] +}; + +class idSWFScriptObject; +struct SWF_AbcFile +{ + template + T* GetTrait( const swfTraits_info& trait, idSWFScriptObject* globals = nullptr ); + + void ReadMethodInfo( idSWFBitStream& bitstream, swfMethod_info& newMethod ); + void ReadOptionInfo( idSWFBitStream& bitstream, swfOption_info& newOption ); + void ReadMetaDataInfo( idSWFBitStream& bitstream, swfMetadata_info& newMetadata ); + void ReadInstanceInfo( idSWFBitStream& bitstream, swfInstance_info& newInstancedata ); + void ReadTraitData( idSWFBitStream& bitstream, swfTraits_info& newTraitsData ); + void ReadTraitsInfo( idSWFBitStream& bitstream, swfTraits_info& newTraitsData ); + void ReadClassInfo( idSWFBitStream& bitstream, swfClass_info& newClassData ); + void ReadScriptInfo( idSWFBitStream& bitstream, swfScript_info& newScriptData ); + void ReadMethodBodyInfo( idSWFBitStream& bitstream, swfMethod_body_info& newMethodBody ); + void ReadExceptionInfo( idSWFBitStream& bitstream, swfException_info& newException ); + + static idStr asString( swfConstantKind_t kind, swfConstant_pool_info& constant_pool ); + static idStr asString( swfMultiname* mn, swfConstant_pool_info& constant_pool, bool prefix = true ); + static void traceMN( const char* name, swfMultiname* mn, swfConstant_pool_info& constant_pool ); + static void traceConstantPool( swfConstant_pool_info& constant_pool ); + + uint16 minor_version; + uint16 major_version; + swfConstant_pool_info constant_pool; + idList methods; //u30 method_count method_info method[method_count] + idList metadatas; //u30 metadata_count metadata_info metadata[metadata_count] + uint32 class_count; //u30 class_count + idList instances; //instance_info instance[class_count] + idList classes; //class_info class[class_count] + idList scripts; //u30 script_count script_info script[script_count] + idList method_bodies; //u30 method_body_count method_body_info method_body[method_body_count] + + //Writing the whole bytestream as whole for now, but : + // debug info plus unused tags and bytecode should be removed, constant pools need to stay the same + // accessibility can also be removed. + idSWFBitStream AbcTagData; + void WriteBinary( idFileLocal& file ); + void LoadBinary( idFile* file ); +}; + +struct SWF_SymbolClass +{ + struct Item + { + uint16 tag; + idStr name; + }; + idList symbols; +}; + +enum SWFAbcOpcode +{ +#define ABC_OP(operandCount, canThrow, stack, internalOnly, nameToken) OP_##nameToken, +#define ABC_UNUSED_OP(operandCount, canThrow, stack, internalOnly, nameToken) ABC_OP(operandCount, canThrow, stack, internalOnly, nameToken) +#include "opcodes.tbl" +#undef ABC_OP +#undef ABC_UNUSED_OP + + //----- + OP_end_of_op_codes +}; + +struct AbcOpcodeInfo +{ + int8_t operandCount; // uses -1 for "invalid", we can avoid that if necessary + int8_t canThrow; // always 0 or 1 + int8_t stack; // stack movement not taking into account run-time names or function arguments + uint16_t wordCode; // a map used during translation + const char* name; // instruction name or OP_0xNN for undefined instructions #IFDEF DEBUGGER +}; + +extern const AbcOpcodeInfo opcodeInfo[]; +extern const unsigned char kindToPushOp[]; diff --git a/neo/swf/SWF_Bitstream.cpp b/neo/swf/SWF_Bitstream.cpp index 68d169850f..73efb0f0ab 100644 --- a/neo/swf/SWF_Bitstream.cpp +++ b/neo/swf/SWF_Bitstream.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -61,6 +62,19 @@ idSWFBitStream::idSWFBitStream() Free(); } +void idSWFBitStream::LoadFromFile( idFile* file, uint32 len ) +{ + Free(); + free = true; + startp = ( const byte* )Mem_Alloc( len, TAG_SWF ); + file->Read( ( byte* )startp, len ); + + endp = startp + len; + readp = startp; + + ResetBits(); +} + /* ======================== idSWFBitStream::operator= diff --git a/neo/swf/SWF_Bitstream.h b/neo/swf/SWF_Bitstream.h index 6951682b48..e4a3333c0f 100644 --- a/neo/swf/SWF_Bitstream.h +++ b/neo/swf/SWF_Bitstream.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -45,6 +46,7 @@ class idSWFBitStream idSWFBitStream& operator=( idSWFBitStream& other ); idSWFBitStream& operator=( idSWFBitStream&& other ); + void LoadFromFile( idFile* file, uint32 len ); void Load( const byte* data, uint32 len, bool copy ); void Free(); const byte* Ptr() @@ -85,6 +87,23 @@ class idSWFBitStream uint32 ReadU32(); int16 ReadS16(); int32 ReadS32(); + int32 ReadS24(); + template< typename T > + T ReadEncoded() + { + T result = 0; + for( int i = 0; i < 5; i++ ) + { + byte b = ReadU8(); + result |= ( b & 0x7F ) << ( 7 * i ); + if( ( b & 0x80 ) == 0 ) + { + return result; + } + } + return result; + } + uint32 ReadEncodedU32(); float ReadFixed8(); float ReadFixed16(); @@ -169,6 +188,19 @@ ID_INLINE int16 idSWFBitStream::ReadS16() readp += 2; return ( readp[-2] | ( readp[-1] << 8 ) ); } + +ID_INLINE int idSWFBitStream::ReadS24() +{ + ResetBits(); + readp += 3; + int32 i = ( readp[-3] | ( readp[-2] << 8 ) | ( readp[-1] << 16 ) ); + if( i & ( 0x80 << 16 ) ) + { + i |= 0xff << ( 24 ); + } + return ( int& )i; +} + ID_INLINE int32 idSWFBitStream::ReadS32() { ResetBits(); @@ -212,4 +244,7 @@ ID_INLINE double idSWFBitStream::ReadDouble() return d; } + + + #endif // !__SWF_BITSTREAM_H__ diff --git a/neo/swf/SWF_Dictionary.cpp b/neo/swf/SWF_Dictionary.cpp index 215219727f..d99394ba78 100644 --- a/neo/swf/SWF_Dictionary.cpp +++ b/neo/swf/SWF_Dictionary.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -43,7 +44,10 @@ idSWFDictionaryEntry::idSWFDictionaryEntry() : edittext( NULL ), imageSize( 0, 0 ), imageAtlasOffset( 0, 0 ), - channelScale( 1.0f, 1.0f, 1.0f, 1.0f ) + channelScale( 1.0f, 1.0f, 1.0f, 1.0f ), + scriptClass(), + resolved( false ), + name( NULL ) { } @@ -78,6 +82,9 @@ idSWFDictionaryEntry& idSWFDictionaryEntry::operator=( idSWFDictionaryEntry& oth edittext = other.edittext; imageSize = other.imageSize; imageAtlasOffset = other.imageAtlasOffset; + scriptClass = other.scriptClass; + resolved = other.resolved; + name = other.name; other.type = SWF_DICT_NULL; other.material = NULL; other.shape = NULL; @@ -85,6 +92,9 @@ idSWFDictionaryEntry& idSWFDictionaryEntry::operator=( idSWFDictionaryEntry& oth other.font = NULL; other.text = NULL; other.edittext = NULL; + other.resolved = false; + other.name = NULL; + other.scriptClass = idSWFScriptVar(); return *this; } diff --git a/neo/swf/SWF_Enums.h b/neo/swf/SWF_Enums.h index 3247bcfe35..3b964da5b0 100644 --- a/neo/swf/SWF_Enums.h +++ b/neo/swf/SWF_Enums.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -28,6 +29,52 @@ If you have questions concerning this license or the applicable additional terms #ifndef __SWF_ENUMS_H__ #define __SWF_ENUMS_H__ + +enum swfInstanceFlags_t +{ + ClassSealed = 0x01, //The class is sealed: properties can not be dynamically added to instances of the class. + ClassFinal = 0x02, //The class is final : it cannot be a base class for any other class. + ClassInterface = 0x04, //The class is an interface. + ClassProtectedNs = 0x08, //The class uses its protected namespace andthe protectedNs +}; + +enum swfConstantKind_t +{ + unused_0x00 = 0x00, + Utf8 = 0x01, +#ifdef SWF_FLOAT + Float = 0x02, +#endif + Int = 0x03, + UInt = 0x04, + PrivateNs = 0x05, // non-shared namespace + Double = 0x06, + Qname = 0x07, // o.ns::name, ct ns, ct name + Namespace = 0x08, + Multiname = 0x09, // o.name, ct nsset, ct name + False = 0x0A, + True = 0x0B, + Null = 0x0C, + QnameA = 0x0D, // o.@ns::name, ct ns, ct attr-name + MultinameA = 0x0E, // o.@name, ct attr-name + RTQname = 0x0F, // o.ns::name, rt ns, ct name + RTQnameA = 0x10, // o.@ns::name, rt ns, ct attr-name + RTQnameL = 0x11, // o.ns::[name], rt ns, rt name + RTQnameLA = 0x12, // o.@ns::[name], rt ns, rt attr-name + NamespaceSet = 0x15, + PackageNamespace = 0x16, + PackageInternalNs = 0x17, + ProtectedNamespace = 0x18, + ExplicitNamespace = 0x19, + StaticProtectedNs = 0x1A, + MultinameL = 0x1B, + MultinameLA = 0x1C, + TypeName = 0x1D, +#ifdef SWF_FLOAT + Float4 = 0x1E, +#endif +}; + enum swfDictType_t { SWF_DICT_NULL, diff --git a/neo/swf/SWF_EventDispatcher.cpp b/neo/swf/SWF_EventDispatcher.cpp new file mode 100644 index 0000000000..e90cd95b89 --- /dev/null +++ b/neo/swf/SWF_EventDispatcher.cpp @@ -0,0 +1,182 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2023 Harrie van Ginneken + +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 . + +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. + +=========================================================================== +*/ +#include "precompiled.h" +#pragma hdrstop + +#include "SWF_EventDispatcher.h" + +/* +======================== +idSWFScriptObject_EventDispatcherPrototype +======================== +*/ +#define SWF_EVENTDISPATCHER_FUNCTION_DEFINE( x ) idSWFScriptVar idSWFScriptObject_EventDispatcherPrototype::idSWFScriptFunction_##x::Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ) +#define SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_GET( x ) idSWFScriptVar idSWFScriptObject_EventDispatcherPrototype::idSWFScriptNativeVar_##x::Get( class idSWFScriptObject * object ) +#define SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_SET( x ) void idSWFScriptObject_EventDispatcherPrototype::idSWFScriptNativeVar_##x::Set( class idSWFScriptObject * object, const idSWFScriptVar & value ) + +#define SWF_EVENTDISPATCHER_PTHIS_FUNC( x ) idSWFScriptObject * pThis = thisObject ? thisObject : NULL; if ( !verify( pThis != NULL ) ) { idLib::Warning( "SWF: tried to call " x " on NULL object" ); return idSWFScriptVar(); } +#define SWF_EVENTDISPATCHER_PTHIS_GET( x ) idSWFScriptObject * pThis = object ? object : NULL; if ( pThis == NULL ) { return idSWFScriptVar(); } +#define SWF_EVENTDISPATCHER_PTHIS_SET( x ) idSWFScriptObject * pThis = object ? object : NULL; if ( pThis == NULL ) { return; } + +#define SWF_EVENTDISPATCHER_FUNCTION_SET( x ) scriptFunction_##x.AddRef(); Set( #x, &scriptFunction_##x ); +#define SWF_EVENTDISPATCHER_NATIVE_VAR_SET( x ) SetNative( #x, &swfScriptVar_##x ); + + +idSWFScriptObject_EventDispatcherPrototype::idSWFScriptObject_EventDispatcherPrototype() +{ + SWF_EVENTDISPATCHER_NATIVE_VAR_SET( MouseEvent ); + SWF_EVENTDISPATCHER_NATIVE_VAR_SET( Event ); + SWF_EVENTDISPATCHER_FUNCTION_SET( addEventListener ); +} + +SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_SET( MouseEvent ) {} +SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_SET( Event ) {} + +SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_GET( MouseEvent ) +{ + static idSWFScriptObject* mouseEventObj = nullptr; + if( mouseEventObj == nullptr ) + { + mouseEventObj = idSWFScriptObject::Alloc(); + idSWFScriptObject* eventParms = idSWFScriptObject::Alloc(); + + eventParms->Set( "type", "MouseEvent" ); + mouseEventObj->Set( "[MouseEvent]", eventParms ); + //constants + mouseEventObj->Set( "CLICK", "click" ); + mouseEventObj->Set( "CONTEXT_MENU", "contextMenu" ); + mouseEventObj->Set( "DOUBLE_CLICK", "doubleClick" ); + mouseEventObj->Set( "MIDDLE_CLICK", "middleClick" ); + mouseEventObj->Set( "MIDDLE_MOUSE_DOWN", "middleMouseDown" ); + mouseEventObj->Set( "MIDDLE_MOUSE_UP", "middleMouseUp" ); + mouseEventObj->Set( "MOUSE_DOWN", "mouseDown" ); + mouseEventObj->Set( "MOUSE_MOVE", "mouseMove" ); + mouseEventObj->Set( "MOUSE_OUT", "mouseOut" ); + mouseEventObj->Set( "MOUSE_OVER", "mouseOver" ); + mouseEventObj->Set( "MOUSE_UP", "mouseUp" ); + mouseEventObj->Set( "MOUSE_WHEEL ", "mouseWheel" ); + mouseEventObj->Set( "RELEASE_OUTSIDE", "releaseOutside" ); + mouseEventObj->Set( "RIGHT_CLICK", "rightClick" ); + mouseEventObj->Set( "RIGHT_MOUSE_DOWN", "rightMouseDown" ); + mouseEventObj->Set( "RIGHT_MOUSE_UP", "rightMouseUp" ); + mouseEventObj->Set( "ROLL_OUT", "rollOut" ); + mouseEventObj->Set( "ROLL_OVER", "rollOver" ); + } + return mouseEventObj; +} + +SWF_EVENTDISPATCHER_NATIVE_VAR_DEFINE_GET( Event ) +{ + static idSWFScriptObject* eventObj = nullptr; + if( eventObj == nullptr ) + { + eventObj = idSWFScriptObject::Alloc(); + idSWFScriptObject* eventParms = idSWFScriptObject::Alloc(); + + eventParms->Set( "type", "Event" ); + eventObj->Set( "[Event]", eventParms ); + + //constants + eventObj->Set( "ACTIVATE", "activate" ); + eventObj->Set( "ADDED", "added" ); + eventObj->Set( "ADDED_TO_STAGE", "addedToStage" ); + eventObj->Set( "BROWSER_ZOOM_CHANGE", "browserZoomChange" ); + eventObj->Set( "CANCEL", "cancel" ); + eventObj->Set( "CHANGE", "change" ); + eventObj->Set( "CHANNEL_MESSAGE", "channelMessage" ); + eventObj->Set( "CHANNEL_STATE", "channelState" ); + eventObj->Set( "CLEAR", "clear" ); + eventObj->Set( "CLOSE", "close" ); + eventObj->Set( "CLOSING", "closing" ); + eventObj->Set( "COMPLETE", "complete" ); + eventObj->Set( "CONNECT", "connect" ); + eventObj->Set( "CONTEXT3D_CREATE", "context3DCreate" ); + eventObj->Set( "COPY", "copy" ); + eventObj->Set( "CUT", "cut" ); + eventObj->Set( "DEACTIVATE", "deactivate" ); + eventObj->Set( "DISPLAYING", "displaying" ); + eventObj->Set( "ENTER_FRAME", "enterFrame" ); + eventObj->Set( "EXIT_FRAME", "exitFrame" ); + eventObj->Set( "EXITING", "exiting" ); + eventObj->Set( "FRAME_CONSTRUCTED", "frameConstructed" ); + eventObj->Set( "FRAME_LABEL", "frameLabel" ); + eventObj->Set( "FULLSCREEN", "fullScreen" ); + eventObj->Set( "HTML_BOUNDS_CHANGE", "htmlBoundsChange" ); + eventObj->Set( "HTML_DOM_INITIALIZE", "htmlDOMInitialize" ); + eventObj->Set( "HTML_RENDER", "htmlRender" ); + eventObj->Set( "ID3", "id3" ); + eventObj->Set( "INIT", "init" ); + eventObj->Set( "LOCATION_CHANGE", "locationChange" ); + eventObj->Set( "MOUSE_LEAVE", "mouseLeave" ); + eventObj->Set( "NETWORK_CHANGE", "networkChange" ); + eventObj->Set( "OPEN", "open" ); + eventObj->Set( "PASTE", "paste" ); + eventObj->Set( "PREPARING", "preparing" ); + eventObj->Set( "REMOVED", "removed" ); + eventObj->Set( "REMOVED_FROM_STAGE", "removedFromStage" ); + eventObj->Set( "RENDER", "render" ); + eventObj->Set( "RESIZE", "resize" ); + eventObj->Set( "SCROLL", "scroll" ); + eventObj->Set( "SELECT", "select" ); + eventObj->Set( "SELECT_ALL", "selectAll" ); + eventObj->Set( "SOUND_COMPLETE", "soundComplete" ); + eventObj->Set( "STANDARD_ERROR_CLOSE", "standardErrorClose" ); + eventObj->Set( "STANDARD_INPUT_CLOSE", "standardInputClose" ); + eventObj->Set( "STANDARD_OUTPUT_CLOSE", "standardOutputClose" ); + eventObj->Set( "SUSPEND", "suspend" ); + eventObj->Set( "TAB_CHILDREN_CHANGE", "tabChildrenChange" ); + eventObj->Set( "TAB_ENABLED_CHANGE", "tabEnabledChange" ); + eventObj->Set( "TAB_INDEX_CHANGE", "tabIndexChange" ); + eventObj->Set( "TEXT_INTERACTION_MODE_CHANGE", "textInteractionModeChange" ); + eventObj->Set( "TEXTURE_READY", "textureReady" ); + eventObj->Set( "UNLOAD", "unload" ); + eventObj->Set( "USER_IDLE", "userIdle" ); + eventObj->Set( "USER_PRESENT", "userPresent" ); + eventObj->Set( "VIDEO_FRAME", "videoFrame" ); + eventObj->Set( "WORKER_STATE", "workerState" ); + } + return eventObj; +} + +SWF_EVENTDISPATCHER_FUNCTION_DEFINE( addEventListener ) +{ + SWF_EVENTDISPATCHER_PTHIS_FUNC( "addEventListener" ); + swfNamedVar_t* dispatcher = thisObject->GetVariable( "__eventDispatcher__", true ); + + if( dispatcher->value.IsUndefined() ) + { + dispatcher->value.SetObject( idSWFScriptObject::Alloc() ); + } + + dispatcher->value.GetObject()->Set( parms[0].ToString(), parms[1] ); + common->DPrintf( "{%s} AddEventListener(%s,%s)\n", thisObject->GetSprite()->name.c_str(), parms[0].ToString().c_str(), parms[1].ToString().c_str() ); + //add listener + return idSWFScriptVar(); +} + + diff --git a/neo/swf/SWF_EventDispatcher.h b/neo/swf/SWF_EventDispatcher.h new file mode 100644 index 0000000000..38204416d9 --- /dev/null +++ b/neo/swf/SWF_EventDispatcher.h @@ -0,0 +1,64 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2023 Harrie van Ginneken + +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 . + +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. + +=========================================================================== +*/ +#ifndef __SWF_EVENTDISPATCHER_H__ +#define __SWF_EVENTDISPATCHER_H__ + +#include "SWF_ScriptObject.h" +#include "SWF_ParmList.h" +#include "SWF_Types.h" +#include "SWF_Sprites.h" +#include "SWF.h" +#include "SWF_Abc.h" + +class idSWFScriptObject_EventDispatcherPrototype : public idSWFScriptObject +{ +public: + idSWFScriptObject_EventDispatcherPrototype(); +#define SWF_EVENTDISPATCHER_FUNCTION_DECLARE( x ) \ + class idSWFScriptFunction_##x : public idSWFScriptFunction { \ + public: \ + void AddRef() {} \ + void Release() {} \ + idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ + } scriptFunction_##x + + SWF_NATIVE_VAR_DECLARE( MouseEvent ); + SWF_NATIVE_VAR_DECLARE( Event ); + + SWF_EVENTDISPATCHER_FUNCTION_DECLARE( addEventListener ); + //SWF_EVENTDISPATHCER_FUNCTION_DECLARE( removeEventListener ); + //SWF_EVENTDISPATHCER_FUNCTION_DECLARE( dispatchEvent ); + //SWF_EVENTDISPATHCER_FUNCTION_DECLARE( dispatchQueue ); + + //object.addEventListener = _fEventDispatcher.addEventListener; + //object.removeEventListener = _fEventDispatcher.removeEventListener; + //object.dispatchEvent = _fEventDispatcher.dispatchEvent; + //object.dispatchQueue = _fEventDispatcher.dispatchQueue; +}; + +#endif //__SWF_EVENTDISPATCHER_H__ diff --git a/neo/swf/SWF_Events.cpp b/neo/swf/SWF_Events.cpp index 57ddfdb1f3..3bd05f54ba 100644 --- a/neo/swf/SWF_Events.cpp +++ b/neo/swf/SWF_Events.cpp @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2016-2017 Dustin Land +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -28,6 +29,36 @@ If you have questions concerning this license or the applicable additional terms */ #include "precompiled.h" #pragma hdrstop +idSWFScriptObject* GetMouseEventDispatcher( idSWFScriptObject* object ) +{ + idSWFScriptObject* dispatcher = nullptr; + if( object->HasValidProperty( "__eventDispatcher__" ) ) + { + dispatcher = object->Get( "__eventDispatcher__" ).GetObject(); + if( dispatcher->HasValidProperty( "click" ) + || dispatcher->HasValidProperty( "contextMenu" ) + || dispatcher->HasValidProperty( "doubleClick" ) + || dispatcher->HasValidProperty( "middleClick" ) + || dispatcher->HasValidProperty( "middleMouseDown" ) + || dispatcher->HasValidProperty( "middleMouseUp" ) + || dispatcher->HasValidProperty( "mouseDown" ) + || dispatcher->HasValidProperty( "mouseMove" ) + || dispatcher->HasValidProperty( "mouseOut" ) + || dispatcher->HasValidProperty( "mouseOver" ) + || dispatcher->HasValidProperty( "mouseUp" ) + || dispatcher->HasValidProperty( "mouseWheel" ) + || dispatcher->HasValidProperty( "releaseOutside" ) + || dispatcher->HasValidProperty( "rightClick" ) + || dispatcher->HasValidProperty( "rightMouseDown" ) + || dispatcher->HasValidProperty( "rightMouseUp" ) + || dispatcher->HasValidProperty( "rollOut" ) + || dispatcher->HasValidProperty( "rollOver" ) ) + { + return dispatcher; + } + } + return nullptr; +} /* =================== @@ -51,15 +82,24 @@ idSWFScriptObject* idSWF::HitTest( idSWFSpriteInstance* spriteInstance, const sw return NULL; } - if( spriteInstance->scriptObject->HasValidProperty( "onRelease" ) - || spriteInstance->scriptObject->HasValidProperty( "onPress" ) - || spriteInstance->scriptObject->HasValidProperty( "onRollOver" ) - || spriteInstance->scriptObject->HasValidProperty( "onRollOut" ) - || spriteInstance->scriptObject->HasValidProperty( "onDrag" ) - ) + idSWFScriptObject* dispatcher = GetMouseEventDispatcher( spriteInstance->scriptObject ); + + if( dispatcher != nullptr ) { parentObject = spriteInstance->scriptObject; } + else + { + if( spriteInstance->scriptObject->HasValidProperty( "onRelease" ) + || spriteInstance->scriptObject->HasValidProperty( "onPress" ) + || spriteInstance->scriptObject->HasValidProperty( "onRollOver" ) + || spriteInstance->scriptObject->HasValidProperty( "onRollOut" ) + || spriteInstance->scriptObject->HasValidProperty( "onDrag" ) + ) + { + parentObject = spriteInstance->scriptObject; + } + } // rather than returning the first object we find, we actually want to return the last object we find idSWFScriptObject* returnObject = NULL; @@ -123,20 +163,25 @@ idSWFScriptObject* idSWF::HitTest( idSWFSpriteInstance* spriteInstance, const sw { // FIXME: this should be roughly the same as SWF_DICT_SHAPE } - else if( entry->type == SWF_DICT_TEXT ) - { - // FIXME: this should be roughly the same as SWF_DICT_SHAPE - } - else if( entry->type == SWF_DICT_EDITTEXT ) + else if( entry->type == SWF_DICT_EDITTEXT || entry->type == SWF_DICT_TEXT ) { idSWFScriptObject* editObject = NULL; - if( display.textInstance->scriptObject.HasProperty( "onRelease" ) || display.textInstance->scriptObject.HasProperty( "onPress" ) ) + idSWFScriptObject* textdispatcher = nullptr; + if( display.textInstance ) + { + dispatcher = GetMouseEventDispatcher( &display.textInstance->scriptObject ); + } + if( dispatcher != nullptr ) + { + editObject = &display.textInstance->scriptObject; + } + else if( display.textInstance && ( display.textInstance->scriptObject.HasProperty( "onRelease" ) || display.textInstance->scriptObject.HasProperty( "onPress" ) ) ) { // if the edit box itself can be clicked, then we want to return it when it's clicked on editObject = &display.textInstance->scriptObject; } - else if( parentObject != NULL ) + else if( !dispatcher && parentObject != NULL ) { // otherwise, we want to return the parent object editObject = parentObject; @@ -147,7 +192,7 @@ idSWFScriptObject* idSWF::HitTest( idSWFSpriteInstance* spriteInstance, const sw continue; } - if( display.textInstance->text.IsEmpty() ) + if( !display.textInstance || display.textInstance->text.IsEmpty() ) { continue; } @@ -254,6 +299,7 @@ bool idSWF::HandleEvent( const sysEvent_t* event ) { mouseEnabled = true; idSWFScriptVar var; + idSWFScriptVar eventDispatcher = mainspriteInstance->GetScriptObject()->Get( "__eventDispatcher__" ); if( event->evValue2 ) { @@ -277,6 +323,36 @@ bool idSWF::HandleEvent( const sysEvent_t* event ) mouseObject = hitObject; mouseObject->AddRef(); + eventDispatcher = hitObject->Get( "__eventDispatcher__" ); + + if( !eventDispatcher.IsUndefined() && !var.IsFunction() ) + { + var = eventDispatcher.GetObject()->Get( "click" ); + if( !var.IsFunction() ) + { + var = eventDispatcher.GetObject()->Get( "mouseDown" ); + } + } + if( var.IsFunction() ) + { + idSWFScriptVar eventArg; + auto* eventObj = globals->Get( "EventDispatcher" ).GetObject() + ->Get( "MouseEvent" ).GetObject() + ->Get( "[MouseEvent]" ).GetObject(); + eventArg.SetObject( idSWFScriptObject::Alloc() ); + eventArg.GetObject()->DeepCopy( eventObj ); + idSWFParmList parms; + parms.Append( eventArg ); + parms.Append( event->inputDevice ); + if( !( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Append( globals ); + } + var.GetFunction()->Call( hitObject, parms ); + parms.Clear(); + return true; + } + var = hitObject->Get( "onPress" ); if( var.IsFunction() ) { @@ -309,12 +385,40 @@ bool idSWF::HandleEvent( const sysEvent_t* event ) { if( mouseObject ) { + eventDispatcher = mouseObject->Get( "__eventDispatcher__" ); + + if( !eventDispatcher.IsUndefined() && !var.IsFunction() ) + { + var = eventDispatcher.GetObject()->Get( "mouseUp" ); + } + if( var.IsFunction() ) + { + idSWFScriptVar eventArg; + auto* eventObj = globals->Get( "EventDispatcher" ).GetObject() + ->Get( "MouseEvent" ).GetObject() + ->Get( "[MouseEvent]" ).GetObject(); + eventArg.SetObject( idSWFScriptObject::Alloc() ); + eventArg.GetObject()->DeepCopy( eventObj ); + idSWFParmList parms; + parms.Append( eventArg ); + if( !( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Append( globals ); + } + var.GetFunction()->Call( mouseObject, parms ); + parms.Clear(); + mouseObject->Release(); + mouseObject = NULL; + return true; + } + var = mouseObject->Get( "onRelease" ); if( var.IsFunction() ) { idSWFParmList parms; parms.Append( mouseObject ); // FIXME: Remove this var.GetFunction()->Call( mouseObject, parms ); + parms.Clear(); } mouseObject->Release(); mouseObject = NULL; @@ -494,13 +598,39 @@ bool idSWF::HandleEvent( const sysEvent_t* event ) hasHitObject = false; } + idSWFScriptVar eventDispatcher; if( hitObject != hoverObject ) { + // First check to see if we should call onRollOut on our previous hoverObject if( hoverObject != NULL ) { idSWFScriptVar var = hoverObject->Get( "onRollOut" ); - if( var.IsFunction() ) + + eventDispatcher = hoverObject->Get( "__eventDispatcher__" ); + if( !eventDispatcher.IsUndefined() && !var.IsFunction() ) + { + var = eventDispatcher.GetObject()->Get( "mouseOut" ); + if( var.IsFunction() ) + { + idSWFScriptVar eventArg; + auto* eventObj = globals->Get( "EventDispatcher" ).GetObject() + ->Get( "MouseEvent" ).GetObject() + ->Get( "[MouseEvent]" ).GetObject(); + eventArg.SetObject( idSWFScriptObject::Alloc() ); + eventArg.GetObject()->DeepCopy( eventObj ); + idSWFParmList parms; + parms.Append( eventArg ); + if( !( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Append( globals ); + } + var.GetFunction()->Call( hoverObject, parms ); + parms.Clear(); + retVal = true; + } + } + else if( var.IsFunction() ) { var.GetFunction()->Call( hoverObject, idSWFParmList() ); retVal = true; @@ -514,7 +644,31 @@ bool idSWF::HandleEvent( const sysEvent_t* event ) hoverObject = hitObject; hoverObject->AddRef(); idSWFScriptVar var = hitObject->Get( "onRollOver" ); - if( var.IsFunction() ) + + eventDispatcher = hoverObject->Get( "__eventDispatcher__" ); + if( !eventDispatcher.IsUndefined() && !var.IsFunction() ) + { + var = eventDispatcher.GetObject()->Get( "mouseOver" ); + if( var.IsFunction() ) + { + idSWFScriptVar eventArg; + auto* eventObj = globals->Get( "EventDispatcher" ).GetObject() + ->Get( "MouseEvent" ).GetObject() + ->Get( "[MouseEvent]" ).GetObject(); + eventArg.SetObject( idSWFScriptObject::Alloc() ); + eventArg.GetObject()->DeepCopy( eventObj ); + idSWFParmList parms; + parms.Append( eventArg ); + if( !( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* ) var.GetFunction() )->GetScope()->Append( globals ); + } + var.GetFunction()->Call( hoverObject, parms ); + parms.Clear(); + retVal = true; + } + } + else if( var.IsFunction() ) { var.GetFunction()->Call( hitObject, idSWFParmList() ); retVal = true; diff --git a/neo/swf/SWF_Interpreter.cpp b/neo/swf/SWF_Interpreter.cpp new file mode 100644 index 0000000000..8f945ed41e --- /dev/null +++ b/neo/swf/SWF_Interpreter.cpp @@ -0,0 +1,1611 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2023 Harrie van Ginneken + +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 . + +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. + +=========================================================================== +*/ +#include "precompiled.h" +#pragma hdrstop + +//#include "SWF_Bitstream.h" +//#include "SWF_Enums.h" +//#include "SWF_Abc.h" +//#include "SWF_ScriptObject.h" +//#include "SWF_ScriptFunction.h" +//#include "framework/Common.h" + +inline int32 readS24( const byte* pc ) +{ + return ( ( uint16_t* )pc )[0] | ( ( int8_t* )pc )[2] << 16; +} + +inline uint32 readU30( const uint8_t*& pc ) +{ + uint32 result = 0; + for( int i = 0; i < 5; i++ ) + { + byte b = *pc++; + result |= ( b & 0x7F ) << ( 7 * i ); + if( ( b & 0x80 ) == 0 ) + { + return result; + } + } + return result; +} + +const AbcOpcodeInfo opcodeInfo[] = +{ + // For stack movement ("stk") only constant movement is accounted for; variable movement, + // as for arguments to CALL, CONSTRUCT, APPLYTYPE, et al, and for run-time parts of + // names, must be handled separately. + +#define ABC_OP(operandCount, canThrow, stack, internalOnly, nameToken) { operandCount, canThrow, stack , OP_##nameToken , #nameToken }, +#define ABC_UNUSED_OP(operandCount, canThrow, stack, internalOnly, nameToken) { operandCount, canThrow, stack , 0 , #nameToken }, + +#include "opcodes.tbl" + +#undef ABC_OP +#undef ABC_UNUSED_OP +#undef ABC_OP_F +}; + +void debugfile( SWF_AbcFile* file, idSWFBitStream& bitstream ) +{ + common->Printf( " debugfile %s\n", file->constant_pool.utf8Strings[bitstream.ReadEncodedU32()].c_str() ); +} + +void debugline( SWF_AbcFile* file, idSWFBitStream& bitstream ) +{ + common->Printf( " debugline %i\n", ( int )bitstream.ReadEncodedU32() ); +} + +void swf_PrintStream( SWF_AbcFile* file, idSWFBitStream& bitstream ) +{ + //(case.*OP_)([A-Za-z0-9]*_?[A-Za-z0-9]*)(.*\n)(.*) + idSWFStack stack; + static idList codeMap; + idStr type; + const AbcOpcodeInfo* info = nullptr; + while( bitstream.Tell() < bitstream.Length() ) + { +#define DoWordCode( n ) case OP_##n: type = #n; info = &opcodeInfo[opCode]; break; +#define ExecWordCode( n ) case OP_##n: type = #n; info = &opcodeInfo[opCode]; n(file,bitstream); continue; + SWFAbcOpcode opCode = ( SWFAbcOpcode ) bitstream.ReadU8(); + switch( opCode ) + { + DoWordCode( bkpt ); + DoWordCode( nop ); + DoWordCode( throw ); + DoWordCode( getsuper ); + DoWordCode( setsuper ); + DoWordCode( dxns ); + DoWordCode( dxnslate ); + DoWordCode( kill ); + DoWordCode( label ); + DoWordCode( ifnlt ); + DoWordCode( ifnle ); + DoWordCode( ifngt ); + DoWordCode( ifnge ); + DoWordCode( jump ); + DoWordCode( iftrue ); + DoWordCode( iffalse ); + DoWordCode( ifeq ); + DoWordCode( ifne ); + DoWordCode( iflt ); + DoWordCode( ifle ); + DoWordCode( ifgt ); + DoWordCode( ifge ); + DoWordCode( ifstricteq ); + DoWordCode( ifstrictne ); + DoWordCode( lookupswitch ); + DoWordCode( pushwith ); + DoWordCode( popscope ); + DoWordCode( nextname ); + DoWordCode( hasnext ); + DoWordCode( pushnull ); + DoWordCode( pushundefined ); + DoWordCode( DISABLED_pushfloat ); + DoWordCode( nextvalue ); + DoWordCode( pushbyte ); + DoWordCode( pushshort ); + DoWordCode( pushtrue ); + DoWordCode( pushfalse ); + DoWordCode( pushnan ); + DoWordCode( pop ); + DoWordCode( dup ); + DoWordCode( swap ); + DoWordCode( pushstring ); + DoWordCode( pushint ); + DoWordCode( pushuint ); + DoWordCode( pushdouble ); + DoWordCode( pushscope ); + DoWordCode( pushnamespace ); + DoWordCode( hasnext2 ); + DoWordCode( lix8 ); + DoWordCode( lix16 ); + DoWordCode( li8 ); + DoWordCode( li16 ); + DoWordCode( li32 ); + DoWordCode( lf32 ); + DoWordCode( lf64 ); + DoWordCode( si8 ); + DoWordCode( si16 ); + DoWordCode( si32 ); + DoWordCode( sf32 ); + DoWordCode( sf64 ); + DoWordCode( newfunction ); + DoWordCode( call ); + DoWordCode( construct ); + DoWordCode( callmethod ); + DoWordCode( callstatic ); + DoWordCode( callsuper ); + DoWordCode( callproperty ); + DoWordCode( returnvoid ); + DoWordCode( returnvalue ); + DoWordCode( constructsuper ); + DoWordCode( constructprop ); + DoWordCode( callsuperid ); + DoWordCode( callproplex ); + DoWordCode( callinterface ); + DoWordCode( callsupervoid ); + DoWordCode( callpropvoid ); + DoWordCode( sxi1 ); + DoWordCode( sxi8 ); + DoWordCode( sxi16 ); + DoWordCode( applytype ); + DoWordCode( DISABLED_pushfloat4 ); + DoWordCode( newobject ); + DoWordCode( newarray ); + DoWordCode( newactivation ); + DoWordCode( newclass ); + DoWordCode( getdescendants ); + DoWordCode( newcatch ); + DoWordCode( findpropglobalstrict ); + DoWordCode( findpropglobal ); + DoWordCode( findpropstrict ); + DoWordCode( findproperty ); + DoWordCode( finddef ); + DoWordCode( getlex ); + DoWordCode( setproperty ); + DoWordCode( getlocal ); + DoWordCode( setlocal ); + DoWordCode( getglobalscope ); + DoWordCode( getscopeobject ); + DoWordCode( getproperty ); + DoWordCode( getouterscope ); + DoWordCode( initproperty ); + DoWordCode( deleteproperty ); + DoWordCode( getslot ); + DoWordCode( setslot ); + DoWordCode( getglobalslot ); + DoWordCode( setglobalslot ); + DoWordCode( convert_s ); + DoWordCode( esc_xelem ); + DoWordCode( esc_xattr ); + DoWordCode( convert_i ); + DoWordCode( convert_u ); + DoWordCode( convert_d ); + DoWordCode( convert_b ); + DoWordCode( convert_o ); + DoWordCode( checkfilter ); + //DoWordCode ( DISABLED_convert ); + //DoWordCode ( DISABLED_unplus ); + //DoWordCode ( DISABLED_convert ); + DoWordCode( coerce ); + DoWordCode( coerce_b ); + DoWordCode( coerce_a ); + DoWordCode( coerce_i ); + DoWordCode( coerce_d ); + DoWordCode( coerce_s ); + DoWordCode( astype ); + DoWordCode( astypelate ); + DoWordCode( coerce_u ); + DoWordCode( coerce_o ); + DoWordCode( negate ); + DoWordCode( increment ); + DoWordCode( inclocal ); + DoWordCode( decrement ); + DoWordCode( declocal ); + DoWordCode( typeof ); + DoWordCode( not ); + DoWordCode( bitnot ); + DoWordCode( add ); + DoWordCode( subtract ); + DoWordCode( multiply ); + DoWordCode( divide ); + DoWordCode( modulo ); + DoWordCode( lshift ); + DoWordCode( rshift ); + DoWordCode( urshift ); + DoWordCode( bitand ); + DoWordCode( bitor ); + DoWordCode( bitxor ); + DoWordCode( equals ); + DoWordCode( strictequals ); + DoWordCode( lessthan ); + DoWordCode( lessequals ); + DoWordCode( greaterthan ); + DoWordCode( greaterequals ); + DoWordCode( instanceof ); + DoWordCode( istype ); + DoWordCode( istypelate ); + DoWordCode( in ); + DoWordCode( increment_i ); + DoWordCode( decrement_i ); + DoWordCode( inclocal_i ); + DoWordCode( declocal_i ); + DoWordCode( negate_i ); + DoWordCode( add_i ); + DoWordCode( subtract_i ); + DoWordCode( multiply_i ); + DoWordCode( getlocal0 ); + DoWordCode( getlocal1 ); + DoWordCode( getlocal2 ); + DoWordCode( getlocal3 ); + DoWordCode( setlocal0 ); + DoWordCode( setlocal1 ); + DoWordCode( setlocal2 ); + DoWordCode( setlocal3 ); + DoWordCode( debug ); + ExecWordCode( debugline ); + ExecWordCode( debugfile ); + DoWordCode( bkptline ); + DoWordCode( timestamp ); + DoWordCode( restargc ); + DoWordCode( restarg ); + default: + common->Printf( "default %s %s\n", type.c_str() , info ? info->name : "Empty" ); + } + static const char* tabs[] = { " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; + if( info && info->operandCount > 0 ) + { + bitstream.ReadData( info->operandCount ); + } + common->Printf( " %s %s o %s%i \t s %s%i \n" , + info ? info->name : type.c_str(), + tabs[int( 18 - ( int( idStr::Length( info->name ) ) ) )], + info->operandCount > 0 ? "^2" : "^1" , + info->operandCount, + info->stack < 0 ? "^2" : "^1", + info->stack + ); + } + bitstream.Rewind(); +#undef DoWordCode +#undef ExecWordCode +} + +void idSWFScriptFunction_Script::findproperty( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + const auto& cp = file->constant_pool; + const auto& mn = file->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr propName = ( idStrPtr )&cp.utf8Strings[mn.nameIndex]; + //search up scope stack. + for( int i = scope.Num() - 1; i >= 0; i-- ) + { + auto* s = scope[i]; + while( s ) + if( s->HasProperty( propName->c_str() ) ) + { + stack.Alloc() = s->Get( propName->c_str() ); + return; + } + else if( s->GetPrototype() && s->GetPrototype()->GetPrototype() ) + { + s = s->GetPrototype()->GetPrototype(); + } + else + { + s = NULL; + } + } + auto prop = scope[0]->GetVariable( *propName, true ); + stack.Alloc() = prop->value; +} + +void idSWFScriptFunction_Script::findpropstrict( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + const auto& cp = file->constant_pool; + const auto& mn = file->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr propName = ( idStrPtr ) &cp.utf8Strings[mn.nameIndex]; + //search up scope stack. + for( int i = scope.Num() - 1; i >= 0; i-- ) + { + auto* s = scope[i]; + while( s ) + if( s->HasProperty( propName->c_str() ) ) + { + stack.Alloc() = s->Get( propName->c_str() ); + return; + } + else if( s->GetPrototype() && s->GetPrototype()->GetPrototype() ) + { + s = s->GetPrototype()->GetPrototype(); + } + else + { + s = NULL; + } + } + common->Warning( "idSWFScriptFunction_Script::findpropstrict cant find %s", propName->c_str() ); + stack.Alloc().SetObject( idSWFScriptObject::Alloc() ); + stack.A().GetObject()->Release(); +} + +void idSWFScriptFunction_Script::getlex( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + const auto& cp = file->constant_pool; + const auto& mn = file->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr propName = ( idStrPtr ) &cp.utf8Strings[mn.nameIndex]; + //search up scope stack. + for( int i = scope.Num() - 1; i >= 0; i-- ) + { + auto* s = scope[i]; + while( s ) + if( s->HasProperty( propName->c_str() ) ) + { + stack.Alloc() = s->Get( propName->c_str() ); + return; + } + else if( s->GetPrototype() && s->GetPrototype()->GetPrototype() ) + { + s = s->GetPrototype()->GetPrototype(); + } + else + { + s = NULL; + } + } +} + +void idSWFScriptFunction_Script::getscopeobject( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + uint8 index = bitstream.ReadEncodedU32(); + stack.Alloc() = scope[( scope.Num() - 1 ) - index]; +} + +void idSWFScriptFunction_Script::pushscope( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + if( stack.Num() > 0 ) + { + if( stack.A().IsObject() ) + { + auto stackOBj = stack.A().GetObject(); + stackOBj->AddRef(); + scope.Alloc() = stackOBj; + + } + else + { + common->DWarning( "tried to push a non object onto scope" ); + } + stack.Pop( 1 ); + } +} + +void idSWFScriptFunction_Script::getlocal0( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + stack.Alloc() = registers[0]; +} + +//Classes are constructed implicitly +void idSWFScriptFunction_Script::newclass( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + const auto& ci = file->classes[bitstream.ReadEncodedU32()]; + idSWFScriptVar base = stack.A(); + stack.Pop( 1 ); + idSWFScriptVar* thisObj = ®isters[0]; + common->FatalError( "Bytestream corrupted? Classes should already be created in CreateAbcObjects()!" ); +} + +void idSWFScriptFunction_Script::callpropvoid( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + const auto& cp = file->constant_pool; + const auto& mn = file->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr funcName = ( idStrPtr ) &cp.utf8Strings[mn.nameIndex]; + + uint32 arg_count = bitstream.ReadEncodedU32(); + + //Todo Optimize: search for addFrameScript string index in constantpool! + if( *funcName == "addFrameScript" ) + { + stack.Pop( arg_count ); + arg_count = 0; + } + idSWFParmList parms( arg_count ); + + for( int i = 0; i < parms.Num(); i++ ) + { + parms[parms.Num() - 1 - i] = stack.A(); + stack.Pop( 1 ); + } + if( stack.Num() ) + { + idSWFScriptVar& item = stack.A(); + if( item.IsFunction() ) + { + auto func = ( ( idSWFScriptFunction_Script* )item.GetFunction() ); + if( !func->GetScope()->Num() ) + { + func->SetScope( *GetScope() ); + } + item.GetFunction()->Call( registers[0].GetObject(), parms ); + } + else if( item.IsObject() ) + { + auto func = item.GetObject()->Get( funcName->c_str() ); + if( func.IsFunction() ) + { + if( !( ( idSWFScriptFunction_Script* )func.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* )func.GetFunction() )->SetScope( *GetScope() ); + } + func.GetFunction()->Call( item.GetObject(), parms ); + } + } + stack.Pop( 1 ); + } +} + +/* +======================== +idSWFScriptFunction_Script::RunAbc bytecode +======================== +*/ +idSWFScriptVar idSWFScriptFunction_Script::RunAbc( idSWFScriptObject* thisObject, idSWFStack& stack, idSWFBitStream& bitstream ) +{ + static int abcCallstackLevel = -1; + assert( abcFile ); + idSWFScriptVar returnValue; + + idSWFSpriteInstance* thisSprite = thisObject->GetSprite(); + idSWFSpriteInstance* currentTarget = thisSprite; + + if( currentTarget == NULL ) + { + thisSprite = currentTarget = defaultSprite; + } + + abcCallstackLevel++; + while( bitstream.Tell() < bitstream.Length() ) + { +#define ExecWordCode( n ) case OP_##n: n(abcFile,stack,bitstream); continue; +#define InlineWordCode( n ) case OP_##n: + SWFAbcOpcode opCode = ( SWFAbcOpcode ) bitstream.ReadU8(); + switch( opCode ) + { + //ExecWordCode( bkpt ); + //ExecWordCode( throw ); + //ExecWordCode( getsuper ); + //ExecWordCode( setsuper ); + //ExecWordCode( dxns ); + //ExecWordCode( dxnslate ); + InlineWordCode( kill ) + { + registers[bitstream.ReadEncodedU32()].SetUndefined(); + continue; + } + InlineWordCode( nop ); + InlineWordCode( label ) + { + continue; + } + InlineWordCode( ifnlt ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = !( lH.ToString() < rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = !( lH.ToFloat() < rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = !( lH.ToInteger() < rH.ToInteger() ); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifnle ) + { + int offset = bitstream.ReadS24(); + const auto& lH = stack.B(); + const auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = !( lH.ToString() <= rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = !( lH.ToFloat() <= rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = !( lH.ToInteger() <= rH.ToInteger() ); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifngt ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = !( lH.ToString() > rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = !( lH.ToFloat() > rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = !( lH.ToInteger() > rH.ToInteger() ); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifnge ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = !( lH.ToString() >= rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = !( lH.ToFloat() >= rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = !( lH.ToInteger() >= rH.ToInteger() ); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( jump ) + { + int offset = bitstream.ReadS24(); + bitstream.Seek( offset ); + continue; + } + InlineWordCode( iftrue ) + { + int offset = bitstream.ReadS24(); + idSWFScriptVar value = stack.A(); + stack.Pop( 1 ); + bool condition = value.ToBool(); + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( iffalse ) + { + int offset = bitstream.ReadS24(); + if( !stack.A().ToBool() ) + { + bitstream.Seek( offset ); + } + stack.Pop( 1 ); + continue; + } + InlineWordCode( ifeq ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() == rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() == rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() == rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifne ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() != rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() != rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() != rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( iflt ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() < rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() < rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() < rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifle ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() <= rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() <= rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() <= rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifgt ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() > rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() > rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() > rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifge ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() >= rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() >= rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() >= rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifstricteq ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + if( lH.GetType() != rH.GetType() ) + { + condition = false; + } + else + { + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = lH.ToString() == rH.ToString(); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() == rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() == rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + InlineWordCode( ifstrictne ) + { + int offset = bitstream.ReadS24(); + auto& lH = stack.B(); + auto& rH = stack.A(); + stack.Pop( 2 ); + bool condition = false; + if( lH.GetType() != rH.GetType() ) + { + condition = true; + } + else + { + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + condition = ( lH.ToString() != rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + condition = lH.ToFloat() != rH.ToFloat(); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + condition = lH.ToInteger() != rH.ToInteger(); + break; + default: + common->Warning( " Tried to compare incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + } + if( condition ) + { + bitstream.Seek( offset ); + } + continue; + } + //ExecWordCode( lookupswitch ); + //ExecWordCode( pushwith ); + InlineWordCode( popscope ) + { + scope[scope.Num() - 1]->Release(); + scope.SetNum( scope.Num() - 1 ); + continue; + } + //ExecWordCode( nextname ); + //ExecWordCode( hasnext ); + InlineWordCode( pushnull ) + { + stack.Append( idSWFScriptVar( ( idSWFScriptObject* )( NULL ) ) ); + continue; + } + InlineWordCode( pushundefined ) + { + stack.Append( idSWFScriptVar() ); + continue; + } + //ExecWordCode( nextvalue ); + InlineWordCode( pushbyte ) + { + stack.Append( idSWFScriptVar( ( int )bitstream.ReadU8() ) ); + continue; + } + InlineWordCode( pushshort ) + { + stack.Append( idSWFScriptVar( ( int )bitstream.ReadEncodedU32() ) ); + continue; + } + InlineWordCode( pushtrue ) + { + stack.Append( idSWFScriptVar( true ) ); + continue; + } + InlineWordCode( pushfalse ) + { + stack.Append( idSWFScriptVar( false ) ); + continue; + } + //ExecWordCode ( pushnan ); + InlineWordCode( pop ) + { + stack.Pop( 1 ); + continue; + } + InlineWordCode( dup ) + { + stack.Alloc() = idSWFScriptVar( stack.A() ); + continue; + } + InlineWordCode( swap ) + { + common->FatalError( "swap not implemented" ); + continue; + } + InlineWordCode( pushstring ) + { + const auto& cp = abcFile->constant_pool.utf8Strings; + const auto& mn = cp[bitstream.ReadEncodedU32()]; + stack.Append( idSWFScriptString( mn ) ); + continue; + } + InlineWordCode( pushint ) + { + const auto& cp = abcFile->constant_pool.integers; + const auto& val = cp[bitstream.ReadEncodedU32()]; + stack.Append( idSWFScriptVar( val ) ); + continue; + } + InlineWordCode( pushuint ) + { + const auto& cp = abcFile->constant_pool.uIntegers; + const auto& val = cp[bitstream.ReadEncodedU32()]; + stack.Append( idSWFScriptVar( ( int )val ) ); + continue; + } + InlineWordCode( pushdouble ) + { + const auto& cp = abcFile->constant_pool.doubles; + const auto& val = cp[bitstream.ReadEncodedU32()]; + stack.Append( idSWFScriptVar( ( float )val ) ); + continue; + } + ExecWordCode( pushscope ) + //ExecWordCode( pushnamespace ); + //ExecWordCode( hasnext2 ); + //ExecWordCode( lix8 ); + //ExecWordCode( lix16 ); + //ExecWordCode( li8 ); + //ExecWordCode( li16 ); + //ExecWordCode( li32 ); + //ExecWordCode( lf32 ); + //ExecWordCode( lf64 ); + //ExecWordCode( si8 ); + //ExecWordCode( si8 ); + //ExecWordCode( si16 ); + //ExecWordCode( si32 ); + //ExecWordCode( sf32 ); + //ExecWordCode( sf64 ); + InlineWordCode( newfunction ) + { + const auto& cp = abcFile->constant_pool; + auto& method = abcFile->methods[bitstream.ReadEncodedU32()]; + idSWFScriptFunction_Script* func = idSWFScriptFunction_Script::Alloc(); + func->SetAbcFile( abcFile ); + func->methodInfo = &method; + func->SetScope( scope ); + func->SetConstants( constants ); + func->SetDefaultSprite( defaultSprite ); + stack.Alloc() = idSWFScriptVar( func ); + stack.A().GetFunction()->Release(); + continue; + } + InlineWordCode( call ) + InlineWordCode( construct ) + InlineWordCode( callmethod ) + InlineWordCode( callstatic ) + InlineWordCode( callsuper ) + { + common->FatalError( "Not implemented" ); + continue; + } + InlineWordCode( callproperty ) + { + //fold this with callpropvoid. + const auto& cp = abcFile->constant_pool; + const auto& mn = abcFile->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr funcName = ( idStrPtr ) &cp.utf8Strings[mn.nameIndex]; + uint32 arg_count = bitstream.ReadEncodedU32(); + + idSWFParmList parms( arg_count ); + + for( int i = 0; i < parms.Num(); i++ ) + { + parms[parms.Num() - 1 - i] = stack.A(); + stack.Pop( 1 ); + } + idSWFScriptVar& item = stack.A(); + + if( item.IsFunction() ) + { + stack.Pop( 1 ); + stack.Alloc() = item.GetFunction()->Call( registers[0].GetObject(), parms ); + } + else if( item.IsObject() ) + { + auto func = item.GetObject()->Get( funcName->c_str() ); + if( !func.IsFunction() ) // search up scope + { + for( int i = scope.Num() - 1; i >= 0; i-- ) + { + auto* s = scope[i]; + while( s ) + { + if( s->HasProperty( funcName->c_str() ) ) + { + func = s->Get( funcName->c_str() ); + s = NULL; + i = -1; + break; + } + else if( s->GetPrototype() && s->GetPrototype()->GetPrototype() ) + { + s = s->GetPrototype()->GetPrototype(); + } + else + { + s = NULL; + } + } + } + } + if( func.IsFunction() ) + { + stack.Alloc() = func.GetFunction()->Call( item.GetObject(), parms ); + } + } + continue; + } + InlineWordCode( returnvalue ) + { + returnValue = stack.A(); + [[fallthrough]]; + } + InlineWordCode( returnvoid ) + { + if( scope.Num() ) + { + scope[scope.Num() - 1]->Release(); + scope.SetNum( scope.Num() - 1 ); + } + continue; + } + InlineWordCode( constructsuper ) + { + uint32 args = bitstream.ReadEncodedU32(); + stack.Pop( args ); + continue; + } + InlineWordCode( constructprop ) + { + //no need to call constructors for props that + const auto& cp = abcFile->constant_pool; + const auto& mn = abcFile->constant_pool.multinameInfos[bitstream.ReadEncodedU32()]; + const idStrPtr propName = ( idStrPtr ) &cp.utf8Strings[mn.nameIndex]; + uint32 arg_count = bitstream.ReadEncodedU32(); + if( *propName == "Array" ) + { + for( int i = 0; i < arg_count; i++ ) + { + stack[stack.Num() - ( arg_count + 1 )].GetObject()->Set( i, stack[stack.Num() - arg_count + i] ); + } + } + stack.Pop( arg_count ); + continue; + } + //ExecWordCode( callsuperid ); + //ExecWordCode( callproplex ); + //ExecWordCode( callinterface ); + //ExecWordCode( callsupervoid ); + ExecWordCode( callpropvoid ); + //ExecWordCode( sxi1 ); + //ExecWordCode( sxi8 ); + //ExecWordCode( sxi16 ); + //ExecWordCode( applytype ); + //ExecWordCode( DISABLED_pushfloat4 ); + InlineWordCode( newarray ) + { + auto* newArray = idSWFScriptObject::Alloc(); + + newArray->MakeArray(); + + uint32 args = bitstream.ReadEncodedU32(); + for( int i = 0; i < args; i++ ) + { + newArray->Set( i, stack.A() ); + stack.Pop( 1 ); + } + + idSWFScriptVar baseObjConstructor = scope[0]->Get( "Object" ); + idSWFScriptFunction* baseObj = baseObjConstructor.GetFunction(); + newArray->SetPrototype( baseObj->GetPrototype() ); + stack.Alloc().SetObject( newArray ); + + newArray->Release(); + continue; + } + InlineWordCode( newobject ); + InlineWordCode( newactivation ) + { + idSWFScriptObject* object = idSWFScriptObject::Alloc(); + idSWFScriptVar baseObjConstructor = scope[0]->Get( "Object" ); + idSWFScriptFunction* baseObj = baseObjConstructor.GetFunction(); + object->SetPrototype( baseObj->GetPrototype() ); + stack.Alloc().SetObject( object ); + object->Release(); + continue; + } + ExecWordCode( newclass ); + //ExecWordCode ( getdescendants ); + //ExecWordCode ( newcatch ); + //ExecWordCode ( findpropglobalstrict ); + //ExecWordCode ( findpropglobal ); + ExecWordCode( findproperty ); + ExecWordCode( findpropstrict ); + + //ExecWordCode ( finddef ); + ExecWordCode( getlex ); + InlineWordCode( setproperty ) + { + const auto& cp = abcFile->constant_pool; + const auto& mn = cp.multinameInfos[bitstream.ReadEncodedU32()]; + const auto& n = cp.utf8Strings[mn.nameIndex]; + idStr index = n; + idSWFScriptObject* target = nullptr; + + if( mn.nameIndex && !stack.B().IsObject() ) + { + target = scope[0]; + } + else if( !mn.nameIndex ) + { + index = stack.B().ToString(); + idSWFScriptVar val = stack.A(); + stack.Pop( 2 ); + target = stack.A().GetObject(); + stack.Alloc() = val; + } + else if( stack.B().IsObject() ) + { + target = stack.B().GetObject(); + } + target->Set( index, stack.A() ); + stack.Pop( 2 ); + continue; + } + InlineWordCode( getlocal ) + { + stack.Alloc() = registers[bitstream.ReadEncodedU32()]; + continue; + } + InlineWordCode( setlocal ); + { + registers[bitstream.ReadEncodedU32()] = stack.A(); + stack.Pop( 1 ); + continue; + } + //ExecWordCode ( getglobalscope ); + ExecWordCode( getscopeobject ); + InlineWordCode( getproperty ) + { + const auto& cp = abcFile->constant_pool; + const auto& mn = cp.multinameInfos[bitstream.ReadEncodedU32()]; + const auto& n = cp.utf8Strings[mn.nameIndex]; + + idStr index = n; + idSWFScriptObject* target = nullptr; + + if( mn.nameIndex && !stack.A().IsObject() ) + { + target = scope[0]; + } + else if( !mn.nameIndex ) + { + target = stack.B().GetObject(); + index = stack.A().ToString(); + stack.Pop( 1 ); + } + else + { + target = stack.A().GetObject(); + } + + stack.Pop( 1 ); + + if( target->HasProperty( index.c_str() ) ) + { + stack.Append( target->Get( index.c_str() ) ); + } + else + { + stack.Alloc().SetObject( idSWFScriptObject::Alloc() ); + stack.A().GetObject()->Release(); + } + + continue; + } + //ExecWordCode ( getouterscope ); + InlineWordCode( initproperty ) + { + const auto& cp = abcFile->constant_pool; + const auto& mn = cp.multinameInfos[bitstream.ReadEncodedU32()]; + const auto& n = cp.utf8Strings[mn.nameIndex]; + + idSWFScriptVar value = stack.A(); + stack.Pop( 1 ); + stack.A().GetObject()->Set( n, value ); + continue; + } + //ExecWordCode ( 0x69 ); + //ExecWordCode ( deleteproperty ); + //ExecWordCode ( 0x6B ); + InlineWordCode( getslot ) + { + if( stack.A().IsObject() ) + { + stack.Append( stack.A().GetObject()->Get( bitstream.ReadEncodedU32() ) ); + } + continue; + } + + InlineWordCode( setslot ) + { + auto var = stack.A(); + + if( stack.B().IsUndefined() || stack.B().IsNULL() ) + { + stack.B().SetObject( idSWFScriptObject::Alloc() ); + stack.B().GetObject()->MakeArray(); + } + + stack.B().GetObject()->Set( bitstream.ReadEncodedU32(), var ); + continue; + } + //ExecWordCode ( getglobalslot ); + //ExecWordCode ( setglobalslot ); + //ExecWordCode ( convert_s ); + //ExecWordCode ( esc_xelem ); + //ExecWordCode ( esc_xattr ); + //ExecWordCode ( convert_i ); + //ExecWordCode ( convert_u ); + //ExecWordCode ( convert_d ); + //ExecWordCode ( convert_b ); + //ExecWordCode ( convert_o ); + //ExecWordCode ( checkfilter ); + //ExecWordCode ( DISABLED_convert ); + //ExecWordCode ( DISABLED_unplus ); + //ExecWordCode ( DISABLED_convert ); + //ExecWordCode ( coerce ); + //ExecWordCode ( coerce_b ); + InlineWordCode( coerce_a ) + { + auto var = stack.A(); + stack.Pop( 1 ); + if( !stack.A().IsValid() ) + { + stack.A().SetNULL(); + stack.Append( var ); + } + else + { + if( !var.IsUndefined() ) + { + stack.Append( var.ToString() ); + } + else + { + stack.Append( var ); + } + + } + + continue; + } + //ExecWordCode ( coerce_i ); + //ExecWordCode ( coerce_d ); + //ExecWordCode ( coerce_s ); + //ExecWordCode ( astype ); + //ExecWordCode ( astypelate ); + //ExecWordCode ( coerce_u ); + //ExecWordCode ( coerce_o ); + InlineWordCode( negate_i ) + InlineWordCode( negate ) + { + auto& val = stack.A(); + idSWFScriptVar result; + switch( val.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + val.SetFloat( -val.ToFloat() ); + continue; + case idSWFScriptVar::SWF_VAR_INTEGER: + val.SetInteger( -val.ToInteger() ); + continue; + default: + common->Warning( " Tried to increment incompatible type %s", val.TypeOf() ); + } + continue; + } + InlineWordCode( increment_i ) + InlineWordCode( increment ) + { + auto& val = stack.A(); + idSWFScriptVar result; + switch( val.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + val.SetFloat( val.ToFloat() + 1.0f ); + continue; + case idSWFScriptVar::SWF_VAR_INTEGER: + val.SetInteger( val.ToInteger() + 1 ); + continue; + default: + common->Warning( " Tried to increment incompatible type %s", val.TypeOf() ); + } + continue; + } + InlineWordCode( inclocal_i ) + InlineWordCode( inclocal ) + { + auto& val = registers[bitstream.ReadEncodedU32()]; + idSWFScriptVar result; + switch( val.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + val.SetFloat( val.ToFloat() + 1.0f ); + continue; + case idSWFScriptVar::SWF_VAR_INTEGER: + val.SetInteger( val.ToInteger() + 1 ); + continue; + default: + common->Warning( " Tried to increment incompatible type %s", val.TypeOf() ); + } + continue; + } + InlineWordCode( decrement_i ) + InlineWordCode( decrement ) + { + auto& val = stack.A(); + idSWFScriptVar result; + switch( val.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + val.SetFloat( val.ToFloat() - 1.0f ); + continue; + case idSWFScriptVar::SWF_VAR_INTEGER: + val.SetInteger( val.ToInteger() + 1 ); + continue; + default: + common->Warning( " Tried to decrement incompatible type %s", val.TypeOf() ); + } + continue; + } + InlineWordCode( declocal_i ); + InlineWordCode( declocal ); + { + auto& val = registers[bitstream.ReadEncodedU32()]; + idSWFScriptVar result; + switch( val.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + val.SetFloat( val.ToFloat() - 1.0f ); + continue; + case idSWFScriptVar::SWF_VAR_INTEGER: + val.SetInteger( val.ToInteger() - 1 ); + continue; + default: + common->Warning( " Tried to decrement incompatible type %s", val.TypeOf() ); + } + continue; + } + //ExecWordCode ( typeof ); + InlineWordCode( not ) + { + stack.A().SetBool( !stack.A().ToBool() ); + continue; + } + //ExecWordCode ( bitnot ); + InlineWordCode( add_i ) + InlineWordCode( add ) + { + auto& lH = stack.B(); + auto& rH = stack.A(); + idSWFScriptVar result; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_STRING: + result.SetString( lH.ToString() + rH.ToString() ); + break; + case idSWFScriptVar::SWF_VAR_FLOAT: + result.SetFloat( lH.ToFloat() + rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + result.SetInteger( lH.ToInteger() + rH.ToInteger() ); + break; + case idSWFScriptVar::SWF_VAR_FUNCTION: + result.SetString( lH.ToString() + rH.ToString() ); + break; + default: + common->Warning( " Tried to add incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + + stack.Pop( 2 ); + stack.Alloc() = result; + continue; + } + InlineWordCode( subtract_i ) + InlineWordCode( subtract ) + { + auto& lH = stack.A(); + auto& rH = stack.B(); + idSWFScriptVar result; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + result.SetFloat( lH.ToFloat() - rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + result.SetInteger( lH.ToInteger() - rH.ToInteger() ); + break; + default: + common->Warning( " Tried to subtract incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + + stack.Pop( 2 ); + stack.Alloc() = result; + continue; + } + InlineWordCode( multiply_i ) + InlineWordCode( multiply ) + { + auto& lH = stack.A(); + auto& rH = stack.B(); + idSWFScriptVar result; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + result.SetFloat( lH.ToFloat() * rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + result.SetInteger( lH.ToInteger() * rH.ToInteger() ); + break; + default: + common->Warning( " Tried to multiply incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + + stack.Pop( 2 ); + stack.Alloc() = result; + continue; + } + InlineWordCode( divide ) + { + auto& lH = stack.A(); + auto& rH = stack.B(); + idSWFScriptVar result; + switch( lH.GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + result.SetFloat( lH.ToFloat() / rH.ToFloat() ); + break; + case idSWFScriptVar::SWF_VAR_INTEGER: + result.SetInteger( lH.ToInteger() / rH.ToInteger() ); + break; + default: + common->Warning( " Tried to divide incompatible types %s + %s", lH.TypeOf(), rH.TypeOf() ); + } + + stack.Pop( 2 ); + stack.Alloc() = result; + continue; + } + //ExecWordCode ( modulo ); + //ExecWordCode ( lshift ); + //ExecWordCode ( rshift ); + //ExecWordCode ( urshift ); + //ExecWordCode ( bitand ); + //ExecWordCode ( bitor ); + //ExecWordCode ( bitxor ); + InlineWordCode( equals ) + { + auto& lH = stack.A(); + auto& rH = stack.B(); + stack.Pop( 2 ); + stack.Alloc() = lH.AbstractEquals( rH ); + continue; + } + InlineWordCode( strictequals ) + { + auto& lH = stack.A(); + auto& rH = stack.B(); + stack.Pop( 2 ); + stack.Alloc() = lH.StrictEquals( rH ); + continue; + } + //ExecWordCode ( lessthan ); + //ExecWordCode ( lessequals ); + //ExecWordCode ( greaterthan ); + //ExecWordCode ( greaterequals ); + //ExecWordCode ( instanceof ); + //ExecWordCode ( istype ); + //ExecWordCode ( istypelate ); + //ExecWordCode ( in ); + InlineWordCode( getlocal0 ) + { + stack.Alloc() = registers[0]; + continue; + } + InlineWordCode( getlocal1 ) + { + stack.Alloc() = registers[1]; + continue; + } + InlineWordCode( getlocal2 ) + { + stack.Alloc() = registers[2]; + continue; + } + InlineWordCode( getlocal3 ) + { + stack.Alloc() = registers[3]; + continue; + } + InlineWordCode( setlocal0 ) + { + registers[0] = stack.A(); + stack.Pop( 1 ); + continue; + } + InlineWordCode( setlocal1 ) + { + registers[1] = stack.A(); + stack.Pop( 1 ); + continue; + } + InlineWordCode( setlocal2 ) + { + registers[2] = stack.A(); + stack.Pop( 1 ); + continue; + } + InlineWordCode( setlocal3 ) + { + registers[3] = stack.A(); + stack.Pop( 1 ); + continue; + } + InlineWordCode( debug ) + { + uint8 type = bitstream.ReadU8(); + uint32 index = bitstream.ReadEncodedU32(); + uint8 reg = bitstream.ReadU8(); + uint32 extra = bitstream.ReadEncodedU32(); + continue; + } + InlineWordCode( debugline ) + InlineWordCode( debugfile ) + { + uint32 nr = bitstream.ReadEncodedU32(); + continue; + } + //ExecWordCode ( bkptline ); + //ExecWordCode ( timestamp ); + //ExecWordCode ( restargc ); + //ExecWordCode ( restarg ); + //ExecWordCode ( codes ); + default: + { + const AbcOpcodeInfo* info = &opcodeInfo[opCode]; + common->DWarning( "^5Unhandled Opcode %s\n", info ? info->name : "Empty" ); +#ifdef _WIN32 + DebugBreak(); +#endif + } + + } + } + abcCallstackLevel--; + return returnValue; +} diff --git a/neo/swf/SWF_Load.cpp b/neo/swf/SWF_Load.cpp index a01945059f..2be81abd46 100644 --- a/neo/swf/SWF_Load.cpp +++ b/neo/swf/SWF_Load.cpp @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013-2015 Robert Beckebans +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -32,11 +33,15 @@ If you have questions concerning this license or the applicable additional terms #include "../renderer/Image.h" //#include "../../libs/rapidjson/include/rapidjson/document.h" -using namespace rapidjson; +using namespace rapidjson;//bleh #pragma warning(disable: 4355) // 'this' : used in base member initializer list -#define BSWF_VERSION 16 // bumped to 16 for storing atlas image dimensions for unbuffered loads + +idCVar swf_fatalVersionMismatch( "swf_fatalVersionMismatch", "0", CVAR_BOOL, "Version number mismatch results in fatal error" ); +// bumped to 16 for storing atlas image dimensions for unbuffered loads +// should be bumped to 17 for storing ABC Tag +#define BSWF_VERSION 16 #define BSWF_MAGIC ( ( 'B' << 24 ) | ( 'S' << 16 ) | ( 'W' << 8 ) | BSWF_VERSION ) // RB begin @@ -68,11 +73,14 @@ bool idSWF::LoadSWF( const char* fullpath ) return false; } - if( header.version > 9 ) + if( header.version >= 9 ) { idLib::Warning( "Unsupported version %d", header.version ); - delete rawfile; - return false; + if( swf_fatalVersionMismatch.GetBool() ) + { + delete rawfile; + return false; + } } bool compressed; @@ -614,6 +622,25 @@ bool idSWF::LoadBinary( const char* bfilename, ID_TIME_T sourceTimeStamp ) } } } + + if( f->Tell() < f->Length() ) + { + abcFile.LoadBinary( f ); + + f->ReadBig( num ); + symbolClasses.symbols.SetNum( num ); + for( int i = 0; i < symbolClasses.symbols.Num(); i++ ) + { + f->ReadBig( symbolClasses.symbols[i].tag ); + f->ReadString( symbolClasses.symbols[i].name ); + } + + if( abcFile.AbcTagData.Length() ) + { + DoABC( abcFile.AbcTagData ); + } + } + delete f; return true; @@ -790,6 +817,14 @@ void idSWF::WriteBinary( const char* bfilename ) } } } + + abcFile.WriteBinary( file ); + file->WriteBig( symbolClasses.symbols.Num() ); + for( int i = 0; i < symbolClasses.symbols.Num(); i++ ) + { + file->WriteBig( symbolClasses.symbols[i].tag ); + file->WriteString( symbolClasses.symbols[i].name ); + } } diff --git a/neo/swf/SWF_Main.cpp b/neo/swf/SWF_Main.cpp index 3d98b573a1..154da31462 100644 --- a/neo/swf/SWF_Main.cpp +++ b/neo/swf/SWF_Main.cpp @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013-2015 Robert Beckebans +Copyright (C) 2022-2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -35,13 +36,115 @@ If you have questions concerning this license or the applicable additional terms idCVar swf_loadBinary( "swf_loadBinary", "1", CVAR_INTEGER, "used to set whether to load binary swf from generated" ); +idCVar swf_printAbcObjects( "swf_printAbcObjects", "0", CVAR_INTEGER, "used to set whether to print all classes constructed from the DoAbc tag" ); +idCVar swf_enableAbcTrace( "swf_enableAbcTrace", "1", CVAR_INTEGER, "used to set whether to print all actionscript traces to the console " ); + int idSWF::mouseX = -1; int idSWF::mouseY = -1; bool idSWF::isMouseInClientArea = false; extern idCVar in_useJoystick; +idSWFScriptObject_EventDispatcherPrototype eventDispatcherScriptObjectPrototype; + +void idSWF::CreateAbcObjects( idSWFScriptObject* globals ) +{ + //2 passes. + //1. Create all classes + int idx = 0; + for( auto& classInfo : abcFile.classes ) + { + swfInstance_info& instanceInfo = abcFile.instances[idx]; + + idSWFScriptObject* tmp = idSWFScriptObject::Alloc(); + idStr& className = abcFile.constant_pool.utf8Strings[instanceInfo.name->nameIndex]; + //if these are namespace sets, concat all? + idStr fullClassName = *abcFile.constant_pool.namespaceNames[instanceInfo.name->index]; + if( !fullClassName.IsEmpty() ) + { + fullClassName += "."; + } + fullClassName += abcFile.constant_pool.utf8Strings[instanceInfo.name->nameIndex]; + idStr& superName = abcFile.constant_pool.utf8Strings[instanceInfo.super_name->nameIndex]; + //lookup prototype + if( globals->HasValidProperty( superName ) ) + { + if( superName == "Object" ) + { + idSWFScriptVar baseObjConstructor = globals->Get( "Object" ); + idSWFScriptFunction* baseObj = baseObjConstructor.GetFunction(); + tmp->SetPrototype( baseObj->GetPrototype() ); + } + else + { + tmp->SetPrototype( globals->GetObject( superName )->GetPrototype() ); + } + + } + else + { + common->Warning( " prototype %s not found for %s ", superName.c_str(), className.c_str() ); + } + + idSWFScriptFunction_Script* init = idSWFScriptFunction_Script::Alloc(); + init->SetData( classInfo.cinit ); + tmp->Set( "__initializer__", idSWFScriptVar( init ) ); + init->SetAbcFile( &abcFile ); + idSWFScriptFunction_Script* constr = idSWFScriptFunction_Script::Alloc(); + constr->SetData( instanceInfo.iinit ); + tmp->Set( "__constructor__", idSWFScriptVar( constr ) ); + constr->SetAbcFile( &abcFile ); + globals->Set( className, tmp ); + globals->Set( fullClassName, tmp ); + idx++; + //check release + init->Release(); + constr->Release(); + } + idx = 0; + + //2, create all traits + for( auto& classInfo : abcFile.classes ) + { + swfInstance_info& instanceInfo = abcFile.instances[idx]; + idStr fullClassName = *abcFile.constant_pool.namespaceNames[instanceInfo.name->index]; + if( !fullClassName.IsEmpty() ) + { + fullClassName += "."; + } + fullClassName += abcFile.constant_pool.utf8Strings[instanceInfo.name->nameIndex]; + idStr& className = abcFile.constant_pool.utf8Strings[instanceInfo.name->nameIndex]; + idSWFScriptObject* tmp = globals->GetObject( fullClassName ); + auto* target = idSWFScriptObject::Alloc(); + auto* var = tmp->GetVariable( "[" + fullClassName + "]", true ); + + for( swfTraits_info& trait : instanceInfo.traits ) + { + target->Set( abcFile.constant_pool.utf8Strings[trait.name->nameIndex], + abcFile.GetTrait( trait, globals )->value ); + } + var->value.SetObject( target ); + target->Release(); + + for( swfTraits_info& trait : classInfo.traits ) + { + tmp->Set( abcFile.constant_pool.utf8Strings[trait.name->nameIndex], + abcFile.GetTrait( trait, globals )->value + ); + } + + tmp->Set( fullClassName, target ); + + if( swf_printAbcObjects.GetBool() ) + { + tmp->PrintToConsole( className.c_str() ); + target->PrintToConsole( "[" + fullClassName + "]" ); + } + idx++; + } + //should remove all API scriptinfo's here. +} /* =================== @@ -348,16 +451,74 @@ idSWF::idSWF( const char* filename_, idSoundWorld* soundWorld_, bool exportJSON, globals = idSWFScriptObject::Alloc(); globals->Set( "_global", globals ); + SWF_NATIVE_API_OBJECT_DECLARE( _global ); + + auto* accessibilityPropertiesObj = idSWFScriptObject::Alloc(); + //accessibilityPropertiesObj->Set( "", idSWFScriptObject::Alloc() ); + auto* dispatcherObj = idSWFScriptObject::Alloc(); + dispatcherObj->SetPrototype( &eventDispatcherScriptObjectPrototype ); + + extern idSWFScriptObject_SpriteInstancePrototype spriteInstanceScriptObjectPrototype; + auto* movieclipObj = idSWFScriptObject::Alloc(); + movieclipObj->SetPrototype( &spriteInstanceScriptObjectPrototype ); + + if( spriteInstanceScriptObjectPrototype.GetPrototype() == NULL ) + { + spriteInstanceScriptObjectPrototype.SetPrototype( &eventDispatcherScriptObjectPrototype ); + } + + auto* arrayObj = idSWFScriptObject::Alloc(); + arrayObj->SetPrototype( scriptFunction_Object.GetPrototype() ); + arrayObj->MakeArray(); + arrayObj->Set( "toString", scriptFunction_ArrayToString.Bind( this ) ); + + globals->Set( "Array", arrayObj ); globals->Set( "Object", &scriptFunction_Object ); + globals->Set( "EventDispatcher", dispatcherObj ); + globals->Set( "DisplayObject", idSWFScriptObject::Alloc() ); + globals->Set( "InteractiveObject", idSWFScriptObject::Alloc() ); + globals->Set( "AccessibilityProperties", accessibilityPropertiesObj ); + globals->Set( "Dictionary", idSWFScriptObject::Alloc() ); + globals->Set( "DisplayObjectContainer", idSWFScriptObject::Alloc() ); + globals->Set( "Sprite", idSWFScriptObject::Alloc() ); + globals->Set( "DisplayObjectContainer", idSWFScriptObject::Alloc() ); + globals->Set( "MovieClip", movieclipObj ); + + CreateAbcObjects( globals ); + bool skipInitOnContruct = symbolClasses.symbols.Num() > 0; mainspriteInstance = spriteInstanceAllocator.Alloc(); + mainspriteInstance->abcFile = &abcFile; + mainspriteInstance->scriptObject = idSWFScriptObject::Alloc(); + + //stage class. + for( auto& symbol : symbolClasses.symbols ) + { + if( !symbol.tag ) + { + mainspriteInstance->name = symbol.name; + idStr objName; + mainspriteInstance->name.ExtractFileExtension( objName ); + + auto* super = globals->Get( symbol.name ).GetObject(); + auto dcopy = super->Get( "[" + symbol.name + "]" ); + if( dcopy.IsObject() ) + { + mainspriteInstance->scriptObject->DeepCopy( dcopy.GetObject() ); + } + + mainspriteInstance->scriptObject->SetPrototype( super ); + mainspriteInstance->scriptObject->Set( "root", mainspriteInstance->scriptObject ); + } + } mainspriteInstance->Init( mainsprite, NULL, 0 ); shortcutKeys = idSWFScriptObject::Alloc(); scriptFunction_shortcutKeys_clear.Bind( this ); scriptFunction_shortcutKeys_clear.Call( shortcutKeys, idSWFParmList() ); - globals->Set( "shortcutKeys", shortcutKeys ); + globals->Set( "shortcutKeys", idSWFScriptVar( shortcutKeys ) ); + SWF_NATIVE_API_OBJECT_DECLARE( shortcutKeys ); globals->Set( "deactivate", scriptFunction_deactivate.Bind( this ) ); globals->Set( "inhibitControl", scriptFunction_inhibitControl.Bind( this ) ); @@ -370,10 +531,14 @@ idSWF::idSWF( const char* filename_, idSoundWorld* soundWorld_, bool exportJSON, globals->Set( "getLocalString", scriptFunction_getLocalString.Bind( this ) ); globals->Set( "swapPS3Buttons", scriptFunction_swapPS3Buttons.Bind( this ) ); globals->Set( "_root", mainspriteInstance->scriptObject ); + globals->Set( "root", mainspriteInstance->scriptObject ); globals->Set( "strReplace", scriptFunction_strReplace.Bind( this ) ); globals->Set( "getCVarInteger", scriptFunction_getCVarInteger.Bind( this ) ); globals->Set( "setCVarInteger", scriptFunction_setCVarInteger.Bind( this ) ); + globals->Set( "registerUserMouse", scriptFunction_registerUserMouse.Bind( this ) ); + globals->Set( "Random", scriptFunction_random.Bind( this ) ); + globals->Set( "random", scriptFunction_random.Bind( this ) ); globals->Set( "acos", scriptFunction_acos.Bind( this ) ); globals->Set( "cos", scriptFunction_cos.Bind( this ) ); globals->Set( "sin", scriptFunction_sin.Bind( this ) ); @@ -385,6 +550,8 @@ idSWF::idSWF( const char* filename_, idSoundWorld* soundWorld_, bool exportJSON, globals->Set( "floor", scriptFunction_floor.Bind( this ) ); globals->Set( "ceil", scriptFunction_ceil.Bind( this ) ); globals->Set( "toUpper", scriptFunction_toUpper.Bind( this ) ); + globals->Set( "trace", scriptFunction_trace.Bind( this ) ); + globals->Set( "String", scriptFunction_String.Bind( this ) ); globals->SetNative( "platform", swfScriptVar_platform.Bind( &scriptFunction_getPlatform ) ); globals->SetNative( "blackbars", swfScriptVar_blackbars.Bind( this ) ); @@ -394,13 +561,21 @@ idSWF::idSWF( const char* filename_, idSoundWorld* soundWorld_, bool exportJSON, // Do this to touch any external references (like sounds) // But disable script warnings because many globals won't have been created yet + // THIS DOES NOT APPLY TO AS3. extern idCVar swf_debug; int debug = swf_debug.GetInteger(); swf_debug.SetInteger( 0 ); - mainspriteInstance->Run(); - mainspriteInstance->RunActions(); - mainspriteInstance->RunTo( 0 ); + + //The original impl does an initial run when contructing the stage. + //This is not possible with swf's that have the tag DoABC / run abc-bytecode + if( !skipInitOnContruct ) + { + mainspriteInstance->Run(); + mainspriteInstance->RunActions(); + mainspriteInstance->RunTo( 0 ); + mainspriteInstance->constructed = true; + } swf_debug.SetInteger( debug ); @@ -578,6 +753,11 @@ idSWF::idSWFScriptFunction_precacheSound::Call */ idSWFScriptVar idSWF::idSWFScriptFunction_precacheSound::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) { + if( !parms.Num() ) + { + return "[Undefined]"; + } + const idSoundShader* soundShader = declManager->FindSound( parms[0].ToString(), true ); return soundShader->GetName(); } @@ -739,6 +919,7 @@ idSWFScriptVar idSWF::idSWFScriptFunction_setCVarInteger::Call( idSWFScriptObjec return idSWFScriptVar(); } + /* =================== idSWF::idSWFScriptFunction_acos::Call @@ -905,6 +1086,48 @@ idSWFScriptVar idSWF::idSWFScriptFunction_ceil::Call( idSWFScriptObject* thisObj return idSWFScriptVar( idMath::Ceil( num ) ); } +/* +=================== +idSWF::idSWFScriptFunction_random::Call +=================== +*/ +idSWFScriptVar idSWF::idSWFScriptFunction_random::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) +{ + float min = 0.0f; + float max = 1.0f; + switch( parms.Num() ) + { + case 0: + return 0; + break; + case 1: + switch( parms[0].GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + return pThis->GetRandom().RandomFloat() * parms[0].ToFloat(); + case idSWFScriptVar::SWF_VAR_INTEGER: + return pThis->GetRandom().RandomInt( parms[0].ToInteger() ); + default: + return 0; + } + break; + default: + min = parms[0].ToFloat(); + max = parms[1].ToFloat(); + break; + } + + switch( parms[0].GetType() ) + { + case idSWFScriptVar::SWF_VAR_FLOAT: + return min + pThis->GetRandom().RandomFloat() * ( max - min ); + case idSWFScriptVar::SWF_VAR_INTEGER: + return ( int )min + pThis->GetRandom().RandomInt() * ( int )( max - min ); + default: + return 0; + } +} + /* ======================== idSWFScriptFunction_toUpper::Call @@ -956,7 +1179,7 @@ idSWFScriptVar idSWF::idSWFScriptFunction_shortcutKeys_clear::Call( idSWFScriptO object->Set( "MWHEELDOWN", "MWHEEL_DOWN" ); object->Set( "MWHEELUP", "MWHEEL_UP" ); object->Set( "K_TAB", "TAB" ); - + object->Set( "K_BACKSPACE", "BACKSPACE" ); // FIXME: I'm an RTARD and didn't realize the keys all have "ARROW" after them object->Set( "LEFTARROW", "LEFT" ); @@ -964,7 +1187,6 @@ idSWFScriptVar idSWF::idSWFScriptFunction_shortcutKeys_clear::Call( idSWFScriptO object->Set( "UPARROW", "UP" ); object->Set( "DOWNARROW", "DOWN" ); - return idSWFScriptVar(); } @@ -988,6 +1210,50 @@ void idSWF::idSWFScriptNativeVar_crop::Set( idSWFScriptObject* object, const idS pThis->crop = value.ToBool(); } +idSWFScriptVar idSWF::idSWFScriptFunction_trace::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) +{ + if( swf_enableAbcTrace.GetBool() ) + { + common->Printf( "^1 [%s] ^8 % s\n", thisObject->GetSprite() ? thisObject->GetSprite()->name.c_str() : "NONAME", + parms[0].ToString().c_str() ); + } + return idSWFScriptVar(); +} + +idSWFScriptVar idSWF::idSWFScriptFunction_ArrayToString::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) +{ + idStr val; + int length = thisObject->Get( "length" ).ToInteger(); + for( int i = 0; i < length; i++ ) + { + if( i ) + { + val += ","; + } + val += thisObject->Get( i ).ToString(); + } + return val; +} + +idSWFScriptVar idSWF::idSWFScriptFunction_registerUserMouse::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) +{ + common->Printf( "^1 registerUserMouse \n" ); + + return idSWFScriptVar(); +} + + +idSWFScriptVar idSWF::idSWFScriptFunction_String::Call( idSWFScriptObject* thisObject, const idSWFParmList& parms ) +{ + idStr val; + int length = parms.Num(); + for( int i = 0; i < length; i++ ) + { + val += parms[i].ToString(); + } + return val; +} + // RB begin CONSOLE_COMMAND_SHIP( exportFlash, "Export all .bswf files to the exported/swf/ folder", NULL ) { diff --git a/neo/swf/SWF_PlaceObject.cpp b/neo/swf/SWF_PlaceObject.cpp index 47d81b513b..ede8ce5f96 100644 --- a/neo/swf/SWF_PlaceObject.cpp +++ b/neo/swf/SWF_PlaceObject.cpp @@ -45,6 +45,7 @@ void idSWFSpriteInstance::PlaceObject2( idSWFBitStream& bitstream ) uint64 flags = bitstream.ReadU8(); int depth = bitstream.ReadU16(); + int characterID = -1; if( ( flags & PlaceFlagHasCharacter ) != 0 ) { @@ -146,10 +147,12 @@ void idSWFSpriteInstance::PlaceObject3( idSWFBitStream& bitstream ) uint64 flags2 = bitstream.ReadU8(); uint16 depth = bitstream.ReadU16(); + /* RB: TODO REVIEW - it has been removed compared to BFG edition if( ( flags2 & PlaceFlagHasClassName ) != 0 || ( ( ( flags2 & PlaceFlagHasImage ) != 0 ) && ( ( flags1 & PlaceFlagHasCharacter ) != 0 ) ) ) { bitstream.ReadString(); // ignored } + */ int characterID = -1; if( ( flags1 & PlaceFlagHasCharacter ) != 0 ) diff --git a/neo/swf/SWF_Render.cpp b/neo/swf/SWF_Render.cpp index a7865ee842..5bc8debf95 100644 --- a/neo/swf/SWF_Render.cpp +++ b/neo/swf/SWF_Render.cpp @@ -834,7 +834,7 @@ void idSWF::RenderShape( idRenderSystem* gui, const idSWFShape* shape, const swf uint32 packedColorM = LittleLong( PackColor( color.mul ) ); uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1 - gui->SetGLState( GLStateForRenderState( renderState ) | GLS_POLYMODE_LINE ); + gui->SetGLState( GLStateForRenderState( renderState ) ); idDrawVert* verts = gui->AllocTris( line.startVerts.Num(), line.indices.Ptr(), line.indices.Num(), white, renderState.stereoDepth ); if( verts == NULL ) diff --git a/neo/swf/SWF_ScriptFunction.cpp b/neo/swf/SWF_ScriptFunction.cpp index a8c6becb01..6cc6e6eaad 100644 --- a/neo/swf/SWF_ScriptFunction.cpp +++ b/neo/swf/SWF_ScriptFunction.cpp @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2015 Robert Beckebans +Copyright (C) 2022-2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -32,6 +33,57 @@ If you have questions concerning this license or the applicable additional terms 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." ); +idList idSwfActionScriptAPI::actionScriptAPIs; +idList idSwfActionScriptAPI::actionScriptVariableAPIs; +idList idSwfActionScriptAPI::actionScriptVariableNames; + +void idSwfActionScriptAPI::AddObjectAPI( idStr var ) +{ + if( !actionScriptVariableNames.Find( var ) ) + { + actionScriptVariableNames.Alloc() = var; + idStr& tmpStr = idSwfActionScriptAPI::actionScriptVariableAPIs.Alloc(); + tmpStr = "/** \n * Generated by RBDoom3BFG \n*/\npackage{\n\tpublic class "; + tmpStr += var; + tmpStr += +" extends Object{};}"; + } +} + +CONSOLE_COMMAND_SHIP( swf_ExportActionScriptAPI, "writes all .as files", NULL ) +{ + for( auto func : idSwfActionScriptAPI::actionScriptAPIs ) + { + idStr result; + idStr name = func->GetActionScriptAPI( result ); + if( !name.IsEmpty() ) + { + idStrStatic< MAX_OSPATH > generatedFileName = name; + generatedFileName.Insert( "ActionscriptAPI/", 0 ); + generatedFileName.SetFileExtension( "as" ); + idFileLocal file( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) ); + file->Printf( result ); + + common->Printf( "Written %s:\n ^4%s^7\n", generatedFileName.c_str(), result.c_str() ); + } + } + + for( int i = 0; i < idSwfActionScriptAPI::actionScriptVariableNames.Num(); i++ ) + { + const idStr& name = idSwfActionScriptAPI::actionScriptVariableNames[i]; + const idStr& apiStr = idSwfActionScriptAPI::actionScriptVariableAPIs[i]; + if( !name.IsEmpty() && !apiStr.IsEmpty() ) + { + idStrStatic< MAX_OSPATH > generatedFileName = name; + generatedFileName.Insert( "ActionscriptAPI/", 0 ); + generatedFileName.SetFileExtension( "as" ); + idFileLocal file( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) ); + file->Printf( apiStr ); + + common->Printf( "Written %s:\n ^4%s^7\n", generatedFileName.c_str(), apiStr.c_str() ); + } + } +} + idSWFConstantPool::idSWFConstantPool() { } @@ -137,26 +189,27 @@ idSWFScriptVar idSWFScriptFunction_Script::Call( idSWFScriptObject* thisObject, 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]; + 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(); + registers[parameters[i].reg].SetUndefined(); } locals->Set( parameters[i].name, idSWFScriptVar() ); } + stack.A().SetInteger( parms.Num() ); int preloadReg = 1; @@ -247,10 +300,25 @@ idSWFScriptVar idSWFScriptFunction_Script::Call( idSWFScriptObject* thisObject, int scopeSize = scope.Num(); scope.Append( locals ); locals->AddRef(); + idSWFScriptVar retVal; + if( methodInfo != nullptr ) + { + assert( methodInfo->body ); + auto* body = methodInfo->body; + registers[0].SetObject( thisObject ); + idSWFBitStream abcStream( body->code.Ptr(), body->codeLength, false ); + retVal = RunAbc( thisObject, stack, abcStream ); + + //-- FIXME + //-- Although some of the abc bytecode is skipped, scopes should match! + //assert(scope.Num() == scopeSize + 1); + } + else + { + retVal = Run( thisObject, stack, bitstream ); + assert( scope.Num() == scopeSize + 1 ); + } - idSWFScriptVar retVal = Run( thisObject, stack, bitstream ); - - assert( scope.Num() == scopeSize + 1 ); for( int i = scopeSize; i < scope.Num(); i++ ) { if( verify( scope[i] ) ) diff --git a/neo/swf/SWF_ScriptFunction.h b/neo/swf/SWF_ScriptFunction.h index d310f8f215..eaef35cdd1 100644 --- a/neo/swf/SWF_ScriptFunction.h +++ b/neo/swf/SWF_ScriptFunction.h @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2015 Robert Beckebans +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -28,6 +29,18 @@ If you have questions concerning this license or the applicable additional terms */ #ifndef __SWF_SCRIPTFUNCTION_H__ #define __SWF_SCRIPTFUNCTION_H__ +#include "SWF_Abc.h" + +#define SWF_NATIVE_API_OBJECT_DECLARE( x ) idSwfActionScriptAPI::AddObjectAPI(#x); + +class idSwfActionScriptAPI +{ +public: + static idList actionScriptAPIs; + static idList actionScriptVariableAPIs; + static idList actionScriptVariableNames; + static void AddObjectAPI( idStr var ); +}; /* ======================== @@ -43,6 +56,13 @@ class idSWFScriptFunction { return idSWFScriptVar(); }; // this should never be hit + + //Used to generate [returnVal].as file with public stub function to be used while compiling actionscript 3.0 + virtual const char* GetActionScriptAPI( idStr& out ) + { + return ""; + }; + virtual void AddRef() {}; virtual void Release() {}; virtual idSWFScriptObject* GetPrototype() @@ -50,6 +70,8 @@ class idSWFScriptFunction return NULL; } virtual void SetPrototype( idSWFScriptObject* _object ) { } + + }; /* @@ -185,7 +207,7 @@ idSWFScriptFunction_Script is a script function that's implemented in action scr class idSWFScriptFunction_Script : public idSWFScriptFunction { public: - idSWFScriptFunction_Script() : refCount( 1 ), flags( 0 ), data( NULL ), length( 0 ), prototype( NULL ), defaultSprite( NULL ) + idSWFScriptFunction_Script() : refCount( 1 ), flags( 0 ), data( NULL ), length( 0 ), prototype( NULL ), defaultSprite( NULL ), methodInfo( NULL ), abcFile( NULL ) { registers.SetNum( 4 ); } @@ -217,7 +239,23 @@ class idSWFScriptFunction_Script : public idSWFScriptFunction data = _data; length = _length; } + void SetData( swfMethod_info* _method ) + { + methodInfo = _method; + } + void SetAbcFile( SWF_AbcFile* _file ) + { + abcFile = _file; + } + swfMethod_info* GetMethodInfo() + { + return methodInfo; + } void SetScope( idList& scope ); + idList* GetScope() + { + return &scope; + } void SetConstants( const idSWFConstantPool& _constants ) { constants.Copy( _constants ); @@ -257,9 +295,20 @@ class idSWFScriptFunction_Script : public idSWFScriptFunction idStr CallToScript( idSWFScriptObject* thisObject, const idSWFParmList& parms, const char* filename, int characterID, int actionID ); private: - idSWFScriptVar Run( idSWFScriptObject* thisObject, idSWFStack& stack, idSWFBitStream& bitstream ); - + ////////////////////////////////////////////////////////////////////////// + //////////////////////ABC Wordcode Interpretation///////////////////////// + ////////////////////////////////////////////////////////////////////////// + void findproperty( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void findpropstrict( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void getlex( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void getscopeobject( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void pushscope( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void getlocal0( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void newclass( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + void callpropvoid( SWF_AbcFile* file, idSWFStack& stack, idSWFBitStream& bitstream ); + ////////////////////////////////////////////////////////////////////////// + idSWFScriptVar Run( idSWFScriptObject* thisObject, idSWFStack& stack, idSWFBitStream& bitstream ); struct ActionBlock { @@ -280,6 +329,9 @@ class idSWFScriptFunction_Script : public idSWFScriptFunction idStr ExportToScript( idSWFScriptObject* thisObject, idSWFStack& stack, idSWFBitStream& bitstream, const char* filename, int characterID, int actionID ); // RB end + + idSWFScriptVar RunAbc( idSWFScriptObject* thisObject, idSWFStack& stack, idSWFBitStream& bitstream ); + SWF_AbcFile* abcFile; private: std::atomic refCount; @@ -300,7 +352,9 @@ class idSWFScriptFunction_Script : public idSWFScriptFunction const char* name; uint8 reg; }; - idList< parmInfo_t, TAG_SWF > parameters; + idList< parmInfo_t > parameters; + + swfMethod_info* methodInfo; }; #endif // !__SWF_SCRIPTFUNCTION_H__ diff --git a/neo/swf/SWF_ScriptObject.cpp b/neo/swf/SWF_ScriptObject.cpp index 908dd60a18..02731ec125 100644 --- a/neo/swf/SWF_ScriptObject.cpp +++ b/neo/swf/SWF_ScriptObject.cpp @@ -59,6 +59,17 @@ idSWFScriptObject::swfNamedVar_t& idSWFScriptObject::swfNamedVar_t::operator=( c return *this; } +void idSWFScriptObject::DeepCopy( idSWFScriptObject* _object ) +{ + if( _object != NULL ) + { + for( int i = 0; i < _object->NumVariables(); i++ ) + { + Set( _object->EnumVariable( i ), _object->Get( _object->EnumVariable( i ) ) ); + } + } +} + /* ======================== idSWFScriptObject::idSWFScriptObject @@ -668,11 +679,21 @@ idSWFTextInstance* idSWFScriptObject::GetNestedText( const char* arg1, const cha idSWFScriptObject::PrintToConsole ======================== */ -void idSWFScriptObject::PrintToConsole() const +void idSWFScriptObject::PrintToConsole( const char* name ) const { + static int recursionCount = 0; + common->Printf( "------------------------------------------------------------\n" ); + if( variables.Num() > 0 ) { - idLib::Printf( "%d subelements:\n", variables.Num() ); + if( name ) + { + idLib::Printf( "[%s] %d subelements:\n", name, variables.Num() ); + } + else + { + idLib::Printf( "%d subelements:\n", variables.Num() ); + } int maxVarLength = 0; for( int i = 0; i < variables.Num(); ++i ) @@ -699,6 +720,14 @@ void idSWFScriptObject::PrintToConsole() const } else { - idLib::Printf( "No subelements\n" ); + if( name ) + { + idLib::Printf( "[%s] No subelements:\n", name ); + } + else + { + idLib::Printf( "No subelements\n" ); + } + } } diff --git a/neo/swf/SWF_ScriptObject.h b/neo/swf/SWF_ScriptObject.h index d9a12d2cd9..e18b7c571e 100644 --- a/neo/swf/SWF_ScriptObject.h +++ b/neo/swf/SWF_ScriptObject.h @@ -140,6 +140,7 @@ class idSWFScriptObject return ( objectType == SWF_OBJECT_TEXT ) ? data.text : NULL; } + void DeepCopy( idSWFScriptObject* _object ); // Also accessible via __proto__ property idSWFScriptObject* GetPrototype() { @@ -181,18 +182,8 @@ class idSWFScriptObject idSWFSpriteInstance* GetNestedSprite( const char* arg1, const char* arg2 = NULL, const char* arg3 = NULL, const char* arg4 = NULL, const char* arg5 = NULL, const char* arg6 = NULL ); idSWFTextInstance* GetNestedText( const char* arg1, const char* arg2 = NULL, const char* arg3 = NULL, const char* arg4 = NULL, const char* arg5 = NULL, const char* arg6 = NULL ); - void PrintToConsole() const; + void PrintToConsole( const char* name = nullptr ) const; -private: - std::atomic refCount; - bool noAutoDelete; - - enum swfNamedVarFlags_t - { - SWF_VAR_FLAG_NONE = 0, - SWF_VAR_FLAG_READONLY = BIT( 1 ), - SWF_VAR_FLAG_DONTENUM = BIT( 2 ) - }; struct swfNamedVar_t { swfNamedVar_t() : native( NULL ) { } @@ -206,6 +197,20 @@ class idSWFScriptObject idSWFScriptNativeVariable* native; int flags; }; + + swfNamedVar_t* GetVariable( int index, bool create ); + swfNamedVar_t* GetVariable( const char* name, bool create ); +private: + std::atomic refCount; + bool noAutoDelete; + + enum swfNamedVarFlags_t + { + SWF_VAR_FLAG_NONE = 0, + SWF_VAR_FLAG_READONLY = BIT( 1 ), + SWF_VAR_FLAG_DONTENUM = BIT( 2 ) + }; + idList< swfNamedVar_t, TAG_SWF > variables; static const int VARIABLE_HASH_BUCKETS = 16; @@ -226,9 +231,6 @@ class idSWFScriptObject idSWFSpriteInstance* sprite; // only valid if objectType == SWF_OBJECT_SPRITE idSWFTextInstance* text; // only valid if objectType == SWF_OBJECT_TEXT } data; - - swfNamedVar_t* GetVariable( int index, bool create ); - swfNamedVar_t* GetVariable( const char* name, bool create ); }; #endif // !__SWF_SCRIPTOBJECT_H__ diff --git a/neo/swf/SWF_ScriptVar.cpp b/neo/swf/SWF_ScriptVar.cpp index 3456d4f878..652e7e54c2 100644 --- a/neo/swf/SWF_ScriptVar.cpp +++ b/neo/swf/SWF_ScriptVar.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -51,6 +52,7 @@ idSWFScriptVar::idSWFScriptVar( const idSWFScriptVar& other ) { other.value.function->AddRef(); } + traitsInfo = other.traitsInfo; } /* diff --git a/neo/swf/SWF_ScriptVar.h b/neo/swf/SWF_ScriptVar.h index 41044dc1c3..0fe1f9b654 100644 --- a/neo/swf/SWF_ScriptVar.h +++ b/neo/swf/SWF_ScriptVar.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -68,40 +69,41 @@ these can be on the stack, in a script object, passed around as parameters, etc they can contain raw data (int, float), strings, functions, or objects ======================== */ +struct swfTraits_info; class idSWFScriptVar { public: - idSWFScriptVar() : type( SWF_VAR_UNDEF ) { } + idSWFScriptVar() : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { } idSWFScriptVar( const idSWFScriptVar& other ); - idSWFScriptVar( idSWFScriptObject* o ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( idSWFScriptObject* o ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetObject( o ); } - idSWFScriptVar( idStrId s ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( idStrId s ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetString( s ); } - idSWFScriptVar( const idStr& s ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( const idStr& s ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetString( s ); } - idSWFScriptVar( const char* s ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( const char* s ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetString( idStr( s ) ); } - idSWFScriptVar( float f ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( float f ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetFloat( f ); } - idSWFScriptVar( bool b ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( bool b ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetBool( b ); } - idSWFScriptVar( int32 i ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( int32 i ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetInteger( i ); } - idSWFScriptVar( idSWFScriptFunction* nf ) : type( SWF_VAR_UNDEF ) + idSWFScriptVar( idSWFScriptFunction* nf ) : traitsInfo( NULL ), type( SWF_VAR_UNDEF ) { SetFunction( nf ); } @@ -272,6 +274,7 @@ class idSWFScriptVar return type; } + const swfTraits_info* traitsInfo; private: void Free(); swfScriptVarType type; diff --git a/neo/swf/SWF_ShapeParser.cpp b/neo/swf/SWF_ShapeParser.cpp index a6266236d0..a0d718396a 100644 --- a/neo/swf/SWF_ShapeParser.cpp +++ b/neo/swf/SWF_ShapeParser.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -31,6 +32,81 @@ If you have questions concerning this license or the applicable additional terms #pragma warning( disable: 4189 ) // local variable is initialized but not referenced +// HarrievG begin +void idSWFShapeParser::MakeCap( idSWFShapeParser::swfSPDrawLine_t& spld, idSWFShapeDrawLine& ld, swfSPMorphEdge_t& edge, bool start ) +{ + //figure out what the orientation of the cap is. + uint16 v0StartIndex = edge.start.v0; + uint16 v1StartIndex = ( edge.start.cp == 0xFFFF ) ? edge.start.v1 : edge.start.cp; + + uint16 v0EndIndex = ( edge.end.cp == 0xFFFF ) ? edge.end.v0 : edge.end.cp; + uint16 v1EndIndex = edge.end.v1; + + uint16 v0 = v0StartIndex; + uint16 v1 = v1StartIndex; + + if( !start ) + { + v0 = v0EndIndex; + v1 = v1EndIndex; + } + + idVec2 up = ( verts[v0] - verts[v1] ); + idVec2 down = ( verts[v1] - verts[v0] ); + idVec2 cross = idVec2( down.y, -down.x ); + idVec2 crossUp = idVec2( up.y, -up.x ); + + uint8 vertIndex; + if( !start ) + { + vertIndex = v1; + } + else + { + vertIndex = v0; + cross = crossUp; + } + + float scale = ( float )( 1.0f / 20 ) * ( ld.style.startWidth * 0.5 ) ; + + //vert + int capCenterIdx = ld.startVerts.AddUnique( verts[vertIndex] ); + int pointCnt; + float x, z; + swfMatrix_t matrix; + float s, c; + idVec2 xup( 1.0f, 0.0f ); + float angle = idMath::ATan( xup.y - cross.y, xup.x - cross.x ) + idMath::PI; + + idMath::SinCos( angle, s, c ); + + matrix.xx = c * scale; + matrix.yx = s * scale; + matrix.xy = -s * scale; + matrix.yy = c * scale; + int A, B; + for( float w = -10; w <= 180 + 10; w += 10 ) + { + idMath::SinCos( DEG2RAD( w ), z, x ); + + ld.indices.Append( capCenterIdx ); + A = ld.startVerts.AddUnique( matrix.Transform( idVec2( x, z ) ) + ld.startVerts[capCenterIdx] ); + + if( w >= 0.0f ) + { + ld.indices.Append( B ); + ld.indices.Append( A ); + ld.indices.Append( capCenterIdx ); + } + + ld.indices.Append( A ); + + idMath::SinCos( DEG2RAD( w ), z, x ); + B = ld.startVerts.AddUnique( matrix.Transform( idVec2( x , z ) ) + ld.startVerts[capCenterIdx] ); + ld.indices.Append( B ); + } +} + /* ======================== idSWFShapeParser::ParseShape @@ -57,28 +133,73 @@ void idSWFShapeParser::Parse( idSWFBitStream& bitstream, idSWFShape& shape, int ParseShapes( bitstream, NULL, false ); TriangulateSoup( shape ); + //generate triangle mesh shape.lineDraws.SetNum( lineDraws.Num() ); + int last = 0; for( int i = 0; i < lineDraws.Num(); i++ ) { + idSWFShapeDrawLine& ld = shape.lineDraws[i]; swfSPDrawLine_t& spld = lineDraws[i]; ld.style = spld.style; + float startWidth = ld.style.startWidth; + float endWidth = ld.style.endWidth; ld.indices.SetNum( spld.edges.Num() * 3 ); ld.indices.SetNum( 0 ); + + //edge list for( int e = 0; e < spld.edges.Num(); e++ ) { - int v0 = ld.startVerts.AddUnique( verts[ spld.edges[e].start.v0 ] ); - ld.indices.Append( v0 ); - ld.indices.Append( v0 ); + //startcap + //only supporting round + if( ld.style.startCapStyle == 0 ) + { + MakeCap( spld, ld, spld.edges[e], true ); + } + + uint16 v0StartIndex = spld.edges[e].start.v0; + uint16 v1StartIndex = ( spld.edges[e].start.cp == 0xFFFF ) ? spld.edges[e].start.v1 : spld.edges[e].start.cp; + + uint16 v0EndIndex = ( spld.edges[e].end.cp == 0xFFFF ) ? spld.edges[e].end.v0 : spld.edges[e].end.cp; + uint16 v1EndIndex = spld.edges[e].end.v1; + + uint16 v0 = v0StartIndex; + uint16 v1 = v1StartIndex; + + idVec2 up = ( verts[v0] - verts[v1] ); + idVec2 down = ( verts[v1] - verts[v0] ); + idVec2 cross = idVec2( down.y, -down.x ); + idVec2 crossUp = idVec2( up.y, -up.x ); + + float scale = ( float )( 1.0f / 20 ) * ( ld.style.startWidth * 0.5 ); + + idVec2 offSetA = crossUp * ( ( ( 1.0f / 20 ) / down.Length() ) * ld.style.startWidth ) * 0.5f ; + idVec2 offSetB = cross * ( ( ( 1.0f / 20 ) / down.Length() ) * ld.style.startWidth ) * 0.5f; + + v0 = ld.startVerts.AddUnique( verts[ spld.edges[e].start.v0 ] - offSetB ); + int v0x = ld.startVerts.AddUnique( verts[ spld.edges[e].start.v0 ] - offSetA ); + + //straight line + if( spld.edges[e].start.cp == 0xFFFF ) + { + ld.indices.Append( v0 ); + ld.indices.Append( v0x ); + } // Rather then tesselating curves at run time, we do them once here by inserting a vert every 10 units // It may not wind up being 10 actual pixels when rendered because the shape may have scaling applied to it if( spld.edges[e].start.cp != 0xFFFF ) { assert( spld.edges[e].end.cp != 0xFFFF ); - float length1 = ( verts[ spld.edges[e].start.v0 ] - verts[ spld.edges[e].start.v1 ] ).Length(); - float length2 = ( verts[ spld.edges[e].end.v0 ] - verts[ spld.edges[e].end.v1 ] ).Length(); + float length1 = ( verts[ spld.edges[e].start.v0 ] - verts[ spld.edges[e].start.v1 ] ).Length() * scale; + float length2 = ( verts[ spld.edges[e].end.v0 ] - verts[ spld.edges[e].end.v1 ] ).Length() * scale; int numPoints = 1 + idMath::Ftoi( Max( length1, length2 ) / 10.0f ); + int lastV1; + int last; + v0 = ld.startVerts.AddUnique( verts[spld.edges[e].end.v0] + offSetB ); + v0x = ld.startVerts.AddUnique( verts[spld.edges[e].end.v0] + offSetA ); + ld.indices.Append( v0 ); + for( int ti = 0; ti < numPoints; ti++ ) { float t0 = ( ti + 1 ) / ( ( float ) numPoints + 1.0f ); @@ -91,16 +212,103 @@ void idSWFShapeParser::Parse( idSWFBitStream& bitstream, idSWFShape& shape, int p1 += c2 * verts[ spld.edges[e].start.cp ]; p1 += c3 * verts[ spld.edges[e].start.v1 ]; - int v1 = ld.startVerts.AddUnique( p1 ); - ld.indices.Append( v1 ); - ld.indices.Append( v1 ); + t0 = ( ti + 1 + 1 ) / ( ( float ) numPoints + 1.0f ); + t1 = ( 1.0f - t0 ); + c1 = t1 * t1; + c2 = t0 * t1 * 2.0f; + c3 = t0 * t0; + + idVec2 p2 = c1 * verts[spld.edges[e].start.v0]; + p2 += c2 * verts[spld.edges[e].start.cp]; + p2 += c3 * verts[spld.edges[e].start.v1]; + + idVec2 iup = p1 - p2; + idVec2 idown = p2 - p1; + idVec2 icross = idVec2( idown.y, -idown.x ); + idVec2 icrossUp = idVec2( iup.y, -iup.x ); + idVec2 ioffSetA = icrossUp * ( ( ( 1.0f / 20 ) / idown.Length() ) * ld.style.startWidth ) * 0.5f; + idVec2 ioffSetB = icross * ( ( ( 1.0f / 20 ) / idown.Length() ) * ld.style.startWidth ) * 0.5f; + + v1 = ld.startVerts.AddUnique( p1 + ioffSetB ); + int v2 = ld.startVerts.AddUnique( p1 + ioffSetA ); + + if( ti > 0 ) + { + ld.indices.Append( v2 ); + ld.indices.Append( lastV1 ); + } + ld.indices.Append( v1 ); + ld.indices.Append( v2 ); + if( ti > 0 ) + { + ld.indices.Append( v2 ); + ld.indices.Append( v1 ); + } + else + { + ld.indices.Append( v2 ); + } + + if( ti == numPoints - 1 ) + { + ld.indices.Append( v2 ); + } + + if( ti == 0 ) + { + + ld.indices.Append( v0 ); + ld.indices.Append( v0x ); + ld.indices.Append( v1 ); + ld.indices.Append( v2 ); + } + + lastV1 = v1; + last = v2; } + + v0 = v0EndIndex; + v1 = v1EndIndex; + + up = ( verts[v0] - verts[v1] ); + down = ( verts[v1] - verts[v0] ); + cross = idVec2( down.y, -down.x ); + crossUp = idVec2( up.y, -up.x ); + + offSetA = crossUp * ( ( ( 1.0f / 20 ) / down.Length() ) * ld.style.startWidth ) * 0.5f; + offSetB = cross * ( ( ( 1.0f / 20 ) / down.Length() ) * ld.style.startWidth ) * 0.5f; + + + ld.indices.Append( lastV1 ); + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] + offSetA ) ); + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] + offSetB ) ); + + ld.indices.Append( last ); + ld.indices.Append( lastV1 ); + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] + offSetA ) ); + } + else + { + //straight line + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] - offSetB ) ); + + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] - offSetA ) ); + ld.indices.Append( v0x ); + ld.indices.Append( ld.startVerts.AddUnique( verts[spld.edges[e].end.v1] - offSetB ) ); + } + + last = ld.indices.Num() - 1; + + //endcap + if( ld.style.endCapStyle == 0 ) + { + MakeCap( spld, ld, spld.edges[e], false ); } - ld.indices.Append( ld.startVerts.AddUnique( verts[ spld.edges[e].start.v1 ] ) ); } } } +// HarrievG end /* ======================== @@ -600,7 +808,7 @@ void idSWFShapeParser::MakeLoops() } if( shape == -1 ) { - idLib::Warning( "idSWFShapeParser: Hole not in a shape" ); + idLib::Warning( "idSWFShapeParser: Hole not in a shape, try to smoothen or straighten the erroneous shape" ); fill.loops.RemoveIndexFast( hole ); continue; } @@ -1003,12 +1211,11 @@ void idSWFShapeParser::ReadFillStyle( idSWFBitStream& bitstream ) { lineStyleCount = bitstream.ReadU16(); } - + int idx = lineDraws.Num(); lineDraws.SetNum( lineDraws.Num() + lineStyleCount ); - lineDraws.SetNum( 0 ); for( int i = 0; i < lineStyleCount; i++ ) { - swfLineStyle_t& lineStyle = lineDraws.Alloc().style; + swfLineStyle_t& lineStyle = lineDraws[idx + i].style; lineStyle.startWidth = bitstream.ReadU16(); if( lineStyle2 ) { @@ -1023,6 +1230,13 @@ void idSWFShapeParser::ReadFillStyle( idSWFBitStream& bitstream ) uint8 reserved = bitstream.ReadU( 5 ); bool noClose = bitstream.ReadBool(); uint8 endCapStyle = bitstream.ReadU( 2 ); + lineStyle.endCapStyle = swfLineStyle_t::capStyle( endCapStyle ); + lineStyle.startCapStyle = swfLineStyle_t::capStyle( startCapStyle ); + + if( noClose ) + { + idLib::Warning( "noClose was set but Ignored." ); + } if( joinStyle == 2 ) { uint16 miterLimitFactor = bitstream.ReadU16(); diff --git a/neo/swf/SWF_ShapeParser.h b/neo/swf/SWF_ShapeParser.h index 3ce5a00358..6a11d5090a 100644 --- a/neo/swf/SWF_ShapeParser.h +++ b/neo/swf/SWF_ShapeParser.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -89,7 +90,7 @@ class idSWFShapeParser void TriangulateSoup( idSWFFontGlyph& shape ); int FindEarVert( const swfSPLineLoop_t& loop ); void AddUniqueVert( idSWFShapeDrawFill& drawFill, const idVec2& start, const idVec2& end ); - + void MakeCap( swfSPDrawLine_t& spld, idSWFShapeDrawLine& ld , swfSPMorphEdge_t& edge, bool start ); }; #endif // !__SWF_SHAPEPARSER_H__ diff --git a/neo/swf/SWF_SpriteInstance.cpp b/neo/swf/SWF_SpriteInstance.cpp index 5caaf0fd67..cc2f11e8dc 100644 --- a/neo/swf/SWF_SpriteInstance.cpp +++ b/neo/swf/SWF_SpriteInstance.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -45,6 +46,7 @@ idSWFSpriteInstance::idSWFSpriteInstance() : sprite( NULL ), parent( NULL ), depth( 0 ), + lastFrame( 0 ), itemIndex( 0 ), materialOverride( NULL ), materialWidth( 0 ), @@ -54,7 +56,10 @@ idSWFSpriteInstance::idSWFSpriteInstance() : moveToXScale( 1.0f ), moveToYScale( 1.0f ), moveToSpeed( 1.0f ), - stereoDepth( 0 ) + stereoDepth( 0 ), + actionScript( NULL ), + scriptObject( NULL ), + constructed( false ) { } @@ -71,18 +76,25 @@ void idSWFSpriteInstance::Init( idSWFSprite* _sprite, idSWFSpriteInstance* _pare frameCount = sprite->frameCount; - scriptObject = idSWFScriptObject::Alloc(); - scriptObject->SetPrototype( &spriteInstanceScriptObjectPrototype ); + if( !scriptObject ) + { + scriptObject = idSWFScriptObject::Alloc(); + scriptObject->SetPrototype( &spriteInstanceScriptObjectPrototype ); + + } scriptObject->SetSprite( this ); firstRun = true; - actionScript = idSWFScriptFunction_Script::Alloc(); - - idList scope; - scope.Append( sprite->swf->globals ); - scope.Append( scriptObject ); - actionScript->SetScope( scope ); + //this is already set if this is the main timeline + if( !actionScript ) + { + actionScript = idSWFScriptFunction_Script::Alloc(); + idList scope; + scope.Append( sprite->swf->globals ); + scope.Append( scriptObject ); + actionScript->SetScope( scope ); + } actionScript->SetDefaultSprite( this ); for( int i = 0; i < sprite->doInitActions.Num(); i++ ) @@ -122,8 +134,15 @@ void idSWFSpriteInstance::FreeDisplayList() { for( int i = 0; i < displayList.Num(); i++ ) { - sprite->swf->spriteInstanceAllocator.Free( displayList[i].spriteInstance ); - sprite->swf->textInstanceAllocator.Free( displayList[i].textInstance ); + if( displayList[i].spriteInstance ) + { + sprite->swf->spriteInstanceAllocator.Free( displayList[i].spriteInstance ); + } + + if( displayList[i].textInstance ) + { + sprite->swf->textInstanceAllocator.Free( displayList[i].textInstance ); + } } displayList.SetNum( 0 ); // not calling Clear() so we don't continuously re-allocate memory currentFrame = 0; @@ -182,17 +201,64 @@ swfDisplayEntry_t* idSWFSpriteInstance::AddDisplayEntry( int depth, int characte idSWFDictionaryEntry* dictEntry = sprite->swf->FindDictionaryEntry( characterID ); if( dictEntry != NULL ) { + if( !dictEntry->resolved ) + { + for( auto& symbol : sprite->swf->symbolClasses.symbols ) + { + if( symbol.tag == characterID ) + { + dictEntry->scriptClass = sprite->swf->globals->Get( symbol.name ); + dictEntry->name = &symbol.name; + break; + } + } + dictEntry->resolved = true;//dictEntry->scriptClass != nullptr; + } + if( dictEntry->type == SWF_DICT_SPRITE ) { display.spriteInstance = sprite->swf->spriteInstanceAllocator.Alloc(); + display.spriteInstance->abcFile = this->abcFile; + if( dictEntry->scriptClass.IsValid() ) + { + display.spriteInstance->scriptObject = idSWFScriptObject::Alloc(); + auto* super = dictEntry->scriptClass.GetObject(); + + auto dcopy = super->Get( *dictEntry->name ); + if( dcopy.IsObject() ) + { + display.spriteInstance->scriptObject->DeepCopy( dcopy.GetObject() ); + } + + display.spriteInstance->scriptObject->SetPrototype( super ); + } + display.spriteInstance->Init( dictEntry->sprite, this, depth ); display.spriteInstance->RunTo( 1 ); } else if( dictEntry->type == SWF_DICT_EDITTEXT ) { display.textInstance = sprite->swf->textInstanceAllocator.Alloc(); + + if( dictEntry->scriptClass.IsValid() ) + { + auto* super = dictEntry->scriptClass.GetObject(); + auto dcopy = super->Get( *dictEntry->name ); + if( dcopy.IsObject() ) + { + display.textInstance->scriptObject.DeepCopy( dcopy.GetObject() ); + } + + display.textInstance->scriptObject.SetPrototype( super ); + } + display.textInstance->Init( dictEntry->edittext, sprite->GetSWF() ); } + else if( dictEntry->type == SWF_DICT_TEXT ) + { + //display.textInstance = sprite->swf->textInstanceAllocator.Alloc(); + //display.textInstance->Init( dictEntry->text, sprite->GetSWF() ); + } } return &display; } @@ -207,8 +273,14 @@ void idSWFSpriteInstance::RemoveDisplayEntry( int depth ) swfDisplayEntry_t* entry = FindDisplayEntry( depth ); if( entry != NULL ) { - sprite->swf->spriteInstanceAllocator.Free( entry->spriteInstance ); - sprite->swf->textInstanceAllocator.Free( entry->textInstance ); + if( entry->spriteInstance ) + { + sprite->swf->spriteInstanceAllocator.Free( entry->spriteInstance ); + } + if( entry->textInstance ) + { + sprite->swf->textInstanceAllocator.Free( entry->textInstance ); + } displayList.RemoveIndex( displayList.IndexOf( entry ) ); } } @@ -312,14 +384,73 @@ bool idSWFSpriteInstance::RunActions() if( !isVisible ) { actions.SetNum( 0 ); + functionActions.SetNum( 0 ); return false; } - if( firstRun && scriptObject->HasProperty( "onLoad" ) ) + if( !constructed ) + { + if( scriptObject->HasProperty( "__constructor__" ) ) + { + //common->DPrintf( "Calling constructor for %s%\n", name.c_str() ); + idSWFScriptVar instanceInit = scriptObject->Get( "__constructor__" ); + idSWFScriptFunction_Script* constructor = ( idSWFScriptFunction_Script* )instanceInit.GetFunction(); + if( !actionScript->GetScope()->Num() ) + { + actionScript->GetScope()->Alloc() = sprite->swf->globals; + } + actionScript->SetData( constructor->GetMethodInfo() ); + actionScript->SetAbcFile( abcFile ); + actionScript->Call( scriptObject, idSWFParmList() ); + constructed = true; + } + } + + if( firstRun && ( scriptObject->HasProperty( "__eventDispatcher__" ) || scriptObject->HasProperty( "onLoad" ) ) ) { firstRun = false; idSWFScriptVar onLoad = scriptObject->Get( "onLoad" ); - onLoad.GetFunction()->Call( scriptObject, idSWFParmList() ); + if( onLoad.IsValid() ) + { + if( !( ( idSWFScriptFunction_Script* )onLoad.GetFunction() )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* )onLoad.GetFunction() )->GetScope()->Append( sprite->swf->globals ); + } + + onLoad.GetFunction()->Call( scriptObject, idSWFParmList() ); + } + + if( scriptObject->HasProperty( "__eventDispatcher__" ) ) + { + idSWFScriptObject* eventDispatcher = scriptObject->Get( "__eventDispatcher__" ).GetObject(); + idSWFScriptVar var = eventDispatcher->Get( "addedToStage" ); + if( var.IsFunction() ) + { + + idSWFScriptObject* eventObj = sprite->swf->globals + ->Get( "EventDispatcher" ).GetObject() + ->Get( "Event" ).GetObject() + ->Get( "[Event]" ).GetObject(); + + idSWFScriptVar eventParm; + eventParm.SetObject( idSWFScriptObject::Alloc() ); + eventParm.GetObject()->DeepCopy( eventObj ); + + idSWFScriptVar eventArg; + eventArg.SetObject( idSWFScriptObject::Alloc() ); + eventArg.GetObject()->Set( "Event", eventParm ); + + idSWFParmList parms; + parms.Append( eventArg ); + + idSWFScriptFunction_Script* eventFunc = ( idSWFScriptFunction_Script* )var.GetFunction(); + + actionScript->SetData( eventFunc->GetMethodInfo() ); + actionScript->SetAbcFile( abcFile ); + actionScript->Call( scriptObject, parms ); + parms.Clear(); + } + } } if( onEnterFrame.IsFunction() ) @@ -334,6 +465,13 @@ bool idSWFSpriteInstance::RunActions() } actions.SetNum( 0 ); + for( int i = 0; i < functionActions.Num(); i++ ) + { + actionScript->SetData( ( ( idSWFScriptFunction_Script* )functionActions[i] )->GetMethodInfo() ); + actionScript->Call( scriptObject, idSWFParmList() ); + } + functionActions.SetNum( 0 ); + for( int i = 0; i < displayList.Num(); i++ ) { if( displayList[i].spriteInstance != NULL ) @@ -341,6 +479,7 @@ bool idSWFSpriteInstance::RunActions() Prefetch( displayList[i].spriteInstance, 0 ); } } + for( int i = 0; i < displayList.Num(); i++ ) { if( displayList[i].spriteInstance != NULL ) @@ -434,7 +573,8 @@ void idSWFSpriteInstance::RunTo( int targetFrame ) for( uint32 c = sprite->frameOffsets[ currentFrame ]; c < sprite->frameOffsets[ targetFrame ]; c++ ) { idSWFSprite::swfSpriteCommand_t& command = sprite->commands[ c ]; - if( command.tag == Tag_DoAction && c < firstActionCommand ) + + if( command.tag == Tag_DoAction && c < firstActionCommand ) { // Skip DoAction up to the firstActionCommand // This is to properly support skipping to a specific frame @@ -458,6 +598,22 @@ void idSWFSpriteInstance::RunTo( int targetFrame ) } } + idStr frameId = idStr( "frame" ) + targetFrame ; + idSWFScriptObject* obj = scriptObject; + if( obj && obj->HasValidProperty( frameId.c_str() ) ) + { + idSWFScriptVar frameFunc = obj->Get( frameId.c_str() ); + if( frameFunc.IsFunction() ) + { + idSWFScriptFunction* funcPtr = frameFunc.GetFunction(); + if( !( ( idSWFScriptFunction_Script* )funcPtr )->GetScope()->Num() ) + { + ( ( idSWFScriptFunction_Script* )funcPtr )->SetScope( *actionScript->GetScope() ); + } + DoAction( funcPtr ); + } + } + currentFrame = targetFrame; } @@ -475,6 +631,11 @@ void idSWFSpriteInstance::DoAction( idSWFBitStream& bitstream ) #endif } +void idSWFSpriteInstance::DoAction( idSWFScriptFunction* function ) +{ + functionActions.Alloc() = function; +} + /* ======================== idSWFSpriteInstance::FindChildSprite @@ -1026,6 +1187,7 @@ idSWFScriptObject_SpriteInstancePrototype idSWFScriptObject_SpriteInstancePrototype::idSWFScriptObject_SpriteInstancePrototype() { + SWF_SPRITE_FUNCTION_SET( addFrameScript ); SWF_SPRITE_FUNCTION_SET( duplicateMovieClip ); SWF_SPRITE_FUNCTION_SET( gotoAndPlay ); SWF_SPRITE_FUNCTION_SET( gotoAndStop ); @@ -1046,6 +1208,7 @@ idSWFScriptObject_SpriteInstancePrototype::idSWFScriptObject_SpriteInstanceProto SWF_SPRITE_NATIVE_VAR_SET( _height ); SWF_SPRITE_NATIVE_VAR_SET( _rotation ); SWF_SPRITE_NATIVE_VAR_SET( _name ); + SWF_SPRITE_NATIVE_VAR_SET( name ); SWF_SPRITE_NATIVE_VAR_SET( _currentframe ); SWF_SPRITE_NATIVE_VAR_SET( _totalframes ); SWF_SPRITE_NATIVE_VAR_SET( _target ); @@ -1111,6 +1274,15 @@ SWF_SPRITE_NATIVE_VAR_DEFINE_GET( _height ) } SWF_SPRITE_NATIVE_VAR_DEFINE_SET( _height ) { } + +SWF_SPRITE_FUNCTION_DEFINE( addFrameScript ) +{ + SWF_SPRITE_PTHIS_FUNC( "addFrameScript" ); + // Frame/timeline actionScript code is added implicitly + return idSWFScriptVar(); +} + + SWF_SPRITE_FUNCTION_DEFINE( duplicateMovieClip ) { SWF_SPRITE_PTHIS_FUNC( "duplicateMovieClip" ); @@ -1157,8 +1329,9 @@ SWF_SPRITE_FUNCTION_DEFINE( gotoAndPlay ) if( parms.Num() > 0 ) { + uint32 targetFrame = pThis->FindFrame( parms[0].ToString() ); pThis->actions.Clear(); - pThis->RunTo( pThis->FindFrame( parms[0].ToString() ) ); + pThis->RunTo( targetFrame ); pThis->Play(); } else @@ -1499,6 +1672,14 @@ SWF_SPRITE_NATIVE_VAR_DEFINE_GET( _name ) return pThis->name.c_str(); } +// HarrievG: I'm not sure how much of the 'legacy' underscore prefixed functions need to be changed for as3.0. +//if more is needed, we should change the macro to work for both legacy AS and 3.0 +SWF_SPRITE_NATIVE_VAR_DEFINE_GET( name ) +{ + SWF_SPRITE_PTHIS_GET( "name" ); + return pThis->name.c_str(); +} + SWF_SPRITE_NATIVE_VAR_DEFINE_GET( _currentframe ) { SWF_SPRITE_PTHIS_GET( "_currentframe" ); diff --git a/neo/swf/SWF_SpriteInstance.h b/neo/swf/SWF_SpriteInstance.h index 1fbb3098f9..123c04d614 100644 --- a/neo/swf/SWF_SpriteInstance.h +++ b/neo/swf/SWF_SpriteInstance.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -147,9 +148,11 @@ class idSWFSpriteInstance bool childrenRunning; bool firstRun; + bool constructed; // currentFrame is the frame number currently in the displayList // we use 1 based frame numbers because currentFrame = 0 means nothing is in the display list // it's also convenient because Flash also uses 1 based frame numbers + uint16 lastFrame; uint16 currentFrame; uint16 frameCount; @@ -184,6 +187,7 @@ class idSWFSpriteInstance idList< swfDisplayEntry_t, TAG_SWF > displayList; swfDisplayEntry_t* FindDisplayEntry( int depth ); + SWF_AbcFile* abcFile; // name of this sprite instance idStr name; @@ -193,6 +197,7 @@ class idSWFSpriteInstance uint32 dataLength; }; idList< swfAction_t, TAG_SWF > actions; + idList< idSWFScriptFunction*, TAG_SWF > functionActions; idSWFScriptFunction_Script* actionScript; @@ -232,6 +237,7 @@ class idSWFSpriteInstance void SwapDepths( int depth1, int depth2 ); void DoAction( idSWFBitStream& bitstream ); + void DoAction( idSWFScriptFunction* function ); idSWFSpriteInstance* FindChildSprite( const char* childName ); idSWFSpriteInstance* ResolveTarget( const char* targetName ); @@ -258,6 +264,7 @@ class idSWFScriptObject_SpriteInstancePrototype : public idSWFScriptObject idSWFScriptVar Call( idSWFScriptObject * thisObject, const idSWFParmList & parms ); \ } scriptFunction_##x + SWF_SPRITE_FUNCTION_DECLARE( addFrameScript ); SWF_SPRITE_FUNCTION_DECLARE( duplicateMovieClip ); SWF_SPRITE_FUNCTION_DECLARE( gotoAndPlay ); SWF_SPRITE_FUNCTION_DECLARE( gotoAndStop ); @@ -279,6 +286,7 @@ class idSWFScriptObject_SpriteInstancePrototype : public idSWFScriptObject SWF_NATIVE_VAR_DECLARE( _rotation ); SWF_NATIVE_VAR_DECLARE_READONLY( _name ); + SWF_NATIVE_VAR_DECLARE_READONLY( name ); SWF_NATIVE_VAR_DECLARE_READONLY( _currentframe ); SWF_NATIVE_VAR_DECLARE_READONLY( _totalframes ); SWF_NATIVE_VAR_DECLARE_READONLY( _target ); diff --git a/neo/swf/SWF_Sprites.cpp b/neo/swf/SWF_Sprites.cpp index 563ad8913a..fca27a3411 100644 --- a/neo/swf/SWF_Sprites.cpp +++ b/neo/swf/SWF_Sprites.cpp @@ -129,6 +129,8 @@ void idSWFSprite::Load( idSWFBitStream& bitstream, bool parseDictionary ) HANDLE_SWF_TAG( DefineText ); HANDLE_SWF_TAG( DefineText2 ); HANDLE_SWF_TAG( DefineEditText ); + HANDLE_SWF_TAG( DoABC ); + HANDLE_SWF_TAG( SymbolClass ); #undef HANDLE_SWF_TAG default: handled = false; diff --git a/neo/swf/SWF_TextInstance.cpp b/neo/swf/SWF_TextInstance.cpp index 92fc00dc05..a15794c0ac 100644 --- a/neo/swf/SWF_TextInstance.cpp +++ b/neo/swf/SWF_TextInstance.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -68,6 +69,81 @@ idSWFTextInstance::~idSWFTextInstance() subtitleTimingInfo.Clear(); } +// HarrievG begin +void idSWFTextInstance::Init( idSWFText* _text, idSWF* _swf ) +{ + editText = nullptr; + staticText = _text; + swf = _swf; + + text = "staticText";//idLocalization::GetString( staticText->textRecords[0]. ); + + lengthCalculated = false; + //if ( editText ) + //{ + // variable = editText->variable; + // color = editText->color; + //} + visible = true; + + selectionStart = -1; + selectionEnd = -1; + + scroll = 0; + scrollTime = 0; + maxscroll = 0; + maxLines = 0; + linespacing = 0; + glyphScale = 1.0f; + + shiftHeld = false; + tooltip = false; + renderMode = SWF_TEXT_RENDER_NORMAL; + generatingText = false; + triggerGenerate = false; + rndSpotsVisible = 0; + textSpotsVisible = 0; + startRndTime = 0; + charMultiplier = 0; + prevReplaceIndex = 0; + scrollUpdate = false; + ignoreColor = false; + + isSubtitle = false; + subLength = 0; + subAlign = 0; + subUpdating = false; + subCharStartIndex = 0; + subNextStartIndex = 0; + subCharEndIndex = 0; + subDisplayTime = 0; + subStartTime = -1; + subSourceID = -1; + subNeedsSwitch = false; + subForceKill = false; + subKillTimeDelay = 0; + subSwitchTime = 0; + subLastWordIndex = 0; + subPrevLastWordIndex = 0; + subInitialLine = true; + + textLength = 0; + + inputTextStartChar = 0; + + renderDelay = swf_textRndLetterDelay.GetInteger(); + needsSoundUpdate = false; + useDropShadow = false; + useStroke = false; + strokeStrength = 1.0f; + strokeWeight = swf_textStrokeSize.GetFloat(); + + scriptObject.SetPrototype( &textInstanceScriptObjectPrototype ); + scriptObject.SetText( this ); + scriptObject.SetNoAutoDelete( true ); +} +// HarrievG end + /* ======================== idSWFTextInstance::Init @@ -1028,6 +1104,7 @@ idSWFScriptObject_TextInstancePrototype::idSWFScriptObject_TextInstancePrototype SWF_TEXT_NATIVE_VAR_SET( text ); SWF_TEXT_NATIVE_VAR_SET( _textLength ); // only works on single lines of text not multiline + SWF_TEXT_NATIVE_VAR_SET( length ); // only works on single lines of text not multiline SWF_TEXT_NATIVE_VAR_SET( autoSize ); SWF_TEXT_NATIVE_VAR_SET( dropShadow ); SWF_TEXT_NATIVE_VAR_SET( _stroke ); @@ -1252,6 +1329,11 @@ SWF_TEXT_NATIVE_VAR_DEFINE_GET( _textLength ) SWF_TEXT_PTHIS_GET( "_textLength" ); return pThis->GetTextLength(); } +SWF_TEXT_NATIVE_VAR_DEFINE_GET( length ) +{ + SWF_TEXT_PTHIS_GET( "length" ); + return pThis->GetTextLength(); +} SWF_TEXT_NATIVE_VAR_DEFINE_SET( mode ) { diff --git a/neo/swf/SWF_TextInstance.h b/neo/swf/SWF_TextInstance.h index 8e61fc32e7..5257431fe9 100644 --- a/neo/swf/SWF_TextInstance.h +++ b/neo/swf/SWF_TextInstance.h @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -47,7 +48,8 @@ class idSWFTextInstance idSWFTextInstance(); ~idSWFTextInstance(); - void Init( idSWFEditText* editText, idSWF* _swf ); + void Init( idSWFEditText* _editText, idSWF* _swf ); + void Init( idSWFText* _text, idSWF* _swf ); idSWFScriptObject* GetScriptObject() { @@ -175,12 +177,18 @@ class idSWFTextInstance lengthCalculated = false; } + const idSWFText* GetText() const + { + return staticText; + } + // Removing the private access control statement due to cl 214702 // Apparently MS's C++ compiler supports the newer C++ standard, and GCC supports C++03 // In the new C++ standard, nested members of a friend class have access to private/protected members of the class granting friendship // In C++03, nested members defined in a friend class do NOT have access to private/protected members of the class granting friendship idSWFEditText* editText; + idSWFText* staticText; idSWF* swf; // this text instance's script object @@ -316,6 +324,7 @@ class idSWFScriptObject_TextInstancePrototype : public idSWFScriptObject SWF_NATIVE_VAR_DECLARE( subtitleSpeaker ); SWF_NATIVE_VAR_DECLARE_READONLY( _textLength ); + SWF_NATIVE_VAR_DECLARE_READONLY( length ); SWF_TEXT_FUNCTION_DECLARE( subtitleSourceCheck ); SWF_TEXT_FUNCTION_DECLARE( subtitleStart ); diff --git a/neo/swf/SWF_Types.h b/neo/swf/SWF_Types.h index 65b8051a12..0485f165e1 100644 --- a/neo/swf/SWF_Types.h +++ b/neo/swf/SWF_Types.h @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013-2015 Robert Beckebans +Copyright (C) 2023 Harrie van Ginneken This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -162,6 +163,12 @@ struct swfLineStyle_t uint16 endWidth; swfColorRGBA_t startColor; swfColorRGBA_t endColor; + + // HarrievG begin + uint8 startCapStyle; + uint8 endCapStyle; + enum capStyle { round = 0, none, square }; + // HarrievG end }; struct swfGradientRecord_t { diff --git a/neo/swf/opcodes.tbl b/neo/swf/opcodes.tbl new file mode 100644 index 0000000000..ab58bc8402 --- /dev/null +++ b/neo/swf/opcodes.tbl @@ -0,0 +1,297 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Includers must define an ABC_OP and ABC_UNUSED_OP macros of the following form: + * + * #define ABC_OP(opCount, throw, stack, internal, name) ... // defines regular op code + * #define ABC_UNUSED_OP(opCount, throw, stack, internal, name) ... // defines unused op code + * + * Selected arguments can then be used within the macro expansions. + * - opCount Number of operands encoded in the instruction. Uses -1 for "invalid", we can avoid that if necessary + * - throw 1 if the operation can throw, 0 otherwise + * - stack Stack movement not taking into account run-time names or function arguments + * - internal 1 if the operation is internal to the VM, 0 otherwise + * - name Unquoted name of operation. + * + * There MUST be exactly 256 entries in this table. + * The location of an opcode definition in this table determines its hex value, which is + * what must correspond to what is read out of the ABC file. + * Entries should never be added or removed from this table. New abc instructions may be added + * by updating entries for unused op codes. Unused op codes have a "name" that is a hex value. + * + */ +//-------------------------------------------------------------------------------------------------------- +#ifndef ABC_OP_F +# ifdef VMCFG_FLOAT +# define ABC_OP_F ABC_OP +# else +# define ABC_OP_F(o,t,s,i,n) ABC_UNUSED_OP(-1, 0, 0, 0, DISABLED_##n) +# endif +# define ABC_OP_F_DEFINED +#endif + +// opCount throw stack internal name hex +ABC_UNUSED_OP( -1, 0, 0, 0, 0x00) // 0x00 +ABC_OP( 0, 0, 0, 0, bkpt) // 0x01 +ABC_OP( 0, 0, 0, 0, nop) // 0x02 +ABC_OP( 0, 1, -1, 0, throw) // 0x03 +ABC_OP( 1, 1, 0, 0, getsuper) // 0x04 +ABC_OP( 1, 1, -2, 0, setsuper) // 0x05 +ABC_OP( 1, 1, 0, 0, dxns) // 0x06 +ABC_OP( 0, 1, -1, 0, dxnslate) // 0x07 +ABC_OP( 1, 0, 0, 0, kill) // 0x08 +ABC_OP( 0, 0, 0, 0, label) // 0x09 +ABC_OP_F( 0, 1, 0, 0, lf32x4) // 0x0A +ABC_OP_F( 0, 1, -2, 0, sf32x4) // 0x0B +ABC_OP( 1, 1, -2, 0, ifnlt) // 0x0C +ABC_OP( 1, 1, -2, 0, ifnle) // 0x0D +ABC_OP( 1, 1, -2, 0, ifngt) // 0x0E +ABC_OP( 1, 1, -2, 0, ifnge) // 0x0F +ABC_OP( 1, 0, 0, 0, jump) // 0x10 +ABC_OP( 1, 0, -1, 0, iftrue) // 0x11 +ABC_OP( 1, 0, -1, 0, iffalse) // 0x12 +ABC_OP( 1, 1, -2, 0, ifeq) // 0x13 +ABC_OP( 1, 1, -2, 0, ifne) // 0x14 +ABC_OP( 1, 1, -2, 0, iflt) // 0x15 +ABC_OP( 1, 1, -2, 0, ifle) // 0x16 +ABC_OP( 1, 1, -2, 0, ifgt) // 0x17 +ABC_OP( 1, 1, -2, 0, ifge) // 0x18 +ABC_OP( 1, 0, -2, 0, ifstricteq) // 0x19 +ABC_OP( 1, 0, -2, 0, ifstrictne) // 0x1A +ABC_OP( 2, 0, -1, 0, lookupswitch) // 0x1B +ABC_OP( 0, 1, -1, 0, pushwith) // 0x1C +ABC_OP( 0, 0, 0, 0, popscope) // 0x1D +ABC_OP( 0, 1, -1, 0, nextname) // 0x1E +ABC_OP( 0, 1, -1, 0, hasnext) // 0x1F +ABC_OP( 0, 0, 1, 0, pushnull) // 0x20 +ABC_OP( 0, 0, 1, 0, pushundefined) // 0x21 +ABC_OP_F( 1, 0, 1, 0, pushfloat) // 0x22 +ABC_OP( 0, 1, -1, 0, nextvalue) // 0x23 +ABC_OP( 1, 0, 1, 0, pushbyte) // 0x24 +ABC_OP( 1, 0, 1, 0, pushshort) // 0x25 +ABC_OP( 0, 0, 1, 0, pushtrue) // 0x26 +ABC_OP( 0, 0, 1, 0, pushfalse) // 0x27 +ABC_OP( 0, 0, 1, 0, pushnan) // 0x28 +ABC_OP( 0, 0, -1, 0, pop) // 0x29 +ABC_OP( 0, 0, 1, 0, dup) // 0x2A +ABC_OP( 0, 0, 0, 0, swap) // 0x2B +ABC_OP( 1, 0, 1, 0, pushstring) // 0x2C +ABC_OP( 1, 0, 1, 0, pushint) // 0x2D +ABC_OP( 1, 0, 1, 0, pushuint) // 0x2E +ABC_OP( 1, 0, 1, 0, pushdouble) // 0x2F +ABC_OP( 0, 1, -1, 0, pushscope) // 0x30 +ABC_OP( 1, 0, 1, 0, pushnamespace) // 0x31 +ABC_OP( 2, 1, 1, 0, hasnext2) // 0x32 +ABC_OP( -1, 1, 0, 1, lix8) // 0x33 +ABC_OP( -1, 1, 0, 1, lix16) // 0x34 +ABC_OP( 0, 1, 0, 0, li8) // 0x35 +ABC_OP( 0, 1, 0, 0, li16) // 0x36 +ABC_OP( 0, 1, 0, 0, li32) // 0x37 +ABC_OP( 0, 1, 0, 0, lf32) // 0x38 +ABC_OP( 0, 1, 0, 0, lf64) // 0x39 +ABC_OP( 0, 1, -2, 0, si8) // 0x3A +ABC_OP( 0, 1, -2, 0, si16) // 0x3B +ABC_OP( 0, 1, -2, 0, si32) // 0x3C +ABC_OP( 0, 1, -2, 0, sf32) // 0x3D +ABC_OP( 0, 1, -2, 0, sf64) // 0x3E +ABC_UNUSED_OP( -1, 0, 0, 0, 0x3F) // 0x3F +ABC_OP( 1, 1, 1, 0, newfunction) // 0x40 +ABC_OP( 1, 1, -1, 0, call) // 0x41 +ABC_OP( 1, 1, 0, 0, construct) // 0x42 +ABC_OP( 2, 1, 0, 0, callmethod) // 0x43 +ABC_OP( 2, 1, 0, 0, callstatic) // 0x44 +ABC_OP( 2, 1, 0, 0, callsuper) // 0x45 +ABC_OP( 2, 1, 0, 0, callproperty) // 0x46 +ABC_OP( 0, 0, 0, 0, returnvoid) // 0x47 +ABC_OP( 0, 1, -1, 0, returnvalue) // 0x48 +ABC_OP( 1, 1, -1, 0, constructsuper) // 0x49 +ABC_OP( 2, 1, 0, 0, constructprop) // 0x4A +ABC_OP( -1, 1, 0, 1, callsuperid) // 0x4B +ABC_OP( 2, 1, 0, 0, callproplex) // 0x4C +ABC_OP( -1, 1, 0, 1, callinterface) // 0x4D +ABC_OP( 2, 1, -1, 0, callsupervoid) // 0x4E +ABC_OP( 2, 1, -1, 0, callpropvoid) // 0x4F +ABC_OP( 0, 1, 0, 0, sxi1) // 0x50 +ABC_OP( 0, 1, 0, 0, sxi8) // 0x51 +ABC_OP( 0, 1, 0, 0, sxi16) // 0x52 +ABC_OP( 1, 1, 0, 0, applytype) // 0x53 +ABC_OP_F( 1, 0, 1, 0, pushfloat4) // 0x54 +ABC_OP( 1, 1, 1, 0, newobject) // 0x55 +ABC_OP( 1, 1, 1, 0, newarray) // 0x56 +ABC_OP( 0, 1, 1, 0, newactivation) // 0x57 +ABC_OP( 1, 1, 0, 0, newclass) // 0x58 +ABC_OP( 1, 1, 0, 0, getdescendants) // 0x59 +ABC_OP( 1, 1, 1, 0, newcatch) // 0x5A +ABC_OP( -1, 1, 0, 1, findpropglobalstrict) // 0x5B +ABC_OP( -1, 1, 0, 1, findpropglobal) // 0x5C +ABC_OP( 1, 1, 1, 0, findpropstrict) // 0x5D +ABC_OP( 1, 1, 1, 0, findproperty) // 0x5E +ABC_OP( 1, 1, 1, 0, finddef) // 0x5F +ABC_OP( 1, 1, 1, 0, getlex) // 0x60 +ABC_OP( 1, 1, -2, 0, setproperty) // 0x61 +ABC_OP( 1, 0, 1, 0, getlocal) // 0x62 +ABC_OP( 1, 0, -1, 0, setlocal) // 0x63 +ABC_OP( 0, 0, 1, 0, getglobalscope) // 0x64 +ABC_OP( 1, 0, 1, 0, getscopeobject) // 0x65 +ABC_OP( 1, 1, 0, 0, getproperty) // 0x66 +ABC_OP( 1, 0, 1, 0, getouterscope) // 0x67 +ABC_OP( 1, 1, -2, 0, initproperty) // 0x68 +ABC_UNUSED_OP( -1, 0, 0, 0, 0x69) // 0x69 +ABC_OP( 1, 1, 0, 0, deleteproperty) // 0x6A +ABC_UNUSED_OP( -1, 0, 0, 0, 0x6B) // 0x6B +ABC_OP( 1, 1, 0, 0, getslot) // 0x6C +ABC_OP( 1, 1, -2, 0, setslot) // 0x6D +ABC_OP( 1, 0, 1, 0, getglobalslot) // 0x6E +ABC_OP( 1, 1, -1, 0, setglobalslot) // 0x6F +ABC_OP( 0, 1, 0, 0, convert_s) // 0x70 +ABC_OP( 0, 1, 0, 0, esc_xelem) // 0x71 +ABC_OP( 0, 1, 0, 0, esc_xattr) // 0x72 +ABC_OP( 0, 1, 0, 0, convert_i) // 0x73 +ABC_OP( 0, 1, 0, 0, convert_u) // 0x74 +ABC_OP( 0, 1, 0, 0, convert_d) // 0x75 +ABC_OP( 0, 1, 0, 0, convert_b) // 0x76 +ABC_OP( 0, 1, 0, 0, convert_o) // 0x77 +ABC_OP( 0, 1, 0, 0, checkfilter) // 0x78 +ABC_OP_F( 0, 1, 0, 0, convert_f) // 0x79 +ABC_OP_F( 0, 1, 0, 0, unplus) // 0x7A +ABC_OP_F( 0, 1, 0, 0, convert_f4) // 0x7B +ABC_UNUSED_OP( -1, 0, 0, 0, 0x7C) // 0x7C +ABC_UNUSED_OP( -1, 0, 0, 0, 0x7D) // 0x7D +ABC_UNUSED_OP( -1, 0, 0, 0, 0x7E) // 0x7E +ABC_UNUSED_OP( -1, 0, 0, 0, 0x7F) // 0x7F +ABC_OP( 1, 1, 0, 0, coerce) // 0x80 +ABC_OP( 0, 1, 0, 0, coerce_b) // 0x81 +ABC_OP( 0, 1, 0, 0, coerce_a) // 0x82 +ABC_OP( 0, 1, 0, 0, coerce_i) // 0x83 +ABC_OP( 0, 1, 0, 0, coerce_d) // 0x84 +ABC_OP( 0, 1, 0, 0, coerce_s) // 0x85 +ABC_OP( 1, 1, 0, 0, astype) // 0x86 +ABC_OP( 0, 1, -1, 0, astypelate) // 0x87 +ABC_OP( 0, 1, 0, 0, coerce_u) // 0x88 +ABC_OP( 0, 1, 0, 0, coerce_o) // 0x89 +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8A) // 0x8A +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8B) // 0x8B +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8C) // 0x8C +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8D) // 0x8D +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8E) // 0x8E +ABC_UNUSED_OP( -1, 0, 0, 0, 0x8F) // 0x8F +ABC_OP( 0, 1, 0, 0, negate) // 0x90 +ABC_OP( 0, 1, 0, 0, increment) // 0x91 +ABC_OP( 1, 1, 0, 0, inclocal) // 0x92 +ABC_OP( 0, 1, 0, 0, decrement) // 0x93 +ABC_OP( 1, 1, 0, 0, declocal) // 0x94 +ABC_OP( 0, 0, 0, 0, typeof) // 0x95 +ABC_OP( 0, 0, 0, 0, not) // 0x96 +ABC_OP( 0, 1, 0, 0, bitnot) // 0x97 +ABC_UNUSED_OP( -1, 0, 0, 0, 0x98) // 0x98 +ABC_UNUSED_OP( -1, 0, 0, 0, 0x99) // 0x99 +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9A) // 0x9A +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9B) // 0x9B +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9C) // 0x9C +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9D) // 0x9D +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9E) // 0x9E +ABC_UNUSED_OP( -1, 0, 0, 0, 0x9F) // 0x9F +ABC_OP( 0, 1, -1, 0, add) // 0xA0 +ABC_OP( 0, 1, -1, 0, subtract) // 0xA1 +ABC_OP( 0, 1, -1, 0, multiply) // 0xA2 +ABC_OP( 0, 1, -1, 0, divide) // 0xA3 +ABC_OP( 0, 1, -1, 0, modulo) // 0xA4 +ABC_OP( 0, 1, -1, 0, lshift) // 0xA5 +ABC_OP( 0, 1, -1, 0, rshift) // 0xA6 +ABC_OP( 0, 1, -1, 0, urshift) // 0xA7 +ABC_OP( 0, 1, -1, 0, bitand) // 0xA8 +ABC_OP( 0, 1, -1, 0, bitor) // 0xA9 +ABC_OP( 0, 1, -1, 0, bitxor) // 0xAA +ABC_OP( 0, 1, -1, 0, equals) // 0xAB +ABC_OP( 0, 1, -1, 0, strictequals) // 0xAC +ABC_OP( 0, 1, -1, 0, lessthan) // 0xAD +ABC_OP( 0, 1, -1, 0, lessequals) // 0xAE +ABC_OP( 0, 1, -1, 0, greaterthan) // 0xAF +ABC_OP( 0, 1, -1, 0, greaterequals) // 0xB0 +ABC_OP( 0, 1, -1, 0, instanceof) // 0xB1 +ABC_OP( 1, 1, 0, 0, istype) // 0xB2 +ABC_OP( 0, 1, -1, 0, istypelate) // 0xB3 +ABC_OP( 0, 1, -1, 0, in) // 0xB4 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xB5) // 0xB5 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xB6) // 0xB6 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xB7) // 0xB7 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xB8) // 0xB8 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xB9) // 0xB9 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBA) // 0xBA +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBB) // 0xBB +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBC) // 0xBC +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBD) // 0xBD +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBE) // 0xBE +ABC_UNUSED_OP( -1, 0, 0, 0, 0xBF) // 0xBF +ABC_OP( 0, 1, 0, 0, increment_i) // 0xC0 +ABC_OP( 0, 1, 0, 0, decrement_i) // 0xC1 +ABC_OP( 1, 1, 0, 0, inclocal_i) // 0xC2 +ABC_OP( 1, 1, 0, 0, declocal_i) // 0xC3 +ABC_OP( 0, 1, 0, 0, negate_i) // 0xC4 +ABC_OP( 0, 1, -1, 0, add_i) // 0xC5 +ABC_OP( 0, 1, -1, 0, subtract_i) // 0xC6 +ABC_OP( 0, 1, -1, 0, multiply_i) // 0xC7 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xC8) // 0xC8 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xC9) // 0xC9 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCA) // 0xCA +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCB) // 0xCB +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCC) // 0xCC +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCD) // 0xCD +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCE) // 0xCE +ABC_UNUSED_OP( -1, 0, 0, 0, 0xCF) // 0xCF +ABC_OP( 0, 0, 1, 0, getlocal0) // 0xD0 +ABC_OP( 0, 0, 1, 0, getlocal1) // 0xD1 +ABC_OP( 0, 0, 1, 0, getlocal2) // 0xD2 +ABC_OP( 0, 0, 1, 0, getlocal3) // 0xD3 +ABC_OP( 0, 0, -1, 0, setlocal0) // 0xD4 +ABC_OP( 0, 0, -1, 0, setlocal1) // 0xD5 +ABC_OP( 0, 0, -1, 0, setlocal2) // 0xD6 +ABC_OP( 0, 0, -1, 0, setlocal3) // 0xD7 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xD8) // 0xD8 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xD9) // 0xD9 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDA) // 0xDA +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDB) // 0xDB +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDC) // 0xDC +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDD) // 0xDD +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDE) // 0xDE +ABC_UNUSED_OP( -1, 0, 0, 0, 0xDF) // 0xDF +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE0) // 0xE0 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE1) // 0xE1 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE2) // 0xE2 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE3) // 0xE3 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE4) // 0xE4 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE5) // 0xE5 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE6) // 0xE6 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE7) // 0xE7 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE8) // 0xE8 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xE9) // 0xE9 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xEA) // 0xEA +ABC_UNUSED_OP( -1, 0, 0, 0, 0xEB) // 0xEB +ABC_UNUSED_OP( -1, 0, 0, 0, 0xEC) // 0xEC +ABC_UNUSED_OP( -1, 0, 0, 0, 0xED) // 0xED +ABC_UNUSED_OP( -1, 0, 0, 0, 0xEE) // 0xEE - was OP_abs_jump +ABC_OP( 4, 1, 0, 0, debug) // 0xEF +ABC_OP( 1, 1, 0, 0, debugline) // 0xF0 +ABC_OP( 1, 1, 0, 0, debugfile) // 0xF1 +ABC_OP( 1, 0, 0, 0, bkptline) // 0xF2 +ABC_OP( 0, 0, 0, 0, timestamp) // 0xF3 +ABC_OP( -1, 1, 0, 1, restargc) // 0xF4 +ABC_OP( -1, 1, 0, 1, restarg) // 0xF5 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xF6) // 0xF6 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xF7) // 0xF7 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xF8) // 0xF8 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xF9) // 0xF9 +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFA) // 0xFA +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFB) // 0xFB +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFC) // 0xFC +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFD) // 0xFD +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFE) // 0xFE +ABC_UNUSED_OP( -1, 0, 0, 0, 0xFF) // 0xFF +#ifdef ABC_OP_F_DEFINED +# undef ABC_OP_F +# undef ABC_OP_F_DEFINED +#endif diff --git a/neo/ui/UserInterface.cpp b/neo/ui/UserInterface.cpp index 8c940fc3ef..9af3846ebe 100644 --- a/neo/ui/UserInterface.cpp +++ b/neo/ui/UserInterface.cpp @@ -455,14 +455,26 @@ bool idUserInterfaceLocal::InitFromFile( const char* qpath, bool rebuild, bool c else { desktop->SetFlag( WIN_DESKTOP ); - desktop->name = "Desktop"; - desktop->text = va( "Invalid GUI: %s", qpath ); - desktop->rect = idRectangle( 0.0f, 0.0f, 640.0f, 480.0f ); - desktop->drawRect = desktop->rect; - desktop->foreColor = idVec4( 1.0f, 1.0f, 1.0f, 1.0f ); - desktop->backColor = idVec4( 0.0f, 0.0f, 0.0f, 1.0f ); + desktop->swf = new( TAG_OLD_UI )idSWF( qpath, NULL ); + + if( !desktop->swf->IsLoaded() ) + { + desktop->name = "Desktop"; + desktop->text = va( "Invalid GUI: %s", qpath ); + desktop->rect = idRectangle( 0.0f, 0.0f, 640.0f, 480.0f ); + desktop->foreColor = idVec4( 1.0f, 1.0f, 1.0f, 1.0f ); + desktop->backColor = idVec4( 0.0f, 0.0f, 0.0f, 1.0f ); + common->Warning( "Couldn't load gui: '%s'", source.c_str() ); + } + else + { + desktop->rect = idRectangle( 0.0f, 0.0f, desktop->swf->GetFrameWidth(), desktop->swf->GetFrameHeight() ); + desktop->name = desktop->swf->GetName(); + common->Warning( "loaded SWF gui: '%s'", source.c_str() ); + } + desktop->SetupFromState(); - common->Warning( "Couldn't load gui: '%s'", source.c_str() ); + } interactive = desktop->Interactive(); @@ -537,8 +549,9 @@ void idUserInterfaceLocal::DrawCursor() { dc->DrawCursor( &cursorX, &cursorY, 32.0f ); } - else + else if( !desktop->swf ) { + dc->DrawCursor( &cursorX, &cursorY, 56.0f ); } } diff --git a/neo/ui/Window.cpp b/neo/ui/Window.cpp index 50cf8d49aa..390a7a9cee 100644 --- a/neo/ui/Window.cpp +++ b/neo/ui/Window.cpp @@ -149,7 +149,7 @@ void idWindow::CommonInit() timeLine = -1; textShadow = 0; hover = false; - + swf = NULL; for( int i = 0; i < SCRIPT_COUNT; i++ ) { scripts[i] = NULL; @@ -262,6 +262,12 @@ void idWindow::CleanUp() { delete scripts[i]; } + + if( swf != NULL ) + { + delete swf; + } + CommonInit(); } @@ -799,6 +805,12 @@ const char* idWindow::HandleEvent( const sysEvent_t* event, bool* updateVisuals { dc->SetCursor( idDeviceContext::CURSOR_ARROW ); } + + if( swf && swf->IsLoaded() ) + { + swf->HandleEvent( event ); + } + } if( visible && !noEvents ) @@ -1513,6 +1525,16 @@ void idWindow::Redraw( float x, float y, bool hud ) } } + if( swf != NULL ) + { + if( !swf->IsActive() ) + { + swf->Activate( true ); + } + + swf->Render( renderSystem, Sys_Milliseconds() ); + } + // Put transforms back to what they were before the children were processed dc->SetTransformInfo( oldOrg, oldTrans ); @@ -4276,6 +4298,10 @@ idWindow::Interactive */ bool idWindow::Interactive() { + if( swf != NULL ) + { + return true;// return swf->isMouseInClientArea; + } if( scripts[ ON_ACTION ] ) { return true; diff --git a/neo/ui/Window.h b/neo/ui/Window.h index 67b830d172..3d6231a8e0 100644 --- a/neo/ui/Window.h +++ b/neo/ui/Window.h @@ -482,6 +482,8 @@ class idWindow idRegisterList regList; idWinBool hideCursor; + + idSWF* swf; }; ID_INLINE void idWindow::AddDefinedVar( idWinVar* var )