content: /*
   * Copyright (C) 2013 Adrian Ulrich <adrian@blinkenlights.ch>
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program. If not, see <http://www.gnu.org/licenses/>.
   */
  
  package net.nullsum.audinaut.util.tags;
  
  
  import java.io.IOException;
  import java.io.RandomAccessFile;
  import java.util.HashMap;
  
  
  class OggFile extends Common {
  
      private static final int OGG_PAGE_SIZE = 27;  // Static size of an OGG Page
      private static final int OGG_TYPE_COMMENT = 3;   // ID of 'VorbisComment's
  
      public OggFile() {
      }
  
      public HashMap getTags(RandomAccessFile s) throws IOException {
          long offset = 0;
          int retry = 64;
          HashMap tags = new HashMap();
  
          for (; retry > 0; retry--) {
              long[] res = parse_ogg_page(s, offset);
              if (res[2] == OGG_TYPE_COMMENT) {
                  tags = parse_ogg_vorbis_comment(s, offset + res[0], res[1]);
                  break;
              }
              offset += res[0] + res[1];
          }
          return tags;
      }
  
  
      /* Parses the ogg page at offset 'offset' and returns
       ** [header_size, payload_size, type]
       */
      private long[] parse_ogg_page(RandomAccessFile s, long offset) throws IOException {
          long[] result = new long[3];               // [header_size, payload_size]
          byte[] p_header = new byte[OGG_PAGE_SIZE];   // buffer for the page header
          byte[] scratch;
          int bread;                         // number of bytes read
          int psize = 0;                         // payload-size
          int nsegs;                         // Number of segments
  
          s.seek(offset);
          bread = s.read(p_header);
          if (bread != OGG_PAGE_SIZE)
              xdie("Unable to read() OGG_PAGE_HEADER");
          if (!(new String(p_header, 0, 5)).equals("OggS\0"))
              xdie("Invalid magic - not an ogg file?");
  
          nsegs = b2u(p_header[26]);
          // debug("> file seg: "+nsegs);
          if (nsegs > 0) {
              scratch = new byte[nsegs];
              bread = s.read(scratch);
              if (bread != nsegs)
                  xdie("Failed to read segtable");
  
              for (int i = 0; i < nsegs; i++) {
                  psize += b2u(scratch[i]);
              }
          }
  
          // populate result array
          result[0] = (s.getFilePointer() - offset);
          result[1] = psize;
          result[2] = -1;
  
          /* next byte is most likely the type -> pre-read */
          if (psize >= 1 && s.read(p_header, 0, 1) == 1) {
              result[2] = b2u(p_header[0]);
          }
  
          return result;
      }
  
      /* In 'vorbiscomment' field is prefixed with \3vorbis in OGG files
       ** we check that this marker is present and call the generic comment
       ** parset with the correct offset (+7) */
      private HashMap parse_ogg_vorbis_comment(RandomAccessFile s, long offset, long pl_len) throws IOException {
          final int pfx_len = 7;
          byte[] pfx = new byte[pfx_len];
  
          if (pl_len < pfx_len)
              xdie("ogg vorbis comment field is too short!");
  
          s.seek(offset);
          s.read(pfx);
  
          if (!(new String(pfx, 0, pfx_len)).equals("\3vorbis"))
              xdie("Damaged packet found!");
  
          return parse_vorbis_comment(s, offset + pfx_len, pl_len - pfx_len);
      }
  
  }
  