先日友人が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 件のコメント:
あっやべ。リンク間違ってた。
コメントを投稿