| /* |
| * 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. |
| * ----------------------------------------------- |
| */ |
| } |