All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.stephenc.javaisotools.loopfs.iso9660.Iso9660VolumeDescriptorSet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010. Stephen Connolly.
 * Copyright (c) 2006-2007. loopy project (http://loopy.sourceforge.net).
 *  
 * This library 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 2.1 of the License, or (at your option) any later version.
 *  
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.github.stephenc.javaisotools.loopfs.iso9660;

import java.io.IOException;

import com.github.stephenc.javaisotools.loopfs.api.FileEntry;
import com.github.stephenc.javaisotools.loopfs.api.LoopFileSystemException;
import com.github.stephenc.javaisotools.loopfs.spi.VolumeDescriptorSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Iso9660VolumeDescriptorSet implements VolumeDescriptorSet {

    public static final int TYPE_BOOTRECORD = 0;
    public static final int TYPE_PRIMARY_DESCRIPTOR = 1;
    public static final int TYPE_SUPPLEMENTARY_DESCRIPTOR = 2;
    public static final int TYPE_PARTITION_DESCRIPTOR = 3;
    public static final int TYPE_TERMINATOR = 255;

    private static final Log log = LogFactory.getLog(Iso9660VolumeDescriptorSet.class);

    private final Iso9660FileSystem isoFile;

    // common
    private String systemIdentifier;
    private String volumeSetIdentifier;
    private String volumeIdentifier;
    private String publisher;
    private String preparer;
    private String application;
    private Iso9660FileEntry rootDirectoryEntry;

    // primary
    private String standardIdentifier;
    private long totalBlocks;
    private int volumeSetSize;
    private int volumeSequenceNumber;
    private long creationTime;
    private long mostRecentModificationTime;
    private long expirationTime;
    private long effectiveTime;
    private long pathTableSize;
    private long locationOfLittleEndianPathTable;
    private long locationOfOptionalLittleEndianPathTable;
    private long locationOfBigEndianPathTable;
    private long locationOfOptionalBigEndianPathTable;

    // supplementary
    public String encoding = Constants.DEFAULT_ENCODING;
    public String escapeSequences;

    private boolean hasPrimary = false;
    private boolean hasSupplementary = false;

    /**
     * Initialize this instance.
     *
     * @param fileSystem the parent file system
     */
    public Iso9660VolumeDescriptorSet(Iso9660FileSystem fileSystem) {
        this.isoFile = fileSystem;
    }

    public boolean deserialize(byte[] descriptor) throws IOException {
        final int type = Util.getUInt8(descriptor, 1);

        boolean terminator = false;

        switch (type) {
            case TYPE_TERMINATOR:
                if (!this.hasPrimary) {
                    throw new LoopFileSystemException("No primary volume descriptor found");
                }
                terminator = true;
                break;
            case TYPE_BOOTRECORD:
                log.debug("Found boot record");
                break;
            case TYPE_PRIMARY_DESCRIPTOR:
                log.debug("Found primary descriptor");
                deserializePrimary(descriptor);
                break;
            case TYPE_SUPPLEMENTARY_DESCRIPTOR:
                log.debug("Found supplementatory descriptor");
                deserializeSupplementary(descriptor);
                break;
            case TYPE_PARTITION_DESCRIPTOR:
                log.debug("Found partition descriptor");
                break;
            default:
                log.debug("Found unknown descriptor with type " + type);
        }

        return terminator;
    }

    /**
     * Read the fields of a primary volume descriptor.
     *
     * @param descriptor the descriptor bytes
     * @throws IOException
     */
    private void deserializePrimary(byte[] descriptor) throws IOException {
        // according to the spec, some ISO 9660 file systems can contain multiple identical primary
        // volume descriptors
        if (this.hasPrimary) {
            return;
        }

        validateBlockSize(descriptor);

        if (!this.hasSupplementary) {
            deserializeCommon(descriptor);
        }

        this.standardIdentifier = Util.getDChars(descriptor, 2, 5);
        this.volumeSetSize = Util.getUInt16Both(descriptor, 121);
        this.volumeSequenceNumber = Util.getUInt16Both(descriptor, 125);
        this.totalBlocks = Util.getUInt32Both(descriptor, 81);
        this.publisher = Util.getDChars(descriptor, 319, 128);
        this.preparer = Util.getDChars(descriptor, 447, 128);
        this.application = Util.getDChars(descriptor, 575, 128);
        //this.copyrightFile = Descriptor.get(buffer, 703, 37)
        //this.abstractFile = Descriptor.get(buffer, 740, 37)
        //this.bibliographicalFile = Descriptor.get(buffer, 777, 37)
        this.creationTime = Util.getStringDate(descriptor, 814);
        this.mostRecentModificationTime = Util.getStringDate(descriptor, 831);
        this.expirationTime = Util.getStringDate(descriptor, 848);
        this.effectiveTime = Util.getStringDate(descriptor, 865);
        this.pathTableSize = Util.getUInt32Both(descriptor, 133);
        this.locationOfLittleEndianPathTable = Util.getUInt32LE(descriptor, 141);
        this.locationOfOptionalLittleEndianPathTable = Util.getUInt32LE(descriptor, 145);
        this.locationOfBigEndianPathTable = Util.getUInt32BE(descriptor, 149);
        this.locationOfOptionalBigEndianPathTable = Util.getUInt32BE(descriptor, 153);

        this.hasPrimary = true;
    }

    /**
     * The supplementary descriptor sets the character encoding and may override the common
     * descriptor information.
     *
     * @param descriptor the descriptor bytes
     * @throws IOException
     */
    private void deserializeSupplementary(byte[] descriptor) throws IOException {
        // for now, only recognize one supplementary descriptor
        if (this.hasSupplementary) {
            return;
        }

        validateBlockSize(descriptor);

        String escapeSequences = Util.getDChars(descriptor, 89, 32);

        String enc = getEncoding(escapeSequences);

        if (null != enc) {
            this.encoding = enc;
            this.escapeSequences = escapeSequences;

            deserializeCommon(descriptor);

            this.hasSupplementary = true;
        } else {
            log.warn("Unsupported encoding, escapeSequences: '" + this.escapeSequences + "'");
        }
    }

    /**
     * Read the information common to primary and secondary volume descriptors.
     *
     * @param descriptor the volume descriptor bytes
     * @throws IOException
     */
    private void deserializeCommon(byte[] descriptor) throws IOException {
        this.systemIdentifier = Util.getAChars(descriptor, 9, 32, this.encoding);
        this.volumeIdentifier = Util.getDChars(descriptor, 41, 32, this.encoding);
        this.volumeSetIdentifier = Util.getDChars(descriptor, 191, 128, this.encoding);
        this.rootDirectoryEntry = new Iso9660FileEntry(this.isoFile, descriptor, 157);
    }

    /**
     * Check that the block size is what we expect.
     *
     * @param descriptor the descriptor bytes
     * @throws IOException
     */
    private void validateBlockSize(byte[] descriptor) throws IOException {
        int blockSize = Util.getUInt16Both(descriptor, 129);
        if (blockSize != Constants.DEFAULT_BLOCK_SIZE) {
            throw new LoopFileSystemException("Invalid block size: " + blockSize);
        }
    }

    /**
     * Derive an encoding name from the given escape sequences.
     *
     * @param escapeSequences
     * @return
     */
    private String getEncoding(String escapeSequences) {
        String encoding = null;

        if (escapeSequences.equals("%/@")) {
            // UCS-2 level 1
            encoding = "UTF-16BE";
        } else if (escapeSequences.equals("%/C")) {
            // UCS-2 level 2
            encoding = "UTF-16BE";
        } else if (escapeSequences.equals("%/E")) {
            // UCS-2 level 3
            encoding = "UTF-16BE";
        }

        return encoding;
    }

    /**
     * Returns true if the .iso file has one or more supplementary volume descriptors.
     *
     * @return
     */
    public boolean hasSupplementary() {
        return this.hasSupplementary;
    }

    public String getSystemIdentifier() {
        return this.systemIdentifier;
    }

    public String getVolumeSetIdentifier() {
        return this.volumeSetIdentifier;
    }

    public String getVolumeIdentifier() {
        return this.volumeIdentifier;
    }

    public String getPublisher() {
        return this.publisher;
    }

    public String getPreparer() {
        return this.preparer;
    }

    public String getApplication() {
        return this.application;
    }

    public Iso9660FileEntry getRootEntry() {
        return this.rootDirectoryEntry;
    }

    public String getStandardIdentifier() {
        return this.standardIdentifier;
    }

    public long getTotalBlocks() {
        return this.totalBlocks;
    }

    public int getVolumeSetSize() {
        return this.volumeSetSize;
    }

    public int getVolumeSequenceNumber() {
        return this.volumeSequenceNumber;
    }

    public long getCreationTime() {
        return this.creationTime;
    }

    public long getLastModifiedTime() {
        return this.mostRecentModificationTime;
    }

    public long getExpirationTime() {
        return this.expirationTime;
    }

    public long getEffectiveTime() {
        return this.effectiveTime;
    }

    public long getPathTableSize() {
        return this.pathTableSize;
    }

    public long getLocationOfLittleEndianPathTable() {
        return this.locationOfLittleEndianPathTable;
    }

    public long getLocationOfOptionalLittleEndianPathTable() {
        return this.locationOfOptionalLittleEndianPathTable;
    }

    public long getLocationOfBigEndianPathTable() {
        return this.locationOfBigEndianPathTable;
    }

    public long getLocationOfOptionalBigEndianPathTable() {
        return this.locationOfOptionalBigEndianPathTable;
    }

    /**
     * Returns the character encoding.
     *
     * @return
     */
    public String getEncoding() {
        return this.encoding;
    }

    public String getEscapeSequences() {
        return this.escapeSequences;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy