JPGvivisect.pm

#!/usr/local/bin/perl
# (c)copyleft 2017-5-17 : Coded by shun kinoshita / knuhs
#  $SGMsize = JPGvivisect( \$jpgSTR, $size, $directory, $jpgFile);
#   $SGMsize : Segment部の長さを返す
#   解剖結果は %HashTBL と %DataTBL 上に保存される
use strict;
use vars qw/ %HashTBL %DataTBL /; # Global の扱いにする
sub JPGvivisect
{
my ( $jpgRef, $Fsize, $dirSTR, $fileSTR ) = @_;

my  ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$Size,$atime,$Mtime,
     $ctime,$blksize,$blocks) = stat("$dirSTR/$fileSTR");
my  ($sec,$Min,$Hour,$Mday,$Mon,$Year,$wday,$yday,$isdst)
       = localtime($Mtime);
my  $Date = sprintf("%04d-%02d-%02d",$Year+1900,$Mon+1,$Mday);
my  $TimeHM = sprintf("%02d:%02d",$Hour,$Min);

# 基礎データを記録する
    $DataTBL{'File Name'} = "$fileSTR\n";
    $DataTBL{'File Path'} = "$dirSTR\n";
    $DataTBL{'File Size'} = insCom($Size)." Bytes\n";
    $DataTBL{'Time Stamp'} = "$Date $TimeHM\n";

# 数値に3桁ごとにコンマを挿入する関数
sub insCom
{ my $_ = shift @_; s/(\d{1,3})(?=(?:\d\d\d)+(?!\d))/$1,/g; return $_; }


# 数値を10進数と16進数で表現する関数
sub show
{ my $_ = shift @_; return ( insCom($_).sprintf(" [0x%X]", $_) ); }


# getByte(startpos, nbyte) 数バイトごとに取り出す関数
sub getByte
{ my ($p, $n) = @_;
  return ( unpack(($n==1)?"C":"n", substr($$jpgRef, $p, $n)) ) ;
}

# Vivisection start here
my ($pos, $cc) = (0, 0);
my $i;

my $FF=0xFF;     #
my $E0=0xE0;     # APP0-15 (0xFFE0〜0xFFEF) その他のAPPセグメント
my $E1=0xE1;     #
my $C0=0xC0;     # SOF (0xFFC0〜FFCF) Frame Header
my $C4=0xC4;     # DHT (0xFFC4) Huffman Table定義
my $DA=0xDA;     # SOS (0xFFDA) Scan Header
my $DB=0xDB;     # ..  (0xFFDB) Scan Header
my $D9=0xD9;     # EOI (0xFFD9) End Marker
my $SOI=0xFFD8;  # SOI : Start Of Image ' FF D8'
my $SOS=0xFFDA;  # SOS : Start Of Scan ' FF DA'
my $EOI=0xFFD9;  # EOI : End Of Image
my $SOF=0xFFC0;  # SOF : Segment Of Frame Header

   if( getByte($pos, 2) != $SOI )
   { print "JPEGファイルではありません!\n"; return(-1); }

# Marker の位置を記録する
   $i = ++$HashTBL{'FFD8'}[0]; $HashTBL{'FFD8'}[$i] = "$pos";

   $pos = 2;
   while(1)
   {  next if( $cc=getByte($pos, 1) != $FF );
      $cc = getByte($pos+1, 1);
      if( $cc==$C0 || $cc==$C4 || $cc==$E0 || $cc==$E1 || $cc==$DB )
      {   my $f = sprintf("FF%X",$cc);
          $i = ++$HashTBL{$f}[0]; $HashTBL{$f}[$i] = "$pos";
          $pos += getByte($pos+2, 2); next;
      }
      if( $cc == $DA )
      {   $i = ++$HashTBL{'FFDA'}[0]; $HashTBL{'FFDA'}[$i] = "$pos";
          $pos += getByte($pos+2, 2); last;
      }
   }continue{ $pos++; }

# データの先頭位置を記録する
   $i = ++$HashTBL{'Data1'}[0]; $HashTBL{'Data1'}[$i] = "$pos"+2;

my $Ssize = $pos+2;

# Segment部の長さを記録する
   $DataTBL{'SGMT Size'} = insCom($Ssize)." Bytes\n";

   $pos = $Fsize-3; # 大幅にスキップして最後の EOI を確認する
   while(1)
   {    next if( getByte($pos, 1) != $FF );
        last if( getByte($pos+1, 1) == $D9 );
        print "データが壊れています!\n"; return(-1);
   }continue{ $pos++; }

# データの終端位置を記録する
   $i = ++$HashTBL{'Data2'}[0]; $HashTBL{'Data2'}[$i] = "$pos"-1;
   $i = ++$HashTBL{'FFD9'}[0]; $HashTBL{'FFD9'}[$i] = "$pos";  

   return ( $Ssize );
}
1;