2011-02-14

Thumbs.dbのフォーマット

WindowsのThumbs.dbはOLE Compound DocumentとかStructured Storageという形式でできている。Wordのdocファイルにも使われていて、要はアーカイブの一種らしい。

この形式を解凍する方法自体はメモしない。ここでは内部にどういうファイルが入っているかだけ書く。

これを読み込むライブラリは、Rubyではruby-oleジェム、PerlならWin32::OLE、JavaならApache POIのPOIFS、CならCOMのIStorageとかPOLEというライブラリがある。

ディレクトリの内容

そのたぐいのライブラリで解凍すると、中身のディレクトリはこうなっている。フォルダはなく、いくつかバイナリファイルが入っているだけ。

  • Catalog
  • 01
  • 1
  • 11
  • 2
  • 21
  • 3
  • 4
  • 5
  • ...

内容としては、名前が数字のファイルがJPEGファイルにヘッダがくっついたもので、Catalogにその数字と実ファイル名の対応付けが記録されている。

各サムネイルにはIDがついている。

不思議なのはIDとファイル名が文字列として反転しているというところ。

idが5のサムネイルは5というファイルに入っているが、idが13のサムネイルは31というファイルに入る。

カタログ (Catalog ファイル)

Catalogのフォーマットは先頭からこう。数値は全てUint32LE。

  • ヘッダが16バイト
    • 4バイト~8バイト目にサムネイル数
  • サムネイル数だけ以下を繰り返し(各エントリのサイズは不定)
    • エントリ全体のサイズ
    • ID
    • 更新日時
    • 何かの数値
    • UTF16LEでファイル名
    • 0でパディング

サムネイル (0, 1, 11, 2, 21 ..)

サムネイル画像のファイルの内容は以下のとおり。

  • ヘッダ (12バイトか16バイト)
  • JPEGファイル

ヘッダの長さは可変というか、12バイトか16バイトかのどちらからしい。

本来ならヘッダにあるタイプを調べるべきなのだろうが、とりあえずJPEGファイルの頭にあるマジックナンバー(0xFF 0xD8)を目印にすればヘッダの内容を読む必要はない。

実装

画像を吐き出すだけのツールを作るなら、流れはこうなる。

  • Structured Storageのライブラリを使ってthumbs.dbを読み込む
  • Catalogファイルを取り出して上のフォーマットで解釈する
  • 各ファイル名についてサムネイルを取り出し、JPEGファイルをその辺に書き出す

これをRubyとC++で書いた。

手抜き

以上はlibforensicsというPythonのライブラリのlf.win.shell.thumbsdbのコードを読んだだけなので、このライブラリを使うのが一番早いかもしれない。

こういった解体処理をする必要があるのは元のファイル名が欲しいときだけで、サムネイル画像を取り出したいだけならまじめに読み込む必要はない。

無圧縮で暗号化もされていないので、thumbs.dbを普通に開き、JPEGのSOI(0xFF 0xD8)とEOI(0xFF 0xD9)を探して次々書き出せばすむ。

(copied from tumblr)

2011-02-06

android.util.Config.DEBUGが役に立たない

public static final boolean DEBUG

If this is a debug build, this field will be true.

android.util.ConfigというクラスにDEBUGというbooleanの定数がある。説明の通りデバッグビルドかどうかこの定数を通じて調べることができる。これは嘘ではないのだが、アプリケーション(apk)のビルドのことではなく、デバイスがデバッグビルドかどうかというのを見るものらしい。つまり、アプリケーションの開発者の欲しがるような「LOGのメソッドを呼ぶか切り替えるスイッチ」ではない。

代案としてあがっているものはどれも微妙に意味あいが違う。

  • ユニットテストを真面目に書く (・・・ようにすれば、デバッグのためのログは必要なくなるはず)

ウィザードモードなんかをつけたい場合には応用がきかない。

  • デバッガが接続されているか調べる

if (Debug.isDebuggerConnected()) { ... }

見た目に綺麗だが、「実機に入れて動かしていたら落ちたのでログを見たい」という場合とは使いどころが合わない。

  • マニフェスト(AndroidManifest.xml) の属性設定でDebuggableを切り替え、それを調べる

if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { ... }

おそらくこれが最もまっとうな方法。設定ファイルに書き込む欄があり、それを調べる手段があるのだから、利用しない手はない。ApplicationInfoを取得するのに手間がかかるのが難点。

  • 自前で定数を定義する

static import com.example.android.app.StaticConfig.DEBUG;

if (DEBUG) { ... }

上に出た代案の問題はどれも問題がある。条件がコンパイル時定数でないので最適化されず、結局コンパイル後にも条件の判定やLOG文は残ってしまう。「#ifdefの代わり」としては結局これしかないように思う。

DEBUG = true; DEBUG = false; を手で切り替える手間を省けないかと考えている。antの-pre-compileタスクあたりにコード生成を挟めばよいのだろうか。

(copied from tumblr)