/* * Drive.java * * Copyright (c) 2007 Vreixo Formoso * * This library 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 2 of the License, or * (at your option) any later version. * See COPYING file for details. */ package org.pykix.libburnia.libburn; import org.pykix.libburnia.bindings.Proxy; /** * References a physical drive in the system. * * @author Vreixo Formoso * @since 0.1 */ public class Drive extends Proxy { Drive(long ptr) { super(ptr); } /** * Grab the drive. This must be done before the drive can be used * (for reading, writing, etc). * * @param load * true to make the drive attempt to load a disc * (close its tray door, etc). * @throws BurnException * if it wasn't possible to grab the drive */ public void grab(boolean load) throws BurnException { if ( !burn_drive_grab(pointerOf(this), load ? 1 : 0) ) { throw new BurnException("Can't grab the drive"); } } /** * Release the drive. This should not be done until the drive is no longer * busy (see burn_drive_get_status). The drive is (O_EXCL) unlocked. * * @param eject * true to make the drive eject the disc in it. */ public void release(boolean eject) { burn_drive_release( pointerOf(this), eject ? 1 : 0 ); } /** * Returns what kind of disc the drive is holding. * *

* This function may need to be called more than once to get a proper * status from it. * * @return * The status of the drive, or what kind of disc is in it. */ public DiscStatus getDiscStatus() { switch ( burn_disc_get_status( pointerOf(this) ) ) { case 0: return DiscStatus.UNREADY; case 1: return DiscStatus.BLANK; case 2: return DiscStatus.EMPTY; case 3: return DiscStatus.APPENDABLE; case 4: return DiscStatus.FULL; case 5: return DiscStatus.UNGRABBED; case 6: return DiscStatus.UNSUITABLE; default: throw new RuntimeException("Unexpected value"); } } /** * Tells the MMC Profile identifier of the loaded media. The drive must be * grabbed in order to get a profile other than {@link Profile#NONE}. * *

* libburn currently writes only to profiles 0x09 "CD-R", 0x0a "CD-RW", * 0x11 "DVD-R", 0x12 "DVD-RAM", 0x13 "DVD-RW restricted overwrite", * 0x14 "DVD-RW Sequential Recording" or 0x1a "DVD+RW". * *

* If enabled by burn_allow_untested_profiles() it also writes to profile * 0x15 "DVD-R/DL Sequential Recording". * * @return * The profile of the loaded media. */ /* * I've implemented this in a different way that libburn, I prefer returning * profile as an enum. It has a lot of advantages for the programmer, * and only one problem: this cannot distinguish between different unknown * profiles. */ public Profile getProfile() { return Profile.get( burn_disc_get_profile( pointerOf(this) ) ); } /** * Tells whether a disc can be erased or not. * * @return * true is inserted disc is erasable, * false if not. */ public boolean isErasable() { return burn_disc_erasable( pointerOf(this) ); } /** * Returns the progress and status of a drive. * * @param progress * Will be filled with the progress of the operation, * you can pass null if you don't care * @return * the current status of the drive. */ public DriveStatus getDriveStatus(Progress progress) { switch ( burn_drive_get_status( pointerOf(this), progress == null ? 0 : pointerOf(progress) ) ) { case 0: return DriveStatus.IDLE; case 1: return DriveStatus.SPAWNING; case 2: return DriveStatus.READING; case 3: return DriveStatus.WRITING; case 4: return DriveStatus.WRITING_LEADIN; case 5: return DriveStatus.WRITING_LEADOUT; case 6: return DriveStatus.ERASING; case 7: return DriveStatus.GRABBING; case 8: return DriveStatus.WRITING_PREGAP; case 9: return DriveStatus.CLOSING_TRACK; case 10: return DriveStatus.CLOSING_SESSION; case 11: return DriveStatus.FORMATTING; default: throw new RuntimeException("Unexpected drive status"); } } /** * Erase a disc in the drive. * *

* The drive must be grabbed successfully BEFORE calling this * function. Always ensure that the drive reports a status of * {@link DiscStatus#FULL} or {@link DiscStatus#APPENDABLE} before * calling this function. An erase operation is not cancellable, as * control of the operation is passed wholly to the drive and * there is no way to interrupt it safely. * * @see #getDiscStatus() * * @param fast * true to do a fast erase, where only the disc's * headers are erased; false to erase the entire disc. * With DVD-RW, fast blanking yields media capable only of DAO. */ public void erase(boolean fast) { burn_disc_erase(pointerOf(this), fast); } /** * Creates a WriteOpts object for burning to this drive. * * @return * a WriteOpts object */ public WriteOpts newWriteOpts() { return new WriteOpts( burn_write_opts_new(pointerOf(this)) ); } /** * Return the best possible estimation of the currently available capacity * of the media. This might depend on particular write option settings. * For inquiring the space with such a set of options, the drive has to be * grabbed and {@link DriveStatus#IDLE}. If not, then one will only get a * canned value from the most recent automatic inquiry (e.g. during last * drive grabbing). * *

* An eventual start address from burn_write_opts_set_start_byte() will be * subtracted from the obtained capacity estimation. Negative results get * defaulted to 0. * * @param opts * If not null, write parameters to be set on drive * before query. * @return * number of most probably available free bytes. */ public long getAvailableSpace(WriteOpts opts) { return burn_disc_available_space( pointerOf(this), opts == null ? 0 : pointerOf(opts) ); } /** * Write a disc in the drive. * *

* The drive must be grabbed successfully before calling this function. * Always ensure that the drive reports a status of * {@link DiscStatus#BLANK} or {@link DiscStatus#APPENDABLE} before * calling this function. * *

* Note: write type {@link WriteType#SAO} is currently not capable of * writing a mix of data and audio tracks. You must use * {@link WriteType#TAO} for such sessions. * To be set by {@link WriteOpts#setWriteType(WriteType, BlockTypes)}. * * @param o * The options for the writing operation. * @param d * The {@link Disc} object that described the disc to be created. */ /* * ok, this not need to be a member of Drive, but as * read, erase, etc are also here... */ public void write( WriteOpts o, Disc disc) { burn_disc_write( pointerOf(o), pointerOf(disc) ); } /** * Format media for use with libburn. This currently applies to DVD-RW * in state "Sequential Recording" (profile 0014h) which get formatted to * state "Restricted Overwrite" (profile 0013h). DVD+RW can be "de-iced" * by setting bit2 of flag. Other media cannot be formatted yet. * * @param size * The size in bytes to be used with the format command. It should * be divisible by 32*1024. The effect of this parameter may * depend on the media profile. * @param flag * Bitfield for control purposes: *

*/ public void format(long size, int flag) { burn_disc_format(pointerOf(this), size, flag); } /** * Cancel an operation on a drive. * *

* This will only work when the drive's busy state is * {@link DriveStatus#READING} or {@link DriveStatus#WRITING}. * * @see #getDriveStatus(Progress) */ public void cancel() { burn_drive_cancel( pointerOf(this) ); } /** * Inquire wether the most recent write run was successful. Reasons for * non-success may be: rejection of burn parameters, abort during fatal * errors during write, a call to {@link #cancel()} by the application * thread. * * @return * true if burn seems to have went well, * false if burn failed. */ public boolean wroteWell() { return burn_drive_wrote_well( pointerOf(this) ); } /** * Get the drive's disc object. * * @return * the Disc object or null on failure. */ public Disc getDisc() { long ptr = burn_drive_get_disc( pointerOf(this) ); if ( ptr == 0 ) { return null; } Disc disc = (Disc) proxyFor(ptr); if ( disc == null) { disc = new Disc(ptr); } return disc; } /** * Sets drive read and write speed. * * @param read * Read speed in k/s (0 is max). * @param write * Write speed in k/s (0 is max). */ public void setSpeed(int read, int write) { burn_drive_set_speed( pointerOf(this), read, write); } /** * Creates a ReadOpts object for reading from this drive. * * @return * the ReadOpts object. */ public ReadOpts newReadOpts() { return new ReadOpts( burn_read_opts_new(pointerOf(this)) ); } /** * Gets the maximum write speed for a drive and eventually loaded media. * *

* The return value might change by the media type of already loaded media, * again by call burn_drive_grab() and again by call burn_disc_read_atip(). * * @return * Maximum write speed in K/s. */ public int getWriteSpeed() { return burn_drive_get_write_speed( pointerOf(this) ); } /** * Gets the minimum write speed for the drive and eventually loaded media. * *

* The return value might change by the media type of already loaded media, * again by call burn_drive_grab() and again by call burn_disc_read_atip(). * * @return * Minimum write speed in K/s. */ public int getMinWriteSpeed() { return burn_drive_get_min_write_speed( pointerOf(this) ); } /** * Gets the maximum read speed for the drive. * * @return * Maximum read speed in K/s. */ public int getReadSpeed() { return burn_drive_get_read_speed( pointerOf(this) ); } /** * Obtain a copy of the current speed descriptor list. The drive's list * gets updated on various occasions such as {@link #grab(boolean)} but * the copy obtained here stays untouched. * *

* Speeds may appear several times in the list. The list content depends * much on drive and media type. It seems that .source == 1 applies mostly * to CD media whereas .source == 2 applies to any media. * * @return * The speed list, may be empty. * @throws BurnException * If severe error occurs. */ public SpeedDescriptor[] getSpeedList() throws BurnException { SpeedDescriptor[] lst = burn_drive_get_speedlist(pointerOf(this)); if (lst == null) { throw new BurnException("Can't get speed list"); } return lst; } /** * WARNING: * This revives an old bug-like behavior that might be dangerous. * * Sets the drive status to {@link DiscStatus#BLANK} if it is * {@link DiscStatus#UNREADY} or {@link DiscStatus#UNSUITABLE}. * Thus marking media as writable which actually failed to declare * themselves either blank or (partially) filled. * * @return * true on success, false if drive is * in an unsuitable status. */ public boolean pretendBlank() { return burn_disc_pretend_blank( pointerOf(this) ); } /** * WARNING: * This overrides the safety measures against unsuitable media. * * Sets the drive status to {@link DiscStatus#FULL} if it is * {@link DiscStatus#UNREADY} or {@link DiscStatus#UNSUITABLE}. * Thus marking media as blankable which actually failed to declare * themselves either blank or (partially) filled. * * @return * true on success, false if drive is * in an unsuitable status. */ public boolean pretendFull() { return burn_disc_pretend_full( pointerOf(this) ); } /** * Reads ATIP information from inserted media. To be obtained via * {@link #getWriteSpeed()}, {@link #getMinWriteSpeed()}, * burn_drive_get_start_end_lba(). The drive must be grabbed for this call. * * @return * true on success, false if no valid ATIP * info read. * @throws BurnException * On a severe error. */ public boolean readAtip() throws BurnException { switch ( burn_disc_read_atip( pointerOf(this) ) ) { case 1: return true; case 0: return false; default: throw new BurnException("Severe error reading ATIP"); } } /** * Returns start lba of the media which is currently inserted in the drive. * The drive has to be grabbed to have hope for reply. * *

* Shortcomming (not a feature): unless {@link #readAtip()} was called * only blank media will return valid info. * * @return * the start lba value * @throws BurnException * On invalid lba value. */ /* * TODO mmm, I really don't like to throw an Exception here... */ public int getStartLba() throws BurnException { /* flag is unused yet */ return burn_drive_get_start_lba(pointerOf(this), 0); } /** * Returns end lba of the media which is currently inserted in the drive. * The drive has to be grabbed to have hope for reply. * *

* Shortcomming (not a feature): unless {@link #readAtip()} was called * only blank media will return valid info. * * @return * the end lba value * @throws BurnException * On invalid lba value. */ /* * TODO mmm, I really don't like to throw an Exception here... */ public int getEndLba() throws BurnException { /* flag is unused yet */ return burn_drive_get_end_lba(pointerOf(this), 0); } /** * Read start lba of a track from media. * *

* Usually a track lba is obtained from the result of * {@link Track#getEntry()}. This call retrieves an updated lba, * and can address the invisible track to come. * *

* The drive must be grabbed for this call. One may not issue this call * during ongoing {@link #write(WriteOpts, Disc)} or {@link #erase(boolean)}. * * @param o * If not null, write parameters to be set on drive * before query * @param trackno * 0=next track to come, >0 number of existing track * @return * start lba * @throws BurnException * On error */ public int getTrackLba(WriteOpts o, int trackno) throws BurnException { return burn_disc_track_lba( pointerOf(this), o == null ? 0 : pointerOf(o), trackno); } /** * Read Next Writeable Address of a track from media. * *

* The drive must be grabbed for this call. One may not issue this call * during ongoing {@link #write(WriteOpts, Disc)} or {@link #erase(boolean)}. * * @param o * If not null, write parameters to be set on drive * before query * @param trackno * 0=next track to come, >0 number of existing track * @return * Next Writeable Address * @throws BurnException * If error or non valid nwa */ public int getTrackNwa(WriteOpts o, int trackno) throws BurnException { return burn_disc_track_nwa( pointerOf(this), o == null ? 0 : pointerOf(o), trackno); } /** * Read start lba of the first track in the last complete session. * *

* This is the first parameter of mkisofs option -C. The second parameter * is nwa as obtained by {@link #getTrackNwa(WriteOpts, int)} with * trackno 0. * * @return * returns the start address of that track * @throws BurnException * on error */ public int getMsc1() throws BurnException { return burn_disc_get_msc1(pointerOf(this)); } /** * Creates a {@link MultiCaps} with values which are appropriate for the * drive and the loaded media. The drive must be grabbed for this call. * * @param wt * With {@link WriteType#NONE} the best capabilities of all write modes * get returned. If set to a write mode like {@link WriteType#SAO} the * capabilities with that particular mode are returned and the * {@link MultiCaps#isWritingPossible()} return value is * false if the desired mode is not possible. * @return * The MultiCaps object. * @throws BurnException * On error. */ public MultiCaps getMultiCaps(WriteType wt) throws BurnException { /* flag param unused yet */ return burn_disc_get_multi_caps(pointerOf(this), wt.ordinal(), 0); } /** * TODO not implemented in libburn * * Read a disc from the drive and write it to an fd pair. The drive must be * grabbed successfully BEFORE calling this function. Always ensure that the * drive reports a status of BURN_DISC_FULL before calling this function. * * @param opts * The options for the read operation. */ public void read(ReadOpts opts) { burn_disc_read( pointerOf(this), pointerOf(opts) ); } /** * Inquire the formatting status, the associated sizes and the number of * available formats. The info is media specific and stems from MMC command * 23h READ FORMAT CAPACITY. See mmc5r03c.pdf 6.24 for background details. * Media type can be determined via burn_disc_get_profile(). * * @return * * @throws BurnException * On failure */ public Formats getFormats() throws BurnException { Formats f = burn_disc_get_formats( pointerOf(this) ); if ( f != null ) { return f; } else { throw new BurnException("Can't get formats"); } } /** * Inquire parameters of an available media format. * * @param index * he index of the format item. Beginning with 0 up to reply * parameter from {@link #getFormats()} : * {@link Formats#getNumFormats()} - 1 * @return * @throws BurnException * On error */ public FormatDesc getFormatDescr(int index) throws BurnException { FormatDesc fd = burn_disc_get_format_descr(pointerOf(this), index); if ( fd == null ) { throw new BurnException("Can't get format desc"); } return fd; } private static native boolean burn_drive_grab(long drive, int load); private static native void burn_drive_release(long drive, int eject); private static native int burn_disc_get_status(long drive); private static native short burn_disc_get_profile(long d); private static native boolean burn_disc_erasable(long d); private static native int burn_drive_get_status(long drive, long p); private static native void burn_disc_erase(long drive, boolean fast); private static native long burn_write_opts_new(long drive); private static native long burn_disc_available_space(long d, long o); private static native void burn_disc_write(long o, long disc); private static native void burn_disc_format(long drive, long size, int flag); private static native void burn_drive_cancel(long drive); private static native boolean burn_drive_wrote_well(long d); private static native long burn_drive_get_disc(long d); private static native void burn_drive_set_speed(long d, int r, int w); private static native int burn_drive_get_write_speed(long d); private static native int burn_drive_get_min_write_speed(long d); private static native int burn_drive_get_read_speed(long d); private static native long burn_read_opts_new(long drive); private static native SpeedDescriptor[] burn_drive_get_speedlist(long d); private static native boolean burn_disc_pretend_blank(long drive); private static native boolean burn_disc_pretend_full(long drive); private static native int burn_disc_read_atip(long drive); /* * These two functions are wrapping for the single C function: * int burn_drive_get_start_end_lba(struct burn_drive *drive, * int *start_lba, int *end_lba, int flag); */ private static native int burn_drive_get_start_lba(long drive, int flag) throws BurnException; private static native int burn_drive_get_end_lba(long drive, int flag) throws BurnException; /* * These two functions are wrapping for the single C function: * int burn_disc_track_lba_nwa(struct burn_drive *d, * struct burn_write_opts *o, int trackno, int *lba, int *nwa); */ private static native int burn_disc_track_lba(long d, long o, int trackno) throws BurnException; private static native int burn_disc_track_nwa(long d, long o, int trackno) throws BurnException; private static native int burn_disc_get_msc1(long d) throws BurnException; private static native MultiCaps burn_disc_get_multi_caps(long d, int wt, int flag) throws BurnException; private static native void burn_disc_read(long d, long o); private static native Formats burn_disc_get_formats(long drive); private static native FormatDesc burn_disc_get_format_descr(long d, int index); }