AS3: How to instantiate the generic 'Document Class' (MainTimeline?) of a loaded SWF? - actionscript-3

I have "Question.swf", which was created from "Question.fla". Note that Question.fla has no Document Class associated with it. (Note that this is legacy content, and there are over 14,000 variants of "Question.swf"; changing all these is not a viable option.)
Now I have my main Flash application, which loads in Question.swf at runtime. I know that Question.swf has a "Document Class" automatically created which represents the entire "stage" of the SWF (and that it's named "MainTimeline"). I want this application to be able to instantiate multiple instances of that Question.swf Document class... how can I?
I've been working with Flash/AS3 since 2006 (I'm very familiar with loading/using external content, the ApplicationDomain, etc.), but I find that I have no idea how to do this!
Things I've tried which haven't worked include querying the relevant ApplicationDomain with hasDefinition( "Question_fla.MainTimeline" ) - this returns false - as well as running getQualifiedClassName() on my loader.content object - this just returns MovieClip.

I'm not sure how to duplicate the main content of the Loader. However, a reasonable workaround might be to load the SWF bytes once and create multiple Loaders from those bytes:
Load your SWF bytes with a URLLoader:
var urlloader : URLLoader = new URLLoader();
urlloader.load(new URLRequest("your url here"));
Once it is loaded, use the bytes to instantiate new display objects:
var loader : Loader = new Loader();
loader.loadBytes(urlloader.bytes);
Use your loaded loader's loader.content display object on the display list (or the loader itself).

Related

Loader.as returning blank bitmap

I have an application which pulls in Bitmap resources from a server - currently I use the Loader class to do this, then, once they're loaded, generate a BitmapData based on the loader dimensions and draw the instance of Loader directly to it (the BitmapData is used for Away3D textures as well as Bitmap instances, so I have no need for the Loader once fetched).
This has always worked for me, but recently I started getting 0x0 Loaders, and invalid BitmapData as a result.
I stopped doing this:
this.imageBitmap = new BitmapData(this.imageLoader.width, this.imageLoader.height, true, 0);
..and started doing this:
this.imageBitmap = new BitmapData(event.target.content.width, event.target.content.height, true, 0);
Where event is the Event.COMPLETE event fired by the loader. This fixed the dimension problem, but the BitmapData is just a plain white bitmap (and it's set to transparent by default, so this is being drawn into it). Frustratingly, this doesn't happen every time, if I refresh the application it works as it should around 25% of the time, otherwise it plays up like this.
I've got a tight deadline and I'm really screwing about this, if anyone could help or suggest a better way of doing it you'd really be saving my neck!
Sounds like you need to adjust the image decoding policy for the loader - to ensure it decodes the image before COMPLETE fires - then the width and height etc should be reliable.
To do it, just add a suitable LoaderContext object to the Loader.load method:
var loaderContext:LoaderContext = new LoaderContext();
//set decode policy
loaderContext.imageDecodingPolicy = ImageDecodingPolicy.ON_LOAD;
//load image
loader.load(yourUrl, loaderContext);
The default decode policy is ImageDecodingPolicy.ON_DEMAND - which doesnt decode the image until it is actually required.
Lang docs: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/ImageDecodingPolicy.html
Fixed it, stupid oversight and bit of an obscure situation but I'll leave an answer in case anyone runs into something similar.
My loader is contained in an Asset class - when another object requires the internal bitmap, it queries this class - if the bitmap's present, it returns it, if not it loads it with a loader and registers a callback for a COMPLETE event which is fired when the Loader has loaded and transferred its contents to a BitmapData instance.
The stupid mistake I'd made was that, if several objects were querying the same (as-yet-unloaded) asset, it would start reloading the asset each time, creating a new Loader as it did so...so when the first Loader was complete, it would fire an event but no reference to it would exist, not only creating a memory leak but causing the Asset class to extract the BitmapData from the most-recently-created Loader, which was not complete! The asynchronous nature of Loader is the reason it worked sometimes, as on occasion the final Loader would be ready in time for BitmapData extraction.
Simple solution was to create an internal boolean, _isLoading, which is set to true the first time load() is called - any subsequent calls are ignored if it's true, but callbacks still registered, works a treat!

Difference between getDefinition and getDefinitionByName in AS3

Can somebody explain what is the difference between getDefinitionByName and getDefinition inA AS3?
When I load an external SWF I can't use getDefinitionByName because I get an Error #1065.
But using externalSWF_ContentLoaderInfo.applicationDomain.getDefinition works OK.
So, why getDefinitionByName doesn't find the className?
I mean, if the definition is inside the applicationDomain of the loaded SWF, why is not in the main SWF too? (I'm using Flex).
Offtopic: I can't create new tags so I can't add the tags getDefinition and getDefinitionByName :(
getDefinition is a method of an ApplicationDomain which returns a definition of a class, namespace or function.
getDefinitionByName is a package-level function from flash.utils which returns a Class object that you can use to instantiate new Objects. The definition must already be loaded somewhere in your ApplicationDomain.
The reason you can't make getDefinitionByName with an external SWF is that it is loaded into a separate ApplicationDomain. Your second example works because you are targeting the correct ApplicationDomain. To make your first example work you must load the external SWF into your current ApplicationDomain like this:
var request:URLRequest = new URLRequest("externalSWF.swf");
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;
var loader:Loader = new Loader();
loader.load(request,context);
This works because it passes the current ApplicationDomain as a property of the loader context.

Loader.load and Loader.loadBytes differences

I'm loading as2 swf into AIR application. It works properly when loaded from file. But when loaded from bytes, it is broken in some way (it reacts to mouse, but some elements are inactive)
var bytes:ByteArray = ... //loaded from resources
var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext(false);
context.allowCodeImport = true; //this is neccessary
// Method 1 - blocks some scripts in loaded SWF
//context.applicationDomain = new ApplicationDomain();
// (application domain has no effect with as2 swf)
//context.securityDomain = SecurityDomain.currentDomain; //gives error 2114: securityDomain must be null
loader.loadBytes(bytes, context);
// Method 2 - loads properly
//loader.load(new URLRequest(file.url));
So why not just load it from file? My resources are protected with encryption and I can't dump them to disk - they must still be protected.
What tricks may exist to load from bytes properly?
There is similar question, but in my case as2 causes more problems.
AS2 and AS3 use different runtimes (bytecode is different) so you won't be able to properly execute any AS2 bytecode in the AS3 runtime. you are basically injecting AS2 code into your AS3 application, so it ain't gonna work :/
According the the documentation for LoaderContext you should only use the applicationDomain property only when loading ActionScript 3.0 SWFs. Try dropping that parameter (or setting it to null) and see what happens.
It seems that old SWF movies (AS1 and AS2, which require AVM1) loaded into an AIR app with load get put in their own domains, but those loaded with loadBytes share a domain. So if you have multiple AVM1 SWFs loaded with loadBytes their _global properties will clobber each other. This affects the Flash MX UI components (ca. 2002).
I can't be the only one trying to package ancient Flash files in AIR apps, so I figure this info may be useful to someone.

Class in loaded SWF cannot use base class in loading SWF

I have two SWF files which I shall call container and slave. The slave file contains a movieclip that extends from a class I shall call base. base extends MovieClip and is compiled into an SWC. slave includes this SWC as a runtime library, while container includes it as merged (and does reference it so it should be compiled into the container SWF).
The container loads the slave like so:
bgURLRequest = new URLRequest(slaveUrl);
var context:LoaderContext = new LoaderContext(false, new ApplicationDomain( null ), SecurityDomain.currentDomain);
bgLoader.load(bgURLRequest, context);
When this loading happens, I get the error that class base was not found. I suspect this has something to do with the presence of the ApplicationDomain in there. I'm not sure what it does exactly, since I didn't write this loading code myself (but I do know that it's there for a reason so it can't be simply removed).
How to fix?
This is the solution
new LoaderContext(false, new ApplicationDomain( ApplicationDomain.currentDomain ), SecurityDomain.currentDomain);
Use ApplicationDomain.currentDomain and track the available classes with the super getDefinitionNames available at: http://etcs.ru/pre/getDefinitionNamesSource/
At least you'll know whats available in the loaded swf.

Flash crash (ends up in a restart loop) when loading an external swf

Im working with FlashDevelop and have two main projects, all pure AS3 projects.
When trying to load my second project from my main project I get all kinds of errors.
The Main class of the main project extends Sprite and the Main class in the "to-be-imported" project extends MovieClip. Looking at the loading of the swf in the debug window in FD it all seems fine:
[SWF] 'pathToSwf'\secondProject.swf - 410 626 bytes after decompression.
If i try to assign the loaded swf to a newly created MovieClip I get a coercion failiure:
swfContent = loader.content; // =>
Type Coercion failed: cannot convert Main#46c0201 to flash.display.MovieClip.
So, typecasting the loaded content like so:
swfContent = loader.content as MovieClip;
removes that error but then I fall into the next pit as I try to call addChild:
Error #2007: Parameter child must be non-null.
Trying to get around the issue I tried to add the loader directly into the container where I want to show the external swf. This is when the real interesting problems begin:
targetContainer.addChild(loader);
My main application now hang, restarting in a never ending loop. I have no idea why..
So my issue is really. How can my external swf be loaded but then again be null.
It works perfectly fine when I run the external swf by itself...
Use getQualifiedClassName and getQualifiedSuperclassName functions (and even describeType if you must) on loader.content to get its exact type information.
loader.content as MovieClip returns null because loader.content is not a MovieClip - casting with as keyword silently returns null when it fails. Is there any chance that the loaded content is an AS2 movie clip instead of AS3 movie clip? In that case getQualifiedClassName will return "AVM1Movie".
The latter issues is weird, but first try changing the type of swfContent to Sprite. A main class does not always extend MovieClip, and judging from the error message it indeed doesn't in this case.
Your swfContent will be null, if it cannot be casted to MovieClip. That is how the as operator is supposed to work when type coercion fails.
Modify your assignment operation like this:
var swfContent :MovieClip = MovieClip(loader.content);
You might want to encompass the assignment in a try...catch block, as an error will be thrown in case of failure, instead of swfContent being set null, as with as.
So the problem was that the main class of my loaded swf had the same name as the swf I was loading from. This led to that when flash tries to execute the loaded swf it actually calls the parent MAIN class which results in the looping behaviour.
To avoid this DHuntrods suggested to change the application domain which solved the issue.
loader = new Loader();
var AD:ApplicationDomain = new ApplicationDomain( null );
var context:LoaderContext = new LoaderContext( false, AD );
loader.load(new URLRequest(path), context);