public function parseFromFile($fp)
 {
     $header_bin = fread($fp, 8);
     $length_read = strlen($header_bin);
     if ($length_read == 0) {
         L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Reached EOF; no more to read", array());
         return false;
         // if we read an exact chunk, it's not an 'eof'.
     }
     if ($length_read < 8) {
         throw new OutOfBoundsException("No more data (read {$length_read} bytes)");
     }
     if ($header_bin === false) {
         throw new RuntimeException("Read error; failed to read 8 bytes of chunk header");
     }
     list($chunk_type, $chunk_size) = $this->parseHeader($header_bin);
     if ($chunk_size > 1048576) {
         // a chunk larger than 1Mb!?
         $chunk_size = number_format($chunk_size / 1024 / 1024, 2);
         throw new RuntimeException("Found chunk claiming to be enormous ({$chunk_size} MiB); are you reading the right file?");
     }
     $body_bin = fread($fp, $chunk_size);
     $chunk = $this->chunk_factory->newChunk($chunk_type, $body_bin);
     L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Read %s chunk from file", array($chunk_type));
     return $chunk;
 }
 public function parseFromFile($fp)
 {
     // It looks like newer Serato sometimes allocates large chunks of free space in the file at the end.
     // This doesn't count as data, so we skip it, using str_pad() to make sure we also skip any run of
     // nulls that is shorter than 8 bytes.
     do {
         $header_bin = fread($fp, 8);
         if ($header_bin === false) {
             throw new RuntimeException("Read error; failed to read 8 bytes of chunk header");
         }
         $length_read = strlen($header_bin);
         if ($length_read == 0) {
             L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Reached EOF; no more to read", array());
             return false;
             // if we read an exact chunk, it's not an 'eof'.
         }
         $just_blank_bytes = str_pad($header_bin, 8, "") == "";
         if ($just_blank_bytes) {
             L::level(L::WARNING) && L::log(L::WARNING, __CLASS__, "Hit unallocated blank space in file.", array());
             // argh. Attempt to resync the stream to the next oent.
             // it looks like Serato sometimes has junk after the free space!
             if (!$this->resync($fp)) {
                 // eof.
                 return false;
             }
         }
     } while ($just_blank_bytes);
     if ($length_read < 8) {
         throw new OutOfBoundsException("No more data (read {$length_read} bytes)");
     }
     list($chunk_type, $chunk_size) = $this->parseHeader($header_bin);
     if ($chunk_size > 1048576) {
         // a chunk larger than 1Mb!?
         $chunk_size = number_format($chunk_size / 1024 / 1024, 2);
         $dumper = new Hexdumper();
         throw new RuntimeException(sprintf("Found chunk claiming to be enormous ({$chunk_size} MiB); are you reading the right file?\n%s", $dumper->hexdump($header_bin)));
     }
     if ($chunk_size > 0) {
         $body_bin = fread($fp, $chunk_size);
         $chunk = $this->chunk_factory->newChunk($chunk_type, $body_bin);
         L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Read %s chunk from file (size: %d)", array($chunk_type, $chunk_size));
     } else {
         $chunk = '';
         L::level(L::WARNING) && L::log(L::WARNING, __CLASS__, "Read 0-byte %s chunk from file. This is unusual.", array($chunk_type));
     }
     return $chunk;
 }