UWSCでPNGファイルを解析する
Filrという、Flickrを任意のファイルストレージにしてしまうソフトがあるようです。
PNGに任意のファイルをつっこんで、そのPNGファイルを入れてしまうという仕掛け。
興味が湧いたので、PNGファイルを覗いてみた。
PNGフォーマット
8byteのシグネチャの後、「サイズ(4byte)、タイプ(4byte)、データ(サイズbyte)、CRC(4byte)」のチャンクが続く。
チャンクは、IHDRが最初で、IENDが最後。
あとは自由。
IDATが必須チャンクで、zlibにより圧縮されたイメージデータ。
カラーパレットを使う場合は、PLTEも必須。
基本的にビッグエンディアンの模様。(サイズ/CRCはそう。他チャンクのデータは知らん)
タイプの4文字は、大文字・小文字に意味がある。
CRCは、タイプとデータのもの。
CRC算出スクリプト
CRCの算出式がc言語で書いてあったので、UWSCスクリプトにしてみた。
crc.uws
OPTION EXPLICIT IFB GET_UWSC_NAME = "crc.uws" THEN DEF_DLL RtlMoveMemory(var BYTE[], string, DWORD): kernel32 DIM str = "tEXtAuthor" + CHR(0) + "junjune", len = LENGTH(str), buf[len-1] RtlMoveMemory(buf, str, len) PRINT FORMAT(len-4, 8, -1) + " " + FORMAT(CRC.calc(buf), 8, -1) ENDIF MODULE CRC // http://www.w3.org/TR/PNG/#D-CRCAppendix DIM crc_table[255] PROCEDURE CRC DIM c, n, k FOR n = 0 TO LENGTH(crc_table) - 1 c = n FOR k = 0 TO 7 IFB c AND 1 THEN c = $edb88320 xor INT(c / 2) ELSE c = c / 2 ENDIF NEXT crc_table[n] = c NEXT FEND FUNCTION update(crc, buf[], len) DIM c = crc, n FOR n = 0 TO len - 1 c = crc_table[(c xor buf[n]) and $ff] xor INT(c / 256) NEXT RESULT = c FEND FUNCTION calc(buf[], len=-1) IF len = -1 THEN len = LENGTH(buf) RESULT = update($ffffffff, buf, len) xor $ffffffff FEND ENDMODULE
CRC.calcにバイト配列を渡すだけ。
PNGファイルチェックスクリプト
シグネチャが正しいことを検証して、どんなチャンクがあるか列挙。
ついでにCRCが正しいか確認します。
OPTION EXPLICIT CALL Binary CALL crc DIM h = Binary.Open(INPUT("target PNG file?")) IF h = Binary.INVALID_HANDLE OR !Binary.Exist(h) THEN EXITEXIT // PNGシグネチャ確認 DIM buf = Binary.ReadArray(h, 8) IFB buf[0]<>$89 OR buf[1]<>$50 OR buf[2]<>$4E OR buf[3]<>$47 OR buf[4]<>$0D OR buf[5]<>$0A OR buf[6]<>$1A OR buf[7]<>$0A THEN PRINT "Not PNG Format" EXITEXIT ENDIF DIM t, size, crc WHILE !Binary.EOS(h) buf = Binary.ReadArray(h, 4) size = ((buf[0] * 256 + buf[1]) * 256 + buf[2]) * 256 + buf[3] buf = Binary.ReadArray(h, 4 + size) t = CHR(buf[0]) + CHR(buf[1]) + CHR(buf[2]) + CHR(buf[3]) crc = CRC.calc(buf, 4 + size) buf = Binary.ReadArray(h, 4) IFB ((buf[0] * 256 + buf[1]) * 256 + buf[2]) * 256 + buf[3] = crc THEN crc = "" ELSE crc = " CRC Error!" ENDIF PRINT t + "(" + size + ")" + crc WEND Binary.Close(h)
Binaryモジュールは、以下から。
UWSCでバイナリーファイルの読み書き - じゅんじゅんのきまぐれ
Filrの仕組み
tEXtチャンクを使う、とか、大きくなる、とか、圧縮している、とか、GIGAZINEに書いてあった。
それが正しいなら、ファイルを圧縮して、Base64エンコードして、tEXtチャンクを適当なところに、適当なキーワードで追加しているのでしょう。
でも、圧縮するなら、zTXtチャンクを使っても良い気がする、、、。
この場合、ファイルをBase64エンコードして、inflateして、zTXtチャンクを適当なところに、適当なキーワードで追加となる。
圧縮方式がdeflateより良いのを使っているとか?
PNGのtEXtチャンクについて
日本語が使えない、という怨嗟の声があるようです。
RFC2047(いわゆるMIMEエンコード)ということにしてしまえば良い気がするけど、、、対応するビューワがないか。
ISO 8859-1が使える文字だから、、、お、Base64じゃなくて、Base128ができますね。(そんなのないけど)
独自実装になりますが、増加量を少し抑えられます。
Base64の4/3倍(133.33%)から、8/7倍(114.29%)に。
0x0?,0x1?,0x8?,0x9?,0x7F以外を使えば良いのだから、例えば
0x3?〜0x6?,0xC?〜0xF?を0x00〜0x7fにマッピングして、8/7倍すればOKということかと。
埋める記号は「~」あたりが良いかな?
もしくは、CP932使用者に配慮して、0x2?〜0x6?,0xB?〜0xD?あたりにするか。(後半が半角カナ領域)
これなら、日本人と欧米人にやさしい感じです。(他は知らん)
スペースや記号がふんだんに入ってしまうことを気にするなら、少しずらすのもありですが、、、ま、いいでしょ。
どうせ読めないのだから。