<?php

// http://siisise.net/jpeg.html#format

class ByteStream {
    private 
$_data;
    private 
$_cursor;
    function 
__construct($data) {
        
$this->_data $data;
        
$this->_cursor 0;
    }
    function 
getBytes($size) {
        
$ret substr($this->_data$this->_cursor$size);
        
$this->_cursor += $size;
        return 
$ret;
    }

    function 
getValue($size) {
        
$data $this->getBytes($size);
        
$value 0;
        for (
$i 0$i $size$i++) {
            
$value <<= 8;
            
$value += ord($data[$i]);
        }
        return 
$value;
    }
    function 
getCursor() {
        return 
$this->_cursor;
    }
}

function 
marker($a$b) { return chr($a).chr($b); }

$jpegdata file_get_contents($argv[1]);

$bs = new ByteStream($jpegdata);

$jpeg_chunk = array();
$done false;
while(
$marker $bs->getBytes(2)) {

    
var_dump(bin2hex($marker));
    
    switch (
$marker) {
      case 
marker(0xFF0xD8): // SOS
          
$length $data null;
          break;
      default: 
// etc
          
if (ord($marker{0}) != 0xFF) {
              echo 
"XXX: ";
              
var_dump(bin2hex($marker));
              exit(
1);
          }
      case 
marker(0xFF0xE0): // APP0
      
case marker(0xFF0xE1): // APP1
          
$length $bs->getValue(2);
          
$data $bs->getBytes($length 2);
          break;
      case 
marker(0xFF0xDA): // SOS include RST
          
$length strlen($jpegdata) - $bs->getCursor() - 2;
          
$data $bs->getBytes($length);
          break;
      case 
marker(0xFF0xD9): // EOI
          
$length $data null;
          
$done true;
          break;
    }
    
$jpeg_chunk[] = array(
        
'marker' => $marker,
        
'length' => $length,
        
'data' => $data);
    if (
$done) {
        break;
    }
}