blob: 40053bee8c8fa28c6940c0c588daab75abe322bf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flash.tools;
import flash.swf.Action;
import flash.swf.Header;
import flash.swf.TagDecoder;
import flash.swf.TagHandler;
import flash.swf.Dictionary;
import flash.swf.ActionConstants;
import flash.swf.MovieMetaData;
import flash.swf.tags.DefineButton;
import flash.swf.tags.DoAction;
import flash.swf.tags.DoInitAction;
import flash.swf.tags.PlaceObject;
import flash.swf.tags.DefineSprite;
import flash.swf.types.ActionList;
import flash.swf.types.ButtonCondAction;
import flash.swf.types.ClipActionRecord;
import flash.swf.actions.DefineFunction;
import flash.swf.actions.ConstantPool;
import flash.util.Trace;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
/**
* This class implements the TagHandler interface
* and provides a mechanism for containing the
* actions associated with a SWF.
*/
public class SwfActionContainer extends TagHandler
{
boolean errorProcessing = true;
ActionList m_master;
// temporaries used while decoding
Dictionary m_dictionary;
Header m_header;
public SwfActionContainer(byte[] swf, byte[] swd) { this(new ByteArrayInputStream(swf), new ByteArrayInputStream(swd)); }
public SwfActionContainer(InputStream swfIn) { this(swfIn, null); }
public SwfActionContainer(InputStream swfIn, InputStream swdIn)
{
TagDecoder p = new TagDecoder(swfIn, swdIn);
try
{
process(p);
errorProcessing = false;
}
catch(IOException io)
{
if (Trace.error)
io.printStackTrace();
}
}
// getters
public ActionList getMasterList() { return m_master; }
public Header getHeader() { return m_header; }
public Dictionary getDictionary() { return m_dictionary; }
// Did we hit an error in processing the swf?
public boolean hasErrors() { return errorProcessing; }
/**
* Ask a TagDecoder to do its magic, calling us
* upon each encounter of a new tag.
*/
void process(TagDecoder d) throws IOException
{
m_master = new ActionList(true);
d.setKeepOffsets(true);
d.parse(this);
}
/**
* Return a path to an ActionList that contains the given offset
* if an exact match is not found then return the largest
* that does not exceed offset.
*/
public ActionLocation locationLessOrEqualTo(int offset)
{
ActionLocation l = new ActionLocation();
locationLessOrEqualTo(l, m_master, offset);
return l;
}
public static ActionLocation locationLessOrEqualTo(ActionLocation location, ActionList list, int offset)
{
int at = findLessOrEqualTo(list, offset);
if (at > -1)
{
// we hit so mark it and extract a constant pool if any
location.at = at;
location.actions = list;
Action a = list.getAction(0);
if (a.code == ActionConstants.sactionConstantPool)
location.pool = (ConstantPool)a;
// then see if we need to traverse
a = list.getAction(at);
if ( (a.code == ActionConstants.sactionDefineFunction) ||
(a.code == ActionConstants.sactionDefineFunction2) )
{
location.function = (DefineFunction)a;
locationLessOrEqualTo(location, ((DefineFunction)a).actionList, offset);
}
else if (a instanceof DummyAction)
{
// our dummy container, then we drop in
locationLessOrEqualTo(location, ((DummyAction)a).getActionList(), offset);
}
}
return location;
}
// find the index of the largest offset in the list that does not
// exceed the offset value provided.
public static int findLessOrEqualTo(ActionList list, int offset)
{
int i = find(list, offset);
if (i < 0)
{
// means we didn't locate it, so get the next closest one
// which is 1 below the insertion point
i = (-i - 1) - 1;
}
return i;
}
// perform a binary search to locate the offset within the sorted
// list of offsets within the action list.
// if no match then (-i - 1) provides the index of where an insertion
// would occur for this offset in the list.
public static int find(ActionList list, int offset)
{
int lo = 0;
int hi = list.size()-1;
while (lo <= hi)
{
int i = (lo + hi)/2;
int m = list.getOffset(i);
if (offset > m)
lo = i + 1;
else if (offset < m)
hi = i - 1;
else
return i; // offset found
}
return -(lo + 1); // offset not found, low is the insertion point
}
/**
* Dummy Action container for housing all of our
* topmost level actionlists in a convenient form
*/
public class DummyAction extends Action
{
public DummyAction(ActionList list)
{
super(ActionConstants.sactionNone);
m_actionList = list;
}
// getters/setters
public ActionList getActionList() { return m_actionList; }
public String getClassName() { return m_className; }
public void setClassName(String name) { m_className = name; }
private ActionList m_actionList;
private String m_className;
}
/**
* Store away the ActionLists for later retrieval
*/
DummyAction recordActions(ActionList list)
{
DummyAction da = null;
if (list != null && list.size() > 0)
{
// use the first offset as our reference point
int offset = list.getOffset(0);
// now create a pseudo action for this action list in our master
da = new DummyAction(list);
m_master.setActionOffset(offset, da);
}
return da;
}
/**
* -----------------------------------------------
* The following APIs override TagHandler.
* -----------------------------------------------
*/
@Override
public void doInitAction(DoInitAction tag)
{
DummyAction a = recordActions(tag.actionList);
// now fill in the class name if we can
if (m_header.version > 6 && tag.sprite != null)
{
String __Packages = MovieMetaData.idRef(tag.sprite, m_dictionary);
String className = (__Packages != null && __Packages.startsWith("__Packages")) ? __Packages.substring(11) : null; //$NON-NLS-1$
a.setClassName(className);
}
}
@Override
public void doAction(DoAction tag)
{
recordActions(tag.actionList);
}
@Override
public void defineSprite(DefineSprite tag)
{
// @todo need to support actions in sprites!!!
}
@Override
public void placeObject2(PlaceObject tag)
{
if (tag.hasClipAction())
{
Iterator it = tag.clipActions.clipActionRecords.iterator();
while (it.hasNext())
{
ClipActionRecord record = (ClipActionRecord) it.next();
recordActions(record.actionList);
}
}
}
@Override
public void defineButton(DefineButton tag)
{
recordActions(tag.condActions[0].actionList);
}
@Override
public void defineButton2(DefineButton tag)
{
if (tag.condActions.length > 0)
{
for (int i=0; i < tag.condActions.length; i++)
{
ButtonCondAction cond = tag.condActions[i];
recordActions(cond.actionList);
}
}
}
@Override
public void setDecoderDictionary(Dictionary dict)
{
m_dictionary = dict;
}
@Override
public void header(Header h)
{
m_header = h;
}
/**
* -----------------------------------------------
* END: override TagHandler.
* -----------------------------------------------
*/
}