Package flash.swf

Source Code of flash.swf.ActionEncoder$UpdateEntry

/*
*
*  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.swf;

import flash.swf.actions.Branch;
import flash.swf.actions.ConstantPool;
import flash.swf.actions.DefineFunction;
import flash.swf.actions.GetURL;
import flash.swf.actions.GetURL2;
import flash.swf.actions.GotoFrame;
import flash.swf.actions.GotoFrame2;
import flash.swf.actions.GotoLabel;
import flash.swf.actions.Label;
import flash.swf.actions.Push;
import flash.swf.actions.SetTarget;
import flash.swf.actions.StoreRegister;
import flash.swf.actions.StrictMode;
import flash.swf.actions.Try;
import flash.swf.actions.Unknown;
import flash.swf.actions.WaitForFrame;
import flash.swf.actions.With;
import flash.swf.debug.LineRecord;
import flash.swf.debug.RegisterRecord;
import flash.swf.types.ActionList;
import flash.swf.types.ClipActionRecord;
import flash.swf.types.ClipActions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
* AS2 encoder.
*/
public class ActionEncoder extends ActionHandler
{
    private SwfEncoder writer;
    private DebugEncoder debug;

    // map label -> position
    private HashMap<Label, LabelEntry> labels;

    private ArrayList<UpdateEntry> updates;
    private int actionCount;

    public ActionEncoder(SwfEncoder writer, DebugEncoder debug)
    {
        this.writer = writer;
        this.debug = debug;
        labels = new HashMap<Label, LabelEntry>();
        updates = new ArrayList<UpdateEntry>();
    }

  private static class UpdateEntry
  {
    int anchor;     // location to subtract to compute delta
    int updatePos;  // byte offset to update with delta value
        Action source;

    public UpdateEntry(int anchor, int updatePos, Action source)
    {
      this.anchor = anchor;
      this.updatePos = updatePos;
            this.source = source;
    }
  }

    private static class LabelEntry
    {
        int offset; // byte offset in swf file
        int count;  // action ordinal number (Nth action in current block)

        public LabelEntry(int offset, int count)
        {
            this.count = count;
            this.offset = offset;
        }
    }

    private int getLabelOffset(Label label)
    {
        assert (labels.containsKey(label)) : ("missing label");
        return labels.get(label).offset;
    }

    private int getLabelCount(Label label)
    {
        assert (labels.containsKey(label)) : ("missing label");
        return labels.get(label).count;
    }

  public void encode(ActionList actionList)
  {
        // write the actions
    for (int i=0; i < actionList.size(); i++)
    {
      Action a = actionList.getAction(i);

      switch (a.code)
      {
            // don't update actionCount for synthetic opcodes
            case ActionList.sactionLabel:
                a.visit(this);
                break;
      case ActionList.sactionLineRecord:
        if (debug != null)
          debug.offset(writer.getPos(), (LineRecord)a);
        break;

      case ActionList.sactionRegisterRecord:
        if (debug != null)
          debug.registers(writer.getPos(), (RegisterRecord)a);
        break;

            // the remaining types need counting
      case ActionConstants.sactionPush:
        i = encodePush((Push)a, i, actionList);
                actionCount++;
        break;
      default:
        if (a.code < 0x80)
          writer.writeUI8(a.code);
        else
          a.visit(this);
                actionCount++;
        break;
      }
    }

        patchForwardBranches();
    }

    private void patchForwardBranches()
    {
        // now patch forward branches
        for (Iterator<UpdateEntry> i = updates.iterator(); i.hasNext();)
        {
            UpdateEntry entry = i.next();
            switch (entry.source.code)
            {
            case ActionConstants.sactionIf:
            case ActionConstants.sactionJump:
                int target = getLabelOffset(((Branch)entry.source).target);
                writer.writeSI16at(entry.updatePos, target - entry.anchor);
                break;
            case ActionConstants.sactionWith:
                int endWith = getLabelOffset(((With)entry.source).endWith);
                writer.writeUI16at(entry.updatePos, endWith - entry.anchor);
                break;
            case ActionConstants.sactionWaitForFrame:
            case ActionConstants.sactionWaitForFrame2:
                int skipTarget = getLabelCount(((WaitForFrame)entry.source).skipTarget);
                writer.writeUI8at(entry.updatePos, skipTarget - entry.anchor);
                break;
            case ActionConstants.sactionTry:
                Try t = (Try) entry.source;
                int endTry = getLabelOffset(t.endTry);
                writer.writeUI16at(entry.updatePos, endTry - entry.anchor);
                entry.anchor = endTry;
                if (t.hasCatch())
                {
                    int endCatch = getLabelOffset(t.endCatch);
                    writer.writeUI16at(entry.updatePos+2, endCatch - entry.anchor);
                    entry.anchor = endCatch;
                }
                if (t.hasFinally())
                {
                    int endFinally = getLabelOffset(t.endFinally);
                    writer.writeUI16at(entry.updatePos+4, endFinally - entry.anchor);
                }
                break;
            default:
                assert false : ("invalid action in UpdateEntry");
            }
        }
    }

//    public void inlineBinaryOp(InlineBinaryOp op)
//    {
//        writer.writeU8(op.code);
//        writer.writeU8(op.dst);
//        encodeOperand(op.lhs, op.rhs);
//    }

//    private void encodeOperand(Object lhs, Object rhs)
//    {
//        int lhsType = operandType(lhs);
//        int rhsType = operandType(rhs);
//        writer.writeU8(lhsType >> 4 | rhsType);
//        encodeOperand(lhsType, lhs);
//        encodeOperand(rhsType, rhs);
//    }
//
//    private void encodeOperand(Object operand)
//    {
//        int type = operandType(operand);
//        writer.writeU8(type);
//        encodeOperand(type, operand);
//    }
//
//    public void inlineBranchWhenFalse(InlineBranchWhenFalse op)
//    {
//        writer.writeU8(op.code);
//        int pos = writer.getPos();
//        writer.writeU8(op.cond);
//        encodeOperand(op.lhs, op.rhs);
//        Integer offset = (Integer) labels.get(op.target);
//        if (offset != null)
//        {
//            // label came earlier
//            writer.writeU16(offset.intValue() - pos - 2);
//        }
//        else
//        {
//            // label comes later. don't know the offset yet.
//      updates.add(new UpdateEntry(pos+2, pos, op));
//            writer.writeU16(0);
//        }
//    }

//    public void inlineGetMember(InlineGetMember op)
//    {
//        writer.writeU8(op.code);
//        writer.writeU8(op.dst);
//        writer.writeU8(op.src);
//        encodeOperand(op.member);
//    }
//
//    public void inlineSetMember(InlineSetMember op)
//    {
//        writer.writeU8(op.code);
//        writer.writeU8(op.dst);
//        encodeOperand(op.member, op.src);
//    }

//    public void inlineSetRegister(InlineSetRegister op)
//    {
//        writer.writeU8(op.code);
//        writer.writeU8(op.dst);
//        encodeOperand(op.value);
//    }
//
//    public void inlineUnaryRegOp(InlineUnaryRegOp op)
//    {
//        writer.writeU8(op.code);
//        writer.writeU8(op.register);
//    }

//    private void encodeOperand(int opType, Object operand)
//    {
//        switch (opType)
//        {
//        case ActionConstants.kInlineTrue:
//        case ActionConstants.kInlineFalse:
//        case ActionConstants.kInlineNull:
//        case ActionConstants.kInlineUndefined:
//            // do nothing, type implies value
//            break;
//        case ActionConstants.kInlineConstantByte:
//        case ActionConstants.kInlineChar:
//        case ActionConstants.kInlineRegister:
//            int i = ((Number)operand).intValue();
//            writer.writeU8(i);
//            break;
//        case ActionConstants.kInlineConstantWord:
//        case ActionConstants.kInlineShort:
//            i = ((Number)operand).intValue();
//            writer.writeU16(i);
//            break;
//        case ActionConstants.kInlineLong:
//            i = ((Number)operand).intValue();
//            writer.write32(i);
//            break;
//        case ActionConstants.kInlineDouble:
//            long num = Double.doubleToLongBits(((Number)operand).doubleValue());
//            writer.write32((int)(num>>32));
//            writer.write32((int)num);
//            break;
//        }
//    }

//    private int operandType(Object operand)
//    {
//        if (operand == Boolean.TRUE)
//            return ActionConstants.kInlineTrue;
//        else if (operand == Boolean.FALSE)
//            return ActionConstants.kInlineFalse;
//        else if (operand == ActionFactory.UNDEFINED)
//            return ActionConstants.kInlineUndefined;
//        else if (operand == ActionFactory.STACKTOP)
//            return ActionConstants.kInlineStack;
//        else if (operand == null)
//            return ActionConstants.kInlineNull;
//        else if (operand instanceof Short)
//            return ((Short)operand).intValue() < 256 ? ActionConstants.kInlineConstantByte : ActionConstants.kInlineConstantWord;
//        else if (operand instanceof Double)
//            return ActionConstants.kInlineDouble;
//        else if (operand instanceof Integer)
//            return (((Integer)operand).intValue() & ~0xFF) == 0 ? ActionConstants.kInlineChar :
//                   (((Integer)operand).intValue() & ~0xFFFF) == 0 ? ActionConstants.kInlineShort :
//                    ActionConstants.kInlineLong;
//        else if (operand instanceof Byte)
//            return ActionConstants.kInlineRegister;
//        else
//            throw new IllegalArgumentException("unknown operand type " + operand.getClass().getName());
//    }

    public void call(Action action)
    {
        writer.writeUI8(action.code);
        // this action's opcode 0x9E has hi bit set, but it never has data.  considered a permanent minor bug.
        writer.writeUI16(0);
    }

    public void constantPool(ConstantPool action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI16(action.pool.length);
        for (int i = 0; i < action.pool.length; i++)
        {
            writer.writeString(action.pool[i]);
        }
        updateActionHeader(updatePos);
    }

    private void updateActionHeader(int updatePos)
    {
        int length = (writer.getPos()-updatePos)-2;
        if (length >= 0x10000)
    {
      assert false : ("action length ("+length+") exceeds 64K");
    }
        writer.writeUI16at(updatePos, length);
    }

    private int encodeActionHeader(Action action)
    {
        writer.writeUI8(action.code);
        int updatePos = writer.getPos();
        writer.writeUI16(0);
        return updatePos;
    }

    public void defineFunction(DefineFunction action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeString(action.name);
        writer.writeUI16(action.params.length);
        for (int i = 0; i < action.params.length; i++)
        {
            writer.writeString(action.params[i]);
        }
        int pos = writer.getPos();
        writer.writeUI16(0); // codesize placeholder
        updateActionHeader(updatePos);

        new ActionEncoder(writer, debug).encode(action.actionList);

        writer.writeUI16at(pos, (writer.getPos()-pos)-2);
    }

    public void defineFunction2(DefineFunction action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeString(action.name);
        writer.writeUI16(action.params.length);
        writer.writeUI8(action.regCount);
        writer.writeUI16(action.flags);

        for (int i = 0; i < action.params.length; i++)
        {
            writer.writeUI8(action.paramReg[i]);
            writer.writeString(action.params[i]);
        }

        int pos = writer.getPos();
        writer.writeUI16(0); // placeholder
        updateActionHeader(updatePos);

    new ActionEncoder(writer, debug).encode(action.actionList);

        writer.writeUI16at(pos, (writer.getPos()-pos)-2);
    }

    public void getURL(GetURL action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeString(action.url);
        writer.writeString(action.target);
        updateActionHeader(updatePos);
    }

    public void getURL2(GetURL2 action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI8(action.method);
        updateActionHeader(updatePos);
    }

    public void gotoFrame(GotoFrame action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI16(action.frame);
        updateActionHeader(updatePos);
    }

    public void gotoFrame2(GotoFrame2 action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI8(action.playFlag);
        updateActionHeader(updatePos);
    }

    public void gotoLabel(GotoLabel action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeString(action.label);
        updateActionHeader(updatePos);
    }

    public void ifAction(Branch action)
    {
        encodeBranch(action);
    }

    public void jump(Branch action)
    {
        encodeBranch(action);
    }

    private void encodeBranch(Branch branch)
    {
        writer.writeUI8(branch.code);
        writer.writeUI16(2);
        int pos = writer.getPos();
        if (labels.containsKey(branch.target))
        {
            // label came earlier
            writer.writeSI16(getLabelOffset(branch.target) - pos - 2);
        }
        else
        {
            // label comes later. don't know the offset yet.
      updates.add(new UpdateEntry(pos+2, pos, branch));
            writer.writeSI16(0);
        }
    }

  public void with(With action)
  {
    writer.writeUI8(action.code);
    writer.writeUI16(2);
    // label comes later, don't know offset yet
    int pos = writer.getPos();
    updates.add(new UpdateEntry(pos+2, pos, action));
    writer.writeUI16(0);
  }

    public void waitForFrame(WaitForFrame action)
    {
        writer.writeUI8(action.code);
        writer.writeUI16(3);
        writer.writeUI16(action.frame);
        int pos = writer.getPos();
        updates.add(new UpdateEntry(actionCount+1, pos, action));
        writer.writeUI8(0);
    }

    public void waitForFrame2(WaitForFrame action)
    {
        writer.writeUI8(action.code);
        writer.writeUI16(1);
        int pos = writer.getPos();
        updates.add(new UpdateEntry(actionCount+1, pos, action));
        writer.writeUI8(0);
    }

    public void label(Label label)
    {
        assert (!labels.containsKey(label)) : ("found duplicate label");
    int labelPos = writer.getPos();
        labels.put(label, new LabelEntry(labelPos, actionCount));
    }

  /**
   * encode a run of push actions into one action record.  The player
   * supports this compact encoding since push is such a common
   * opcode.  the format is:
   *
   *   sactionPush type1 value1 type2 value2 ...
   *
   * @param push
   * @param j the index of the starting push action
   * @param actions
   * @return the index of the last push action encoded.  the next action will
   * not be a push action.
   */
    public int encodePush(Push push, int j, ActionList actions)
    {
        int updatePos = encodeActionHeader(push);
        do
    {
            Object value = push.value;
      int type = Push.getTypeCode(value);
      writer.writeUI8(type);

            switch (type)
      {
      case 0: // string
        writer.writeString(value.toString());
        break;
      case 1: // float
        int bits = Float.floatToIntBits(((Float) value).floatValue());
        writer.write32(bits);
        break;
      case 2: // null
        break;
      case 3: // undefined
        break;
      case 4: // register
        writer.writeUI8(((Byte) value).intValue() & 0xFF);
        break;
      case 5: // boolean
        writer.writeUI8(((Boolean) value).booleanValue() ? 1 : 0);
        break;
      case 6: // double
        double d = ((Double) value).doubleValue();
        long num = Double.doubleToLongBits(d);
        writer.write32((int)(num>>32));
                writer.write32((int)num);
        break;
      case 7: // integer
        writer.write32(((Integer) value).intValue());
        break;
      case 8: // const8
        writer.writeUI8(((Short) value).intValue());
        break;
      case 9: // const16
        writer.writeUI16(((Short) value).intValue() & 0xFFFF);
        break;
      }

            if (debug == null)
            {
                // ignore line records if we aren't debugging
                while (j+1 < actions.size() && actions.getAction(j+1).code == ActionList.sactionLineRecord)
                    j++;
            }

      Action a;
      if (++j < actions.size()
          && (a=actions.getAction(j)).code == ActionConstants.sactionPush)
            {
        push = (Push) a;
            }
            else
            {
        push = null;
            }
    }
        while (push != null);
        updateActionHeader(updatePos);
    return j-1;
    }

    public void setTarget(SetTarget action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeString(action.targetName);
        updateActionHeader(updatePos);
    }

    public void storeRegister(StoreRegister action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI8(action.register);
        updateActionHeader(updatePos);
    }

    public void strictMode(StrictMode action)
    {
        int updatePos = encodeActionHeader(action);
        writer.writeUI8(action.mode ? 1 : 0);
        updateActionHeader(updatePos);
    }

    public void tryAction(Try a)
    {
        int updatePos = encodeActionHeader(a);

        writer.writeUI8(a.flags);
        int trySizePos = writer.getPos();
        writer.writeUI16(0)// try size
        writer.writeUI16(0)// catch size
        writer.writeUI16(0)// finally size

    if (a.hasRegister())
      writer.writeUI8(a.catchReg);
    else
          writer.writeString(a.catchName);

    // we have emitted the try action, what follows is label mgmt
    updateActionHeader(updatePos);

        int tryStart = writer.getPos();
        updates.add(new UpdateEntry(tryStart, trySizePos, a));
    }

    public void unknown(Unknown action)
    {
        int updatePos = encodeActionHeader(action);
        writer.write(action.data);
        updateActionHeader(updatePos);
    }

    public void encodeClipActions(ClipActions clipActions)
    {
        writer.writeUI16(0);

        encodeClipEventFlags(clipActions.allEventFlags, writer);

        Iterator it = clipActions.clipActionRecords.iterator();
        while (it.hasNext())
        {
            ClipActionRecord r = (ClipActionRecord) it.next();
            encodeClipActionRecord(r);
        }

        if (writer.swfVersion >= 6)
            writer.write32(0);
        else
            writer.writeUI16(0);
    }


    private void encodeClipActionRecord(ClipActionRecord r)
    {
        encodeClipEventFlags(r.eventFlags, writer);

        int pos = writer.getPos();
        writer.write32(0)// offset placeholder

        if ((r.eventFlags & ClipActionRecord.keyPress) != 0)
        {
            writer.writeUI8(r.keyCode);
        }

    encode(r.actionList);

        writer.write32at(pos, (writer.getPos()-pos)-4);
    }

    private void encodeClipEventFlags(int flags, SwfEncoder w)
    {
        if (w.swfVersion >= 6)
            w.write32(flags);
        else
            w.writeUI16(flags);
    }

}
TOP

Related Classes of flash.swf.ActionEncoder$UpdateEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.