2010年3月14日日曜日

FlashLiteで引数を埋め込む(Java)

先日友人がFlashLiteでの引数埋込に苦戦してた。
携帯で個体識別番号などの引数を元にFlashを書き換えたいらしい。

ここに方式はあるんだけど、、、って事でJava化してみた。




public static String inCode = "utf-8";
public static String outCode = "shift-jis";
/**
* FlashLite への引数の埋込
* @param oldFile
* @param argMap
* @return
*/
public static byte[] createArgEmbedSwf(byte[] oldFile, Map<String, String> argMap) {

ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
//rectbitの3ビット右シフト
byte rectbit = (byte)((int)oldFile[8] / 8);

//$headlen = ceil(((( 8 - (($rb*4+5)&7) )&7)+ $rb*4 + 5 )/8) + 12 + 5;
double dblHead = ((( 8 - ((rectbit*4+5)&7) )&7)+ rectbit*4 + 5 ) / 8;
//ヘッダ長を取得
int headLen = (int)Math.ceil(dblHead) + 12 + 5;

//引数部分の作成
byte[] doActionTag = createArg(argMap);

//$newsize = $oldsize+strlen($doactiontag);
int oldSize = oldFile.length;
//新しいサイズを取得
int newSize = oldSize + doActionTag.length;

//新しいヘッダを作成
byte[] newHeader = createHeader(oldFile,headLen,newSize);
//ヘッダ後のデータを取得
byte[] tail = createTail(oldFile,headLen);
try {
//タグ部分の書き込み
byteStream.write(newHeader);
byteStream.write(doActionTag);
byteStream.write(tail);
} catch (IOException e) {
e.printStackTrace();
}
return byteStream.toByteArray();
}
/**
* 新しいヘッダーの作成
* @param buf
* @param headLen
* @param newSize
* @return
*/
private static byte[] createHeader(byte[] buf, int headLen, int newSize) {

//ヘッダの位置を取得
//$head = $headtmp.fread($fr,$headlen-9);
//$newhead = substr($head,0,4).h32($newsize).substr($head,8);

byte[] newHeader = new byte[headLen];
//4バイトでint値をバイトに変換
byte[] newHeadSize = changeBytes(newSize,4);
//長さを変更
for ( int cnt = 0; cnt < headLen; ++cnt ) {
if ( cnt >= 4 && cnt < 8 ) {
newHeader[cnt] = newHeadSize[cnt-4];
} else {
newHeader[cnt] = buf[cnt];
}
}
return newHeader;
}
//3f 03 18 00 00 00 96 08
//00 00 6d 79 6e 61 6d 65
//00 96 08 00 00 74 61 77
//74 61 77 00 1d 00

//3f 03 -- タグはじまり, Type=12(DoActionタグ)
//18 00 00 00 -- タグ長さ 0x18 = 24 byte(ここからタグ終わりまでの長さ)

//96 -- ActionPush
//08 00 -- Pushするものの長さ, 8 byte
//00 -- Pushするものは文字列
//6d 79 6e 61 6d 65 00 -- "myname" (ord("m")=109=0x6d など。00は文字列終端を意味)

//96 -- ActionPush
//08 00 -- Pushするものの長さ
//00  -- Pushするものは文字列
//74 61 77 74 61 77 00 -- "tawtaw"

//1d -- ActionSetVariable
//00 -- タグ終わり
/**
* タグ用のバイトを作成
* @param argMap
* @return
*/
private static byte[] createArg(Map<String, String> argMap) {

List<Byte> byteList = new ArrayList<Byte>();

//タグの始まりを設定
//$tag = "\x3f\x03";
byteList.add((byte)0x3f);
byteList.add((byte)0x03);

//タグの長さを取得
//$taglen = calctaglen($dataarray);
int tagLen = calcArgLen(argMap);

//$tag .= h32($taglen);
add(changeBytes(tagLen,4),byteList);

//foreach($dataarray as $key => $value)
Iterator<Entry<String,String>> itr = argMap.entrySet().iterator();
//キー数回繰り返す
while ( itr.hasNext() ) {
Entry<String,String> entry = itr.next();

//$tag .= "\x96".h16(strlen($key)+2)."\x00".$key."\x00";
addString(entry.getKey(),byteList);
//$tag .= "\x96".h16(strlen($value)+2)."\x00".$value."\x00";
addString(entry.getValue(),byteList);

//$tag .= "\x1d";
byteList.add((byte)0x1d);
}
//終端のバイトを設定
//$tag .= "\x00";
byteList.add((byte)0x00);
return change(byteList);
}

/**
* 引数の長さを取得
* @param argMap 引数用のマップ
* @return
*/
private static int calcArgLen(Map<String, String> argMap) {
int rtn = 0;
Iterator<Entry<String,String>> itr = argMap.entrySet().iterator();
//キー数回繰り返す
while ( itr.hasNext() ) {
Entry<String,String> entry = itr.next();
String key;
String value;
try {
key = new String(entry.getKey().getBytes(inCode),outCode);
value = new String(entry.getValue().getBytes(inCode),outCode);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列コード変換失敗",e);
}
//それぞれのバイト数とタグ用の11バイトを追加
try {
rtn += (key.getBytes(outCode).length + value.getBytes(outCode).length + 11);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列コード変換失敗",e);
}
}
//終端分を追加
return rtn + 1;
}

/**
* ヘッダの後ろを取得
* @param buf
* @param oldSize
* @param headLen
* @return
*/
private static byte[] createTail(byte[] buf,int headLen) {

int oldSize = buf.length - headLen;
byte[] tail = new byte[oldSize];
int idx = 0;
//$tail = fread($fr, $oldsize-$headlen);
for ( int cnt = headLen; cnt < buf.length; ++cnt ) {
tail[idx] = buf[cnt];
++idx;
}
return tail;
}


/**
* リストから配列に変更
* @param byteList
* @return
*/
private static byte[] change(List<Byte> byteList) {
byte[] rtnByte = new byte[byteList.size()];
int idx = 0;
for ( Byte b : byteList ) {
rtnByte[idx] = b;
++idx;
}
return rtnByte;
}

/**
* バイト配列への変更
* @param i
* @param leng
* @return
*/
private static byte[] changeBytes( int i ,int leng){
byte[] b = new byte[leng] ;
if ( leng > 4) {
b[3] = (byte)((i >>> 24 ) & 0xFF);
}
if ( leng > 3) {
b[2] = (byte)((i >>> 16 ) & 0xFF);
}
if ( leng > 2) {
b[1] = (byte)((i >>> 8 ) & 0xFF);
}
if ( leng > 1) {
b[0] = (byte)((i >>> 0 ) & 0xFF);
}
return b;
}

/**
* リストへの追加
*/
private static void add(byte[] byteArray,List<Byte> byteList) {
for ( byte b : byteArray ) {
byteList.add(b);
}
}

/**
* 文字列の追加
* @param key
* @param byteList
*/
private static void addString(String key, List<Byte> byteList) {

String tmp = null;
try {
tmp = new String(key.getBytes(inCode),outCode);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}

//$tag .= "\x96".h16(strlen($key)+2)."\x00".$key."\x00";
byteList.add((byte)0x96);
//文字列数を設定
byte[] lengByte;
try {
lengByte = changeBytes(tmp.getBytes(outCode).length + 2,2);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}
add(lengByte,byteList);

byteList.add((byte)0x00);
//文字列を設定
try {
add(tmp.getBytes(outCode),byteList);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}
byteList.add((byte)0x00);
}


byteのつなげ方とかが独自で、少ない時間での実装なので何か良い方法があるかも。
byte[]での変換に終わってるのは、GAE上での動作確認をしたかったからです。

blob型で保存して動的に変更させる事に成功しました。






byte[] newFile = FlashUtil.createArgEmbedSwf(targetFile.getBytes() , argMap);

try {
response.setContentType("application/x-shockwave-flash");
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
out.write(newFile);
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}



こんな感じかな?

日本語文字化けしちゃうけど、、、、まぁ方式がわかれば解決するでしょう。

1 件のコメント:

secondarykey さんのコメント...

あっやべ。リンク間違ってた。