/** * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * muCommander 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.commons.file.archive.iso; import com.mucommander.commons.io.RandomAccessInputStream; import com.mucommander.commons.io.StreamUtils; import java.io.IOException; import java.io.InputStream; /** * <code>IsoEntryInputStream</code> allows to reads an ISO entry. * * @author Xavier Martin */ class IsoEntryInputStream extends InputStream { private RandomAccessInputStream rais; private int pos; private long size; private int sectSize; private boolean audio; IsoEntryInputStream(RandomAccessInputStream rais, IsoArchiveEntry entry) throws IOException { this.rais = rais; this.size = entry.getSize(); this.pos = 0; this.sectSize = entry.getSectSize(); this.audio = entry.getAudio(); rais.seek(IsoUtil.offsetInSector(entry.getIndex(), sectSize, audio) + entry.getShiftOffset()); } //////////////////////////////// // InputStream implementation // //////////////////////////////// @Override public int read() throws IOException { return rais.read(); } //////////////////////// // Overridden methods // //////////////////////// @Override public int read(byte b[]) throws IOException { return read(b, 0, b.length); } @Override public int read(byte b[], int off, int len) throws IOException { int available = available(); int toRead = len; if (toRead > available) { toRead = available; } if (available == 0) { return -1; } int ret; int cur = off; int before = pos; int skip = 0; // on the 1st run : generate a valid wav header if (audio && pos == 0) { IsoUtil.toArray(0x46464952, b, 0); // "RIFF" IsoUtil.toArray((int) size - 8, b, 4); // size of file - 8 IsoUtil.toArray(0x45564157, b, 8); // "WAVE" IsoUtil.toArray(0x20746D66, b, 12); // "fmt " b[16] = 0x10; // Chunk Data Size IsoUtil.toArray(0x00020001, b, 20); // WAVE type format : PCM header 0100, stereo 0200 IsoUtil.toArray(0x0000AC44, b, 24); // sample rate : 44100hz IsoUtil.toArray(0x0002B110, b, 28); // bytes/sec : 176400 IsoUtil.toArray(0x00100004, b, 32); // Block alignment 0400 + Bits/sample 1000 IsoUtil.toArray(0x61746164, b, 36); // "data" IsoUtil.toArray((int) size - IsoUtil.WAV_header, b, 40); // size of 'real' data cur = IsoUtil.WAV_header; toRead -= IsoUtil.WAV_header; pos += IsoUtil.WAV_header; } switch (sectSize) { case IsoUtil.MODE2_2336: skip = IsoUtil.MODE2_2336_skip; break; case IsoUtil.MODE2_2352: skip = IsoUtil.MODE2_2352_skip; break; case IsoUtil.MODE1_2048: // shortcut ret = rais.read(b, off, toRead); if (ret != -1) pos += ret; return ret; } // atm it work because it's called for 8192 (2048 * 4) int full = toRead >> 11; int half = toRead % IsoUtil.MODE1_2048; if (full >= 1) { for (int i = 0; i < full; i++) { ret = rais.read(b, cur, IsoUtil.MODE1_2048); if (ret != -1) pos += ret; if (!audio) StreamUtils.skipFully(rais, IsoUtil.MODE2_EC_skip + skip); cur += IsoUtil.MODE1_2048; } ret = rais.read(b, cur, half); if (ret != -1) pos += ret; } else { // in fact it doesn't work for internal viewer, because it's called by chunk of 1024 ret = rais.read(b, cur, half); if (ret != -1) pos += ret; if (((pos % 2048) == 0) && !audio) StreamUtils.skipFully(rais, IsoUtil.MODE2_EC_skip + skip); } return pos - before; } @Override public int available() throws IOException { int available = (int) (size - pos); return (available < 0) ? 0 : available; } @Override public void close() throws IOException { rais.close(); } }