/* This file is part of jpcsp. Jpcsp 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. Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.filesystems.umdiso; import jpcsp.util.FileUtil; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; public class CSOFileSectorDevice extends AbstractFileSectorDevice { protected int offsetShift; protected int numSectors; protected long[] sectorOffsets; private static final long sectorOffsetMask = 0x7FFFFFFFL; public CSOFileSectorDevice(RandomAccessFile fileAccess, byte[] header) throws IOException { super(fileAccess); ByteBuffer byteBuffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN); /* u32 'CISO' u64 image size in bytes (first u32 is highest 32-bit, second u32 is lowest 32-bit) u32 sector size? (00000800 = 2048 = sector size) u32 ? (1) u32[] sector offsets (as many as image size / sector size, I guess) */ long lengthInBytes = (((long) byteBuffer.getInt(4)) << 32) | (byteBuffer.getInt(8) & 0xFFFFFFFFL); int sectorSize = byteBuffer.getInt(16); offsetShift = byteBuffer.get(21) & 0xFF; numSectors = getNumSectors(lengthInBytes, sectorSize); sectorOffsets = new long[numSectors + 1]; byte[] offsetData = new byte[(numSectors + 1) * 4]; fileAccess.seek(24); fileAccess.readFully(offsetData); ByteBuffer offsetBuffer = ByteBuffer.wrap(offsetData).order(ByteOrder.LITTLE_ENDIAN); for (int i = 0; i <= numSectors; i++) { sectorOffsets[i] = offsetBuffer.getInt(i * 4) & 0xFFFFFFFFL; if (i > 0) { if ((sectorOffsets[i] & sectorOffsetMask) < (sectorOffsets[i - 1] & sectorOffsetMask)) { log.error(String.format("Corrupted CISO - Invalid offset [%d]: 0x%08X < 0x%08X", i, sectorOffsets[i], sectorOffsets[i - 1])); } } } } @Override public int getNumSectors() { return numSectors; } @Override public void readSector(int sectorNumber, byte[] buffer, int offset) throws IOException { long sectorOffset = sectorOffsets[sectorNumber]; long sectorEnd = sectorOffsets[sectorNumber + 1]; if ((sectorOffset & 0x80000000) != 0) { long realOffset = (sectorOffset & sectorOffsetMask) << offsetShift; fileAccess.seek(realOffset); fileAccess.read(buffer, offset, sectorLength); } else { sectorEnd = (sectorEnd & sectorOffsetMask) << offsetShift; sectorOffset = (sectorOffset & sectorOffsetMask) << offsetShift; int compressedLength = (int) (sectorEnd - sectorOffset); if (compressedLength < 0) { Arrays.fill(buffer, offset, offset + sectorLength, (byte) 0); } else { byte[] compressedData = new byte[compressedLength]; fileAccess.seek(sectorOffset); fileAccess.read(compressedData); try { Inflater inf = new Inflater(true); try (InputStream s = new InflaterInputStream(new ByteArrayInputStream(compressedData), inf)) { FileUtil.readAll(s, buffer, offset, sectorLength); } } catch (IOException e) { throw new IOException(String.format("Exception while uncompressing sector %d", sectorNumber)); } } } } }