flash.swf.ActionDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swfutils Show documentation
Show all versions of swfutils Show documentation
The Apache Royale Compiler SWF Utility classes
/*
*
* 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.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.DebugTable;
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.io.IOException;
import java.util.ArrayList;
/**
* AS2 decoder.
*/
public class ActionDecoder
implements ActionConstants
{
private SwfDecoder reader;
private DebugTable debug;
private boolean keepOffsets;
private int actionCount;
public ActionDecoder(SwfDecoder reader)
{
this(reader, null);
}
public ActionDecoder(SwfDecoder reader, DebugTable debug)
{
this.reader = reader;
this.debug = debug;
}
public void setKeepOffsets(boolean b)
{
keepOffsets = b;
}
/**
* consume actions until length bytes are used up.
* @param length
* @throws IOException
*/
public ActionList decode(int length) throws IOException { return decode(length, true); }
/**
* consume actions until length bytes are used up
* @param length
* @param throwExceptions - if false exceptions will NOT be thrown. This is
* used for decoding a series of opcodes which may not be complete on their own.
* @throws IOException
*/
public ActionList decode(int length, boolean throwExceptions) throws IOException
{
int startOffset = reader.getOffset();
int end = startOffset+length;
boolean ending = false;
ActionFactory factory = new ActionFactory(length, startOffset, actionCount);
try
{
for (int offset = startOffset; offset < end; offset = reader.getOffset())
{
int opcode = reader.readUI8();
if (opcode > 0)
{
if (ending)
throw new SwfFormatException("unexpected bytes after sactionEnd: " + opcode);
factory.setActionOffset(actionCount, offset);
decodeAction(opcode, offset, factory);
actionCount++;
}
else if (opcode == 0)
{
ending = true;
}
else
{
break;
}
}
// keep track of the end too
factory.setActionOffset(actionCount, reader.getOffset());
}
catch(ArrayIndexOutOfBoundsException aio)
{
if (throwExceptions)
throw aio;
}
catch(SwfFormatException swf)
{
if (throwExceptions)
throw swf;
}
return factory.createActionList(keepOffsets);
}
public ClipActions decodeClipActions(int length) throws IOException
{
ClipActions a = new ClipActions();
reader.readUI16(); // must be 0
a.allEventFlags = decodeClipEventFlags(reader);
ArrayList list = new ArrayList();
ClipActionRecord record = decodeClipActionRecord();
while (record != null)
{
list.add(record);
record = decodeClipActionRecord();
}
a.clipActionRecords = list;
return a;
}
private ClipActionRecord decodeClipActionRecord() throws IOException
{
int flags = decodeClipEventFlags(reader);
if (flags != 0)
{
ClipActionRecord c = new ClipActionRecord();
c.eventFlags = flags;
// this tells us how big the action block is
int size = (int)reader.readUI32();
if ((flags & ClipActionRecord.keyPress) != 0)
{
size--;
c.keyCode = reader.readUI8();
}
c.actionList = decode(size);
return c;
}
else
{
return null;
}
}
private int decodeClipEventFlags(SwfDecoder r) throws IOException
{
int flags;
if (r.swfVersion >= 6)
flags = (int)r.readUI32();
else
flags = r.readUI16();
return flags;
}
private void decodeAction(int opcode, int offset, ActionFactory factory) throws IOException
{
LineRecord line = debug != null ? debug.getLine(offset) : null;
if (line != null)
{
factory.setLine(offset, line);
}
// interleave register records in the action list
RegisterRecord record = (debug != null) ? debug.getRegisters(offset) : null;
if (record != null)
{
factory.setRegister(offset, record);
}
Action a;
if (opcode < 0x80)
{
a = ActionFactory.createAction(opcode);
factory.setAction(offset, a);
return;
}
int len = reader.readUI16();
int pos = offset+3;
switch (opcode)
{
case sactionDefineFunction:
a = decodeDefineFunction(pos, len);
factory.setAction(offset, a);
return;
case sactionDefineFunction2:
a = decodeDefineFunction2(pos, len);
factory.setAction(offset, a);
return;
case sactionWith:
a = decodeWith(factory);
break;
case sactionTry:
a = decodeTry(factory);
break;
case sactionPush:
Push p = decodePush(offset, pos+len, factory);
checkConsumed(pos, len, p);
return;
case sactionStrictMode:
a = decodeStrictMode();
break;
case sactionCall:
// this actions opcode has the high bit set, but there is no length. considered a permanent bug.
a = ActionFactory.createCall();
break;
case sactionGotoFrame:
a = decodeGotoFrame();
break;
case sactionGetURL:
a = decodeGetURL();
break;
case sactionStoreRegister:
a = decodeStoreRegister();
break;
case sactionConstantPool:
a = decodeConstantPool();
break;
case sactionWaitForFrame:
a = decodeWaitForFrame(opcode, factory);
break;
case sactionSetTarget:
a = decodeSetTarget();
break;
case sactionGotoLabel:
a = decodeGotoLabel();
break;
case sactionWaitForFrame2:
a = decodeWaitForFrame(opcode, factory);
break;
case sactionGetURL2:
a = decodeGetURL2();
break;
case sactionJump:
case sactionIf:
a = decodeBranch(opcode, factory);
break;
case sactionGotoFrame2:
a = decodeGotoFrame2();
break;
default:
a = decodeUnknown(opcode, len);
break;
}
checkConsumed(pos, len, a);
factory.setAction(offset, a);
}
private Try decodeTry(ActionFactory factory) throws IOException
{
Try a = new Try();
a.flags = reader.readUI8();
int trySize = reader.readUI16();
int catchSize = reader.readUI16();
int finallySize = reader.readUI16();
if (a.hasRegister())
a.catchReg = reader.readUI8();
else
a.catchName = reader.readString();
// we have now consumed the try action. what follows is label mgmt
int tryEnd = reader.getOffset() + trySize;
a.endTry = factory.getLabel(tryEnd);
// place the catchLabel to mark the end point of the catch handler
if (a.hasCatch())
a.endCatch = factory.getLabel(tryEnd + catchSize);
// place the finallyLabel to mark the end point of the finally handler
if (a.hasFinally())
a.endFinally = factory.getLabel(tryEnd + finallySize + (a.hasCatch() ? catchSize : 0));
return a;
}
private GotoFrame2 decodeGotoFrame2() throws IOException
{
GotoFrame2 a = new GotoFrame2();
a.playFlag = reader.readUI8();
return a;
}
private Branch decodeBranch(int code, ActionFactory factory) throws IOException
{
Branch a = new Branch(code);
int offset = reader.readSI16();
int target = offset + reader.getOffset();
a.target = factory.getLabel(target);
return a;
}
private WaitForFrame decodeWaitForFrame(int opcode, ActionFactory factory) throws IOException
{
WaitForFrame a = new WaitForFrame(opcode);
if (opcode == sactionWaitForFrame)
a.frame = reader.readUI16();
int skipCount = reader.readUI8();
int skipTarget = actionCount+1 + skipCount;
factory.addSkipEntry(a, skipTarget);
return a;
}
private GetURL2 decodeGetURL2() throws IOException
{
GetURL2 a = new GetURL2();
a.method = reader.readUI8();
return a;
}
private GotoLabel decodeGotoLabel() throws IOException
{
GotoLabel a = new GotoLabel();
a.label = reader.readString();
return a;
}
private SetTarget decodeSetTarget() throws IOException
{
SetTarget a = new SetTarget();
a.targetName = reader.readString();
return a;
}
private ConstantPool decodeConstantPool() throws IOException
{
ConstantPool cpool = new ConstantPool();
int count = reader.readUI16();
cpool.pool = new String[count];
for (int i = 0; i < count; i++)
{
cpool.pool[i] = reader.readString();
}
return cpool;
}
private StoreRegister decodeStoreRegister() throws IOException
{
int register = reader.readUI8();
return ActionFactory.createStoreRegister(register);
}
private GetURL decodeGetURL() throws IOException
{
GetURL a = new GetURL();
a.url = reader.readString();
a.target = reader.readString();
return a;
}
private GotoFrame decodeGotoFrame() throws IOException
{
GotoFrame a = new GotoFrame();
a.frame = reader.readUI16();
return a;
}
private Unknown decodeUnknown(int opcode, int length) throws IOException
{
Unknown a = new Unknown(opcode);
a.data = new byte[length];
reader.readFully(a.data);
return a;
}
private StrictMode decodeStrictMode() throws IOException
{
boolean mode = reader.readUI8() != 0;
return ActionFactory.createStrictMode(mode);
}
private Push decodePush(int offset, int end, ActionFactory factory) throws IOException
{
Push p;
do
{
int pushType = reader.readUI8();
switch (pushType)
{
case Push.kPushStringType: // string
p = ActionFactory.createPush(reader.readString());
break;
case Push.kPushFloatType: // float
float fvalue = Float.intBitsToFloat((int) reader.readUI32());
p = ActionFactory.createPush(fvalue); // value
break;
case Push.kPushNullType: // null
p = ActionFactory.createPushNull();
break;
case Push.kPushUndefinedType: // undefined
p = ActionFactory.createPushUndefined();
break;
case Push.kPushRegisterType: // register
p = ActionFactory.createPushRegister(reader.readUI8());
break;
case Push.kPushBooleanType: // boolean
p = ActionFactory.createPush(reader.readUI8() != 0);
break;
case Push.kPushDoubleType: // double
// read two 32 bit little-endian values in big-endian order. weird.
long hx = reader.readUI32();
long lx = reader.readUI32();
p = ActionFactory.createPush(Double.longBitsToDouble((hx << 32) | (lx & 0xFFFFFFFFL)));
break;
case Push.kPushIntegerType: // integer
p = ActionFactory.createPush((int)reader.readUI32());
break;
case Push.kPushConstant8Type: // 8-bit cpool reference
p = ActionFactory.createPushCpool(reader.readUI8());
break;
case Push.kPushConstant16Type: // 16-bit cpool reference
p = ActionFactory.createPushCpool(reader.readUI16());
break;
default:
throw new SwfFormatException("Unknown push data type "+pushType);
}
factory.setAction(offset, p);
offset = reader.getOffset();
}
while (offset < end);
return p;
}
private DefineFunction decodeDefineFunction(int pos, int len) throws IOException
{
DefineFunction a = new DefineFunction(ActionConstants.sactionDefineFunction);
a.name = reader.readString();
int number = reader.readUI16();
a.params = new String[number];
for (int i = 0; i < number; i++)
{
a.params[i] = reader.readString();
}
a.codeSize = reader.readUI16();
checkConsumed(pos, len, a);
a.actionList = decode(a.codeSize);
return a;
}
private DefineFunction decodeDefineFunction2(int pos, int len) throws IOException
{
DefineFunction a = new DefineFunction(ActionConstants.sactionDefineFunction2);
a.name = reader.readString();
int number = reader.readUI16();
a.params = new String[number];
a.paramReg = new int[number];
a.regCount = reader.readUI8();
a.flags = reader.readUI16();
for (int i = 0; i < number; i++)
{
a.paramReg[i] = reader.readUI8();
a.params[i] = reader.readString();
}
a.codeSize = reader.readUI16();
checkConsumed(pos, len, a);
a.actionList = decode(a.codeSize);
return a;
}
private void checkConsumed(int pos, int len, Action a) throws IOException
{
int consumed = reader.getOffset() - pos;
if (consumed != len)
{
throw new SwfFormatException(a.getClass().getName() + ": " + consumed + " was read. " + len + " was required.");
}
}
private With decodeWith(ActionFactory factory) throws IOException
{
With a = new With();
int size = reader.readUI16();
int target = size + reader.getOffset();
a.endWith = factory.getLabel(target);
return a;
}
}