Daniel,

Thank you for replying, I was very very anxious to get a word from
you, since I assume you know a good deal more about swfmill code and
its implications than me.

I hope you will take the time to read through this entire message, as
I think it is of importance to swfmill, also because I will start
coding as soon as I possibly can and will submit the patch, but before
I dive into it I would like to discuss the semantics and constraints
of a possible solution to the problem with you and whomever else this
may concern.

I have done a lot of investigation into the issue, all the way down to
decoding the opcodes involved in setting up asset classes.

First of all, as both you and me established - this is purely an AVM2
problem, since earlier Flash Player APIs rely on symbols, and these
work just fine (although it has been long time I have used that API,
so cannot confirm all quirks).

I can tell you that it is not as simple as disregarding this as "let
people implement classes in their compiler language instead". Consider
f.e. fonts. Without a font class stub present in a SWF generated by
swfmill beside the font data tag and symbol id tag, SWFs running on
AVM2 that load this font library SWF, will *NOT* be able to even find
the font, no matter what tricks you try! There is simply no API to do
that, other than to locate the font class and construct the font
object. This is the problem, not the fact that we need write own
classes for some language. The problem pertains to ALL kind of SWF
applications that want to load and use a font. This is why this is
paramount.

I have built a bash script that essentially combines an SWF produced
with swfmill from a simple dialect carrying a DefineFont3 and Symbol
tag, with another SWF produced with swfmill from an XML reverse
engineered from an SWF produced from Adobe Flash CS3 exporting a font
(something you alluded to earlier - suggesting I reverse-engineer CS3
SWFs carrying exported font classes to see how these classes are
carried). The two SWFs are "merged" i.e. the resultant SWF carries
DefineFont3 and Symbol tags, plus a DoAction tag that sets up a class
stub. This solution works, but it is my opinion that swfmill should do
the job instead, with a better level of flexibility than my script.
Here is a class stub definition XML template (notice the $-prefixed
substitution strings), ready to be merged into whatever swfmill
produces currently:

<?xml version="1.0"?>
<swf version="9" compressed="1">
        <Header framerate="0">
                <tags>
                        <DoABCDefine flags="1" name="">
                                <actions>
                                        <Action3 minorVersion="16" 
majorVersion="46">
                                                <constants>
                                                        <Constants>
                                                                <ints/>
                                                                <uints/>
                                                                <doubles/>
                                                                <strings>
                                                                        
<String2 value="$CLASSPATH"/>
                                                                        
<String2 value="$CLASSNAME"/>
                                                                        
<String2 value="flash.text"/>
                                                                        
<String2 value="Font"/>
                                                                        
<String2 value="$CLASSPATH:$CLASSNAME"/>
                                                                        
<String2 value=""/>
                                                                        
<String2 value="Object"/>
                                                                </strings>
                                                                <namespaces>
                                                                        
<PackageNamespace index="1"/>
                                                                        
<PackageNamespace index="3"/>
                                                                        
<ProtectedNamespace index="5"/>
                                                                        
<PackageNamespace index="6"/>
                                                                </namespaces>
                                                                <namespaceSets/>
                                                                <multinames>
                                                                        <QName 
namespaceIndex="1" nameIndex="2"/>
                                                                        <QName 
namespaceIndex="2" nameIndex="4"/>
                                                                        <QName 
namespaceIndex="4" nameIndex="7"/>
                                                                </multinames>
                                                        </Constants>
                                                </constants>
                                                <methods>
                                                        <MethodInfo retType="0" 
nameIndex="0" hasParamNames="0"
setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0"
needRest="0" needActivation="0" needArguments="0">
                                                                <paramTypes/>
                                                        </MethodInfo>
                                                        <MethodInfo retType="0" 
nameIndex="0" hasParamNames="0"
setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0"
needRest="0" needActivation="0" needArguments="0">
                                                                <paramTypes/>
                                                        </MethodInfo>
                                                        <MethodInfo retType="0" 
nameIndex="0" hasParamNames="0"
setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0"
needRest="0" needActivation="0" needArguments="0">
                                                                <paramTypes/>
                                                        </MethodInfo>
                                                </methods>
                                                <metadata/>
                                                <instances>
                                                        <InstanceInfo 
nameIndex="1" superIndex="2" hasProtectedNS="1"
interface="0" final="0" sealed="0" protectedNS="3" iInitIndex="1">
                                                                <interfaces/>
                                                                <traits/>
                                                        </InstanceInfo>
                                                </instances>
                                                <classes>
                                                        <ClassInfo 
cInitIndex="0">
                                                                <traits/>
                                                        </ClassInfo>
                                                </classes>
                                                <scripts>
                                                        <ScriptInfo 
initIndex="2">
                                                                <traits>
                                                                        
<TraitInfo nameIndex="1" override="0" final="0">
                                                                                
<trait>
                                                                                
  <Class slotID="1" classInfo="0"/>
                                                                                
</trait>
                                                                        
</TraitInfo>
                                                                </traits>
                                                        </ScriptInfo>
                                                </scripts>
                                                <methodBodies>
                                                        <MethodBody 
methodInfo="0" maxStack="1" maxRegs="1"
scopeDepth="4" maxScope="5" exceptionCount="0">
                                                                <code>
                                                                        
<OpGetLocal0/>
                                                                        
<OpPushScope/>
                                                                        
<OpReturnVoid/>
                                                                </code>
                                                                <exceptions/>
                                                                <traits/>
                                                        </MethodBody>
                                                        <MethodBody 
methodInfo="1" maxStack="1" maxRegs="1"
scopeDepth="5" maxScope="6" exceptionCount="0">
                                                                <code>
                                                                        
<OpGetLocal0/>
                                                                        
<OpPushScope/>
                                                                        
<OpGetLocal0/>
                                                                        
<OpConstructSuper argc="0"/>
                                                                        
<OpReturnVoid/>
                                                                </code>
                                                                <exceptions/>
                                                                <traits/>
                                                        </MethodBody>
                                                        <MethodBody 
methodInfo="2" maxStack="2" maxRegs="1"
scopeDepth="1" maxScope="4" exceptionCount="0">
                                                                <code>
                                                                        
<OpGetLocal0/>
                                                                        
<OpPushScope/>
                                                                        
<OpGetScopeObject scopeIndex="0"/>
                                                                        
<OpGetLex name="3"/>
                                                                        
<OpPushScope/>
                                                                        
<OpGetLex name="2"/>
                                                                        
<OpPushScope/>
                                                                        
<OpGetLex name="2"/>
                                                                        
<OpNewClass classIndex="0"/>
                                                                        
<OpPopScope/>
                                                                        
<OpPopScope/>
                                                                        
<OpInitProperty name="1"/>
                                                                        
<OpReturnVoid/>
                                                                </code>
                                                                <exceptions/>
                                                                <traits/>
                                                        </MethodBody>
                                                </methodBodies>
                                        </Action3>
                                </actions>
                        </DoABCDefine>
                        <SymbolClass>
                                <symbols>
                                        <Symbol objectID="1" 
name="$CLASSPATH.$CLASSNAME"/>
                                </symbols>
                        </SymbolClass>
                        <End/>
                </tags>
        </Header>
</swf>

Like I said, this is a template - since font class and package name
are user-defined, these are replaced before building the stub SWF, and
before it is in turn merged with a SWF produced from a traditional
simple dialect XML. The merging is currently done by the "swfcombine"
utility from the "swftools" software, because doing so with swfmill
would require some extra steps and doing some XML parsing (the
"DoAction" node has to be injected into a proper place, as do
DefineFont3 tags). By the way I have no idea whether "Symbol" and/or
potentially "Export" tags have to be present for the font library to
work. I have yet to try that.

As you can see, the issue is complicated enough to, IMO, not have to
be left to homecooked bash scripts (ugly, error prone, somewhat
inflexible and also SLOWER) and at the same time paramount for ANY
kind of SWF application. Put simply, there is no way known to me to
link a font asset to a font class at runtime - in the very least AVM2
only references font classes, there is no more "attachMovie" thing
anymore, you simply have no access to symbols, and I am sure dealing
with them is deprecated. Your only chance is to do a
ApplicationDomain.getDefinition call, and the class it gets has to be
compiled into the SWF you are loading, and linked to the asset data.

I am attaching the bash script and its dependencies that illustrate
how to generate AVM2 compliant font (although this applies to any kind
of asset) libraries, that actually work when loaded. This may give
ideas to what swfmill may be missing.

Thank you for reading, I hope you see what I mean. Please reply, of
course when you find time, but please do. I think this is important,
many folks write AVM2 code nowadays... Thankfully, asset loading is
much better implemented for AVM2 than it was for AVM1, but without
swfmill supporting it fully, it is like a gaping hole in its
functionality when dealing with AVM2 applications.

On Tue, Nov 10, 2009 at 16:28, Daniel Cassidy <m...@danielcassidy.me.uk> wrote:
> Hi Armencho,
>
> 2009/11/9 armen...@gmail.com <armen...@gmail.com>:
>> So, any suggestions, Daniel? :-)
>>
>> What do you think about implementing class stub generation in swfmill?
>
> Sorry for the delay responding. I’ve been trying to get a better idea
> of what’s going on so I could post a more detailed reply but I haven’t
> had much chance yet.
>
> Anyway, I had time for a very brief investigation, so here are my
> findings. I’m going to be a bit vague on technical details because I
> haven’t had a chance to look too closely.
>
> I had thought that when you specify an id attribute on a clip in the
> simple dialect, that swfmill automatically associates that clip with
> the AS3 class of the same name (if it exists). It turns out that it
> does not do that. In fact, the value attribute becomes the linkage id
> of the clip. As far as I can tell, when compiling for AS3, haxe
> automatically associates classes with movie clips whose linkage id is
> the same as the fully qualified name of the class. as3compile and
> mxmlc do *not* do this, so it is not possible to associate clips with
> as3compile- or mxmlc-compiled classes using the simple dialect.
>
> I definitely agree that this will not do -- I want swfmill to be
> useful for constructing asset libraries for all compilers.
>
> For AS2, the simple dialect currently sets class associations
> according to the value of the class attribute, but this does not work
> for AS3. It should be a reasonably simple matter to extend this to
> work with AS3 -- see the register-class template in
> src/xslt/simple-tools.xslt. The trick should be to check if the as3
> attribute is set at the root of the simple XML, and if it is, replace
> the DoInitAction tag with an appropriate SymbolClass tag (see the SWF
> spec for details).
>
>
> Generating class stubs is a separate issue. It should only be
> necessary if you can’t or don’t want to write a class for each
> exported symbol. In most cases I think it would be more appropriate to
> write a utility to generate class stubs in your preferred language.
> This approach has the advantage that you can construct instances of
> your clip using the new operator, which the compiler can check
> statically for errors.
>
> Despite that I am not against generating class stubs in swfmill. In
> particular it is useful for the case where you are instantiating clips
> loaded from an external SWF at runtime. The correct place to generate
> class stubs is probably immediately before generating the SymbolClass
> tag discussed above.
>
> Patches for this are very welcome, or I will work on this myself
> eventually. Right now, however, I am more focused on closing the known
> bugs.
>
> Hope this helps,
> Dan.
>
> _______________________________________________
> swfmill mailing list
> swfmill@osflash.org
> http://osflash.org/mailman/listinfo/swfmill_osflash.org
>

Attachment: build-font-library
Description: Binary data

<?xml version="1.0"?>
<swf version="9" compressed="1">
  <Header framerate="0">
    <tags>
      <DoABCDefine flags="1" name="">
        <actions>
          <Action3 minorVersion="16" majorVersion="46">
            <constants>
              <Constants>
                <ints/>
                <uints/>
                <doubles/>
                <strings>
                  <String2 value="$CLASSPATH"/>
                  <String2 value="$CLASSNAME"/>
                  <String2 value="flash.text"/>
                  <String2 value="Font"/>
                  <String2 value="$CLASSPATH:$CLASSNAME"/>
                  <String2 value=""/>
                  <String2 value="Object"/>
                </strings>
                <namespaces>
                  <PackageNamespace index="1"/>
                  <PackageNamespace index="3"/>
                  <ProtectedNamespace index="5"/>
                  <PackageNamespace index="6"/>
                </namespaces>
                <namespaceSets/>
                <multinames>
                  <QName namespaceIndex="1" nameIndex="2"/>
                  <QName namespaceIndex="2" nameIndex="4"/>
                  <QName namespaceIndex="4" nameIndex="7"/>
                </multinames>
              </Constants>
            </constants>
            <methods>
              <MethodInfo retType="0" nameIndex="0" hasParamNames="0" setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0" needRest="0" needActivation="0" needArguments="0">
                <paramTypes/>
              </MethodInfo>
              <MethodInfo retType="0" nameIndex="0" hasParamNames="0" setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0" needRest="0" needActivation="0" needArguments="0">
                <paramTypes/>
              </MethodInfo>
              <MethodInfo retType="0" nameIndex="0" hasParamNames="0" setSDXNs="0" isExplicit="0" ignoreRest="0" hasOptional="0" needRest="0" needActivation="0" needArguments="0">
                <paramTypes/>
              </MethodInfo>
            </methods>
            <metadata/>
            <instances>
              <InstanceInfo nameIndex="1" superIndex="2" hasProtectedNS="1" interface="0" final="0" sealed="0" protectedNS="3" iInitIndex="1">
                <interfaces/>
                <traits/>
              </InstanceInfo>
            </instances>
            <classes>
              <ClassInfo cInitIndex="0">
                <traits/>
              </ClassInfo>
            </classes>
            <scripts>
              <ScriptInfo initIndex="2">
                <traits>
                  <TraitInfo nameIndex="1" override="0" final="0">
                    <trait>
                      <Class slotID="1" classInfo="0"/>
                    </trait>
                  </TraitInfo>
                </traits>
              </ScriptInfo>
            </scripts>
            <methodBodies>
              <MethodBody methodInfo="0" maxStack="1" maxRegs="1" scopeDepth="4" maxScope="5" exceptionCount="0">
                <code>
                  <OpGetLocal0/>
                  <OpPushScope/>
                  <OpReturnVoid/>
                </code>
                <exceptions/>
                <traits/>
              </MethodBody>
              <MethodBody methodInfo="1" maxStack="1" maxRegs="1" scopeDepth="5" maxScope="6" exceptionCount="0">
                <code>
                  <OpGetLocal0/>
                  <OpPushScope/>
                  <OpGetLocal0/>
                  <OpConstructSuper argc="0"/>
                  <OpReturnVoid/>
                </code>
                <exceptions/>
                <traits/>
              </MethodBody>
              <MethodBody methodInfo="2" maxStack="2" maxRegs="1" scopeDepth="1" maxScope="4" exceptionCount="0">
                <code>
                  <OpGetLocal0/>
                  <OpPushScope/>
                  <OpGetScopeObject scopeIndex="0"/>
                  <OpGetLex name="3"/>
                  <OpPushScope/>
                  <OpGetLex name="2"/>
                  <OpPushScope/>
                  <OpGetLex name="2"/>
                  <OpNewClass classIndex="0"/>
                  <OpPopScope/>
                  <OpPopScope/>
                  <OpInitProperty name="1"/>
                  <OpReturnVoid/>
                </code>
                <exceptions/>
                <traits/>
              </MethodBody>
            </methodBodies>
          </Action3>
        </actions>
      </DoABCDefine>
      <SymbolClass>
        <symbols>
          <Symbol objectID="1" name="$CLASSPATH.$CLASSNAME"/>
        </symbols>
      </SymbolClass>
      <!-- ShowFrame/ -->
      <End/>
    </tags>
  </Header>
</swf>
_______________________________________________
swfmill mailing list
swfmill@osflash.org
http://osflash.org/mailman/listinfo/swfmill_osflash.org

Reply via email to