Using embedded SWF assets [Windows]

I had this problem for a while, but this time I would really like to solve it. Basically, if you embed a SWF library (bundle) - it wouldn’t be accessible anymore.

The SWF libraries are being generated via the openfl process mySWF.swf command. That would by default give me a mySWF.bundle directory with my assets.

In the projects.xml file I could then include the library:

<assets path="assets/layout.bundle" rename="layout" embed="false" />

In my project, I would have to first load the library and then be able to use it. It’s wildly convenient to use it this way because I get access to generated classes that I could then use with all the auto-completion.

The problem starts when I try to set the tag embed=true. At this point the generated bundle becomes a part of the EXE file, and you can see the assets in the asset-list: Assets.list().toString() would give you a whole list of assets.

But now there’s no known way to access any of the assets. Trying to use any of set methods from the Assets class would tell me that the asset does not exist.

An interesting side note is that SWF assets aren’t listed within any AssetType category:

		trace( "all", Assets.list().toString() ); // [symbols/1.png,symbols/9.png,swflite.bin,symbols/6.png,symbols/15.png,fonts/NotoSansJP-Regular.otf]
		trace( "bin", Assets.list(AssetType.BINARY).toString() ); // [fonts/NotoSansJP-Regular.otf]
		trace( "font", Assets.list(AssetType.FONT).toString() ); // [fonts/NotoSansJP-Regular.otf]
		trace( "img", Assets.list(AssetType.IMAGE).toString() ); //[]
		trace( "mcs", Assets.list(AssetType.MOVIE_CLIP).toString() ); //[]
		trace( "music", Assets.list(AssetType.MUSIC).toString() ); //[]
		trace( "sound", Assets.list(AssetType.SOUND).toString() ); //[]
		trace( "text", Assets.list(AssetType.TEXT).toString() ); //[]
		trace( "template", Assets.list(AssetType.TEMPLATE).toString() ); //[]

FYI, the font in this example is added via <assets path="assets/fonts" rename="fonts" embed="true"/> and is not a part of the main library.

Is there a way use generated bundles without referring to the generated=true tag and loading SWFs? The reason I want to use bundles is because it’s really nice for auto-completion, especially with nested assets and using SWFs would often bug out IDEs and make them stop auto-completion throughout the whole project.

Lime added an AssetBundle class which should be able to extract a zipped library at runtime, this can then be used as a library at runtime

I think it would be good if this path (runtime not build time) would be used for bundles so they are just a flat binary zip file and don’t require so much special handling in the command line tools

Asset Bundle sounds cool, but:

  1. There’s no sample on how to use it anywhere. Searched on lime-samples and openfl-samples etc.
  2. This doesn’t address the main problem of embedding resources. Although I’ve tried to simply ZIP my SWF file, use Haxe --resources flag to add it, use AssetBundles to load my ZIP, and openfl.Assets to load the SWF extracted from the said ZIP as a library. My code didn’t work, but it potentially could.

If I could fix my code below I think I could use embedded resources for future builds if I really wanted to keep everything in a single EXE file (or at least some more sensitive data files):

class Main extends Sprite 
{

	public function new() 
	{
		super();

		 // testLib is a "test.zip" file embedded via
         // <haxeflag name="-resource assets/test.zip@testLib" /> in project.xml
		var bytes = haxe.Resource.getBytes("testLib");
		
		var ab = AssetBundle.loadFromBytes(bytes).onComplete( (ab:AssetBundle) ->
		{
			for ( p in ab.paths)
			{
				trace( p ); // output: test.swf
				Assets.loadLibrary( p ).onComplete( (al:AssetLibrary) ->
				{
					var t = Assets.getMovieClip("TestMC");
					trace(t == null);
					addChild(t);
				}).onError( (d) -> { // ERROR: There is no asset library with an ID of "test.swf"
					trace( Std.string(d) );
				});
			}
			
			for ( key in ab.data.keys())
			{
				trace( key, ab.data[key]); // output: test.swf,CWS☼8
			}
		});
	}

}

@singmajesty Can you please write a code snippet here in this post? I’ll make a pull-request to lime-samples over the weekend myself. I really don’t know how AssetBundles are intended to be used together with openfl.Assets to register a library, so I need your help.

Here is how I managed to load the bundle from assets folder. You can load it from an url also using URLStream.

import openfl.utils.Assets;
import swf.exporters.animate.AnimateLibrary;
import lime.utils.AssetBundle;
import haxe.io.BytesInput;
import openfl.utils.ByteArray;
import openfl.display.MovieClip;
import openfl.display.Sprite;
import lime.utils.AssetLibrary;

class Main extends Sprite 
{

    public function new () {
        super();

        var onComplete = function(bytes:ByteArray){

            var input:BytesInput = new BytesInput(bytes);
            var bundle:AssetBundle = AssetBundle.fromBytes(input.readAll());

            var library:AssetLibrary = AssetLibrary.fromBundle(bundle);

            library.load().onComplete(function(_):Void{

                var swf = cast(library, AnimateLibrary);
                var prefab:MovieClip = swf.getMovieClip("TestMC");
                addChild(prefab);

            }).onError(function(_):Void{
                trace("AnimateLibrary load error!");
            });

        }
        var onError = function(_) {
            trace('Error loading zip');
        }

        Assets.loadBytes("assets/test.zip").onComplete(onComplete).onError(onError);
    }
}
1 Like

Thanks @alexk for your working code sample :smiley:

A couple quick notes:

  1. openfl.utils.ByteArray joins with the haxe.io.Bytes already, so you can skip the BytesInput step and pass a loaded ByteArray directly into AssetBundle.fromBytes
  2. There’s no need to cast the loaded library as AnimateLibrary, it can be openfl.utils.AssetLibrary.

If we like the AssetBundle approach and finalize it perhaps we can get the loadFromBundle or fromBundle methods added to the openfl.utils.AssetLibrary type so we can simplify the code

Oh – BTW

If you have a bundle in your assets directory at compile-time, I believe you can use Assets to load it?

Assets.loadLibrary("assets/test.zip").onComplete(function(library)
{
    var prefab = library.getMovieClip("TestMC");
    // or
    var prefab = Assets.getMovieClip("assets/test.zip:TestMC");
    addChild(prefab);
});

If not it would be a good thing to add, though I believe it is already supported

Thanks for the tips. I did not pay attention to the types, I confused lime.utils.AssetLibrary with openfl.utils.AssetLibrary :smiley:

Regarding the compile-time bundle I think it works but I did not test it. I was interested in loading the bundles at run-time from an url, because I have a lot of game assets to load from the CDN. I think many people might want this if they are building web games :slight_smile:

If everything works I think it would be good for us to add some additional functions to simplify the code down to a shorter command. For example:

AssetLibrary.loadFromFile("path/to/bundle.zip").onComplete(function (library)
{
    var mc = library.getMovieClip("TestMC");
});

This does not currently exist… the loadFromFile assumes you will load an asset manifest but perhaps there would be a clean way for us to make the code work for both bundles and manifests?

Your workflow (of using a CDN) was a big reason for the push to ZIP assets, generating a stable SWF-based output (that does not require recompilation) and moving for a simpler way to load these at runtime (similar to Loader – and if it does not already work I’d like Loader to work with bundles) and use their assets. The Timeline class is meant to abstract the internal format of a bundle so that any editor or resource could generate the frames within a MovieClip