/* * Created on 1 Nov 2006 * Created by Paul Gardner * Copyright (C) 2006 Aelitis, All Rights Reserved. * * 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 2 * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 63.529,40 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package org.gudy.azureus2.core3.disk.impl; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.disk.*; import org.gudy.azureus2.core3.disk.impl.resume.RDResumeHandler; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerException; import org.gudy.azureus2.core3.download.DownloadManagerState; import org.gudy.azureus2.core3.download.DownloadManagerStats; import org.gudy.azureus2.core3.download.impl.DownloadManagerStatsImpl; import org.gudy.azureus2.core3.internat.LocaleTorrentUtil; import org.gudy.azureus2.core3.internat.LocaleUtilDecoder; import org.gudy.azureus2.core3.logging.LogAlert; import org.gudy.azureus2.core3.logging.LogEvent; import org.gudy.azureus2.core3.logging.LogIDs; import org.gudy.azureus2.core3.logging.Logger; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.torrent.TOTorrentFile; import org.gudy.azureus2.core3.util.*; import com.aelitis.azureus.core.diskmanager.cache.CacheFile; import com.aelitis.azureus.core.diskmanager.cache.CacheFileManagerFactory; import com.aelitis.azureus.core.diskmanager.cache.CacheFileOwner; import com.aelitis.azureus.core.diskmanager.file.FMFileManagerFactory; public class DiskManagerUtil { private static final LogIDs LOGID = LogIDs.DISK; protected static int max_read_block_size; static{ ParameterListener param_listener = new ParameterListener() { public void parameterChanged( String str ) { max_read_block_size = COConfigurationManager.getIntParameter( "BT Request Max Block Size" ); } }; COConfigurationManager.addAndFireParameterListener( "BT Request Max Block Size", param_listener); } public static boolean checkBlockConsistencyForHint( DiskManager dm, String originator, int pieceNumber, int offset, int length) { if (length <= 0 ) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " length=" + length + " <= 0")); return false; } if (pieceNumber < 0) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " pieceNumber=" + pieceNumber + " < 0")); return false; } if (pieceNumber >= dm.getNbPieces()) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " pieceNumber=" + pieceNumber + " >= this.nbPieces=" + dm.getNbPieces())); return false; } int pLength = dm.getPieceLength(pieceNumber); if (offset < 0) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " offset=" + offset + " < 0")); return false; } if (offset > pLength) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " offset=" + offset + " > pLength=" + pLength)); return false; } if (offset + length > pLength) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Hint invalid: " + originator + " offset=" + offset + " + length=" + length + " > pLength=" + pLength)); return false; } return true; } public static boolean checkBlockConsistencyForRead( DiskManager dm, String originator, boolean peer_request, int pieceNumber, int offset, int length) { if ( !checkBlockConsistencyForHint( dm, originator, pieceNumber, offset, length )){ return( false ); } if (length > max_read_block_size && peer_request) { if (Logger.isEnabled()) Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Read invalid: " + originator + " length=" + length + " > " + max_read_block_size)); return false; } if(!dm.getPiece(pieceNumber).isDone()) { Logger.log(new LogEvent(dm, LOGID, LogEvent.LT_ERROR, "Read invalid: " + originator + " piece #" + pieceNumber + " not done")); return false; } return true; } public static void doFileExistenceChecks( DiskManagerFileInfoSet fileSet, boolean[] toCheck, DownloadManager dm, boolean allowAlloction ) { DiskManagerFileInfo[] files = fileSet.getFiles(); int lastPieceScanned = -1; int windowStart = -1; int windowEnd = -1; String[] types = DiskManagerImpl.getStorageTypes(dm); // sweep over all files to see if adjacent files of changed files can be deleted or need allocation for(int i = 0; i< files.length;i++) { int firstPiece = files[i].getFirstPieceNumber(); int lastPiece = files[i].getLastPieceNumber(); if(toCheck[i]) { // found a file that changed, scan adjacent files if(lastPieceScanned < firstPiece) { // haven't checked the preceding files, slide backwards windowStart = firstPiece; while(i > 0 && files[i-1].getLastPieceNumber() >= windowStart) i--; } if(windowEnd < lastPiece) windowEnd = lastPiece; } if((windowStart <= firstPiece && firstPiece <= windowEnd) || (windowStart <= lastPiece && lastPiece <= windowEnd)) { // file falls in current scanning window, check it File currentFile = files[i].getFile(true); if(!RDResumeHandler.fileMustExist(dm, files[i])) { int st = convertDMStorageTypeFromString( types[i] ); if( st == DiskManagerFileInfo.ST_COMPACT || st == DiskManagerFileInfo.ST_REORDER_COMPACT ){ currentFile.delete(); } } else if(allowAlloction && !currentFile.exists()) { /* * file must exist, does not exist and we probably just changed to linear * mode, assume that (re)allocation of adjacent files is necessary */ dm.setDataAlreadyAllocated(false); } lastPieceScanned = lastPiece; } } } private static AEMonitor cache_read_mon = new AEMonitor( "DiskManager:cacheRead" ); private static boolean setFileLink( DownloadManager download_manager, DiskManagerFileInfo[] info, DiskManagerFileInfo file_info, File from_file, File to_link ) { File existing_file = file_info.getFile( true ); if ( to_link.equals( existing_file )){ // already pointing to the right place return( true ); } for (int i=0;i<info.length;i++){ if ( to_link.equals( info[i].getFile( true ))){ Logger.log(new LogAlert(download_manager, LogAlert.REPEATABLE, LogAlert.AT_ERROR, "Attempt to link to existing file '" + info[i].getFile(true) + "'")); return( false ); } } if ( to_link.exists()){ if ( !existing_file.exists()){ // using a new file, make sure we recheck download_manager.recheckFile( file_info ); }else{ if ( FileUtil.deleteWithRecycle( existing_file, download_manager.getDownloadState().getFlag( DownloadManagerState.FLAG_LOW_NOISE ))){ // new file, recheck download_manager.recheckFile( file_info ); }else{ Logger.log(new LogAlert(download_manager, LogAlert.REPEATABLE, LogAlert.AT_ERROR, "Failed to delete '" + existing_file.toString() + "'")); return( false ); } } }else{ if ( existing_file.exists()){ if ( !FileUtil.renameFile( existing_file, to_link )){ Logger.log(new LogAlert(download_manager, LogAlert.REPEATABLE, LogAlert.AT_ERROR, "Failed to rename '" + existing_file.toString() + "'" )); return( false ); } } } DownloadManagerState state = download_manager.getDownloadState(); state.setFileLink( file_info.getIndex(), from_file, to_link ); state.save(); return( true ); } static abstract class FileSkeleton implements DiskManagerFileInfoHelper { protected int priority; protected boolean skipped_internal; protected long downloaded; protected abstract File setSkippedInternal( boolean skipped ); } public static DiskManagerFileInfoSet getFileInfoSkeleton( final DownloadManager download_manager, final DiskManagerListener listener ) { final TOTorrent torrent = download_manager.getTorrent(); if ( torrent == null ){ return( new DiskManagerFileInfoSetImpl(new DiskManagerFileInfoImpl[0],null) ); } String tempRootDir = download_manager.getAbsoluteSaveLocation().getParent(); if(tempRootDir == null) // in case we alraedy are at the root tempRootDir = download_manager.getAbsoluteSaveLocation().getPath(); if ( !torrent.isSimpleTorrent()){ tempRootDir += File.separator + download_manager.getAbsoluteSaveLocation().getName(); } tempRootDir += File.separator; final String root_dir = StringInterner.intern(tempRootDir) ; try{ final LocaleUtilDecoder locale_decoder = LocaleTorrentUtil.getTorrentEncoding( torrent ); TOTorrentFile[] torrent_files = torrent.getFiles(); final FileSkeleton[] res = new FileSkeleton[ torrent_files.length ]; final String incomplete_suffix = download_manager.getDownloadState().getAttribute( DownloadManagerState.AT_INCOMP_FILE_SUFFIX ); final DiskManagerFileInfoSet fileSetSkeleton = new DiskManagerFileInfoSet() { public DiskManagerFileInfo[] getFiles() { return res; } public int nbFiles() { return res.length; } public void setPriority(int[] toChange) { if(toChange.length != res.length) throw new IllegalArgumentException("array length mismatches the number of files"); for(int i=0;i<res.length;i++) res[i].priority = toChange[i]; DiskManagerImpl.storeFilePriorities( download_manager, res); for(int i=0;i<res.length;i++) if(toChange[i] != 0 ) listener.filePriorityChanged(res[i]); } public void setSkipped(boolean[] toChange, boolean setSkipped) { if(toChange.length != res.length) throw new IllegalArgumentException("array length mismatches the number of files"); if (!setSkipped ){ String[] types = DiskManagerImpl.getStorageTypes(download_manager); boolean[] toLinear = new boolean[toChange.length]; boolean[] toReorder = new boolean[toChange.length]; int num_linear = 0; int num_reorder = 0; for ( int i=0;i<toChange.length;i++){ if ( toChange[i] ){ int old_type = DiskManagerUtil.convertDMStorageTypeFromString( types[i] ); if ( old_type == DiskManagerFileInfo.ST_COMPACT ){ toLinear[i] = true; num_linear++; }else if ( old_type == DiskManagerFileInfo.ST_REORDER_COMPACT ){ toReorder[i] = true; num_reorder++; } } } if ( num_linear > 0 ){ if (!Arrays.equals(toLinear, setStorageTypes(toLinear, DiskManagerFileInfo.ST_LINEAR))){ return; } } if ( num_reorder > 0 ){ if (!Arrays.equals(toReorder, setStorageTypes(toReorder, DiskManagerFileInfo.ST_REORDER ))){ return; } } } File[] to_link = new File[res.length]; for(int i=0;i<res.length;i++){ if(toChange[i]){ to_link[i] = res[i].setSkippedInternal( setSkipped ); } } DiskManagerImpl.storeFilePriorities( download_manager, res); List<Integer> from_indexes = new ArrayList<Integer>(); List<File> from_links = new ArrayList<File>(); List<File> to_links = new ArrayList<File>(); for(int i=0;i<res.length;i++){ if ( to_link[i] != null ){ from_indexes.add( i ); from_links.add( res[i].getFile( false )); to_links.add( to_link[i] ); } } if ( from_links.size() > 0 ){ download_manager.getDownloadState().setFileLinks( from_indexes, from_links, to_links ); } if(!setSkipped){ doFileExistenceChecks(this, toChange, download_manager, true); } for(int i=0;i<res.length;i++){ if(toChange[i]){ listener.filePriorityChanged(res[i]); } } } public boolean[] setStorageTypes(boolean[] toChange, int newStorageType) { if(toChange.length != res.length) throw new IllegalArgumentException("array length mismatches the number of files"); String[] types = DiskManagerImpl.getStorageTypes(download_manager); boolean[] modified = new boolean[res.length]; boolean[] toSkip = new boolean[res.length]; int toSkipCount = 0; DownloadManagerState dmState = download_manager.getDownloadState(); try { dmState.suppressStateSave(true); for(int i=0;i<res.length;i++) { if(!toChange[i]) continue; final int idx = i; int old_type = DiskManagerUtil.convertDMStorageTypeFromString( types[i] ); //System.out.println(old_type + " <> " + newStroageType); if ( newStorageType == old_type ) { modified[i] = true; continue; } try{ File target_file = res[i].getFile( true ); // if the file doesn't exist then this is the start-of-day, most likely // being called from the torrent-opener, so we don't need to do any // file fiddling (in fact, if we do, we end up leaving zero length // files for dnd files which then force a recheck when the download // starts for the first time) if ( target_file.exists()){ CacheFile cache_file = CacheFileManagerFactory.getSingleton().createFile( new CacheFileOwner() { public String getCacheFileOwnerName() { return( download_manager.getInternalName()); } public TOTorrentFile getCacheFileTorrentFile() { return( res[idx].getTorrentFile() ); } public File getCacheFileControlFileDir() { return( download_manager.getDownloadState().getStateFile( )); } public int getCacheMode() { return( CacheFileOwner.CACHE_MODE_NORMAL ); } }, target_file, DiskManagerUtil.convertDMStorageTypeToCache( newStorageType )); cache_file.getLength(); // need this to trigger recovery for re-order files :( cache_file.close(); } toSkip[i] = ( newStorageType == FileSkeleton.ST_COMPACT || newStorageType == FileSkeleton.ST_REORDER_COMPACT )&& !res[i].isSkipped(); if ( toSkip[i] ){ toSkipCount++; } modified[i] = true; }catch( Throwable e ){ Debug.printStackTrace(e); Logger.log( new LogAlert(download_manager, LogAlert.REPEATABLE, LogAlert.AT_ERROR, "Failed to change storage type for '" + res[i].getFile(true) +"': " + Debug.getNestedExceptionMessage(e))); // download's not running - tag for recheck RDResumeHandler.recheckFile( download_manager, res[i] ); } types[i] = DiskManagerUtil.convertDMStorageTypeToString( newStorageType ); } /* * set storage type and skipped before we do piece clearing and file * clearing checks as those checks work better when skipped/stype is set * properly */ dmState.setListAttribute( DownloadManagerState.AT_FILE_STORE_TYPES, types); if ( toSkipCount > 0){ setSkipped( toSkip, true ); } for(int i=0;i<res.length;i++) { if(!toChange[i]) continue; // download's not running, update resume data as necessary int cleared = RDResumeHandler.storageTypeChanged( download_manager, res[i] ); // try and maintain reasonable figures for downloaded. Note that because // we don't screw with the first and last pieces of the file during // storage type changes we don't have the problem of dealing with // the last piece being smaller than torrent piece size if (cleared > 0) { res[i].downloaded = res[i].downloaded - cleared * res[i].getTorrentFile().getTorrent().getPieceLength(); if (res[i].downloaded < 0) res[i].downloaded = 0; } } DiskManagerImpl.storeFileDownloaded( download_manager, res, true ); doFileExistenceChecks(this, toChange, download_manager, newStorageType == FileSkeleton.ST_LINEAR || newStorageType == FileSkeleton.ST_REORDER ); } finally { dmState.suppressStateSave(false); dmState.save(); } return modified; } }; for (int i=0;i<res.length;i++){ final TOTorrentFile torrent_file = torrent_files[i]; final int file_index = i; FileSkeleton info = new FileSkeleton() { private volatile CacheFile read_cache_file; // do not access this field directly, use lazyGetFile() instead private WeakReference dataFile = new WeakReference(null); public void setPriority(int b) { priority = b; DiskManagerImpl.storeFilePriorities( download_manager, res ); listener.filePriorityChanged( this ); } public void setSkipped(boolean _skipped) { if ( !_skipped && getStorageType() == ST_COMPACT ){ if ( !setStorageType( ST_LINEAR )){ return; } } if ( !_skipped && getStorageType() == ST_REORDER_COMPACT ){ if ( !setStorageType( ST_REORDER )){ return; } } File to_link = setSkippedInternal( _skipped ); DiskManagerImpl.storeFilePriorities( download_manager, res ); if ( to_link != null ){ download_manager.getDownloadState().setFileLink( file_index, getFile( false ), to_link ); } if ( !_skipped ){ boolean[] toCheck = new boolean[fileSetSkeleton.nbFiles()]; toCheck[file_index] = true; doFileExistenceChecks(fileSetSkeleton, toCheck, download_manager, true); } listener.filePriorityChanged( this ); } public int getAccessMode() { return( READ ); } public long getDownloaded() { return( downloaded ); } public void setDownloaded( long l ) { downloaded = l; } public String getExtension() { String ext = lazyGetFile().getName(); if ( incomplete_suffix != null && ext.endsWith( incomplete_suffix )){ ext = ext.substring( 0, ext.length() - incomplete_suffix.length()); } int separator = ext.lastIndexOf("."); if (separator == -1) separator = 0; return ext.substring(separator); } public int getFirstPieceNumber() { return( torrent_file.getFirstPieceNumber()); } public int getLastPieceNumber() { return( torrent_file.getLastPieceNumber()); } public long getLength() { return( torrent_file.getLength()); } public int getIndex() { return( file_index ); } public int getNbPieces() { return( torrent_file.getNumberOfPieces()); } public int getPriority() { return( priority ); } protected File setSkippedInternal( boolean _skipped ) { // returns the file to link to if linkage is required skipped_internal = _skipped; if ( !download_manager.isDestroyed()){ DownloadManagerState dm_state = download_manager.getDownloadState(); String dnd_sf = dm_state.getAttribute( DownloadManagerState.AT_DND_SUBFOLDER ); if ( dnd_sf != null ){ File link = getLink(); File file = getFile( false ); if ( _skipped ){ if ( link == null || link.equals( file )){ File parent = file.getParentFile(); if ( parent != null ){ File new_parent = new File( parent, dnd_sf ); File new_file = new File( new_parent, file.getName()); if ( !new_file.exists()){ if ( !new_parent.exists()){ new_parent.mkdirs(); } if ( new_parent.canWrite()){ boolean ok; if ( file.exists()){ ok = FileUtil.renameFile( file, new_file ); }else{ ok = true; } if ( ok ){ return( new_file ); } } } } } }else{ if ( link != null && !file.exists()){ File parent = file.getParentFile(); if ( parent != null && parent.canWrite()){ File new_parent = parent.getName().equals( dnd_sf )?parent:new File( parent, dnd_sf ); // use link name to handle incomplete file suffix if set File new_file = new File( new_parent, link.getName()); if ( new_file.equals( link )){ boolean ok; String incomp_ext = dm_state.getAttribute( DownloadManagerState.AT_INCOMP_FILE_SUFFIX ); if ( incomp_ext != null && incomp_ext.length() > 0 ){ file = new File( file.getParentFile(), file.getName() + incomp_ext ); } if ( new_file.exists()){ ok = FileUtil.renameFile( new_file, file ); }else{ ok = true; } if ( ok ){ File[] files = new_parent.listFiles(); if ( files != null && files.length == 0 ){ new_parent.delete(); } return( file ); } } } } } } } return( null ); } public boolean isSkipped() { return( skipped_internal ); } public DiskManager getDiskManager() { return( null ); } public DownloadManager getDownloadManager() { return( download_manager ); } public File getFile( boolean follow_link ) { if ( follow_link ){ File link = getLink(); if ( link != null ){ return( link ); } } return lazyGetFile(); } private File lazyGetFile() { File toReturn = (File)dataFile.get(); if(toReturn != null) return toReturn; TOTorrent tor = download_manager.getTorrent(); String path_str = root_dir; File simpleFile = null; // for a simple torrent the target file can be changed if ( tor == null || tor.isSimpleTorrent()){ // rumour has it tor can sometimes be null simpleFile = download_manager.getAbsoluteSaveLocation(); }else{ byte[][]path_comps = torrent_file.getPathComponents(); for (int j=0;j<path_comps.length;j++){ String comp; try { comp = locale_decoder.decodeString( path_comps[j] ); } catch (UnsupportedEncodingException e) { Debug.printStackTrace(e); comp = "undecodableFileName"+file_index; } comp = FileUtil.convertOSSpecificChars( comp, j != path_comps.length-1 ); path_str += (j==0?"":File.separator) + comp; } } dataFile = new WeakReference(toReturn = simpleFile != null ? simpleFile : new File( path_str )); //System.out.println("new file:"+toReturn); return toReturn; } public TOTorrentFile getTorrentFile() { return( torrent_file ); } public boolean setLink( File link_destination ) { /** * If we a simple torrent, then we'll redirect the call to the download and move the * data files that way - that'll keep everything in sync. */ if (download_manager.getTorrent().isSimpleTorrent()) { try { download_manager.moveDataFiles(link_destination.getParentFile(), link_destination.getName()); return true; } catch (DownloadManagerException e) { // What should we do with the error? return false; } } return setLinkAtomic(link_destination); } public boolean setLinkAtomic( File link_destination ) { return( setFileLink( download_manager, res, this, lazyGetFile(), link_destination )); } public File getLink() { return( download_manager.getDownloadState().getFileLink( file_index, lazyGetFile() )); } public boolean setStorageType(int type) { boolean[] change = new boolean[res.length]; change[file_index] = true; return fileSetSkeleton.setStorageTypes(change, type)[file_index]; } public int getStorageType() { return( DiskManagerUtil.convertDMStorageTypeFromString( DiskManagerImpl.getStorageType(download_manager, file_index))); } public void flushCache() { } public DirectByteBuffer read( long offset, int length ) throws IOException { CacheFile temp;; try{ cache_read_mon.enter(); if ( read_cache_file == null ){ try{ int type = convertDMStorageTypeFromString( DiskManagerImpl.getStorageType(download_manager, file_index)); read_cache_file = CacheFileManagerFactory.getSingleton().createFile( new CacheFileOwner() { public String getCacheFileOwnerName() { return( download_manager.getInternalName()); } public TOTorrentFile getCacheFileTorrentFile() { return( torrent_file ); } public File getCacheFileControlFileDir() { return( download_manager.getDownloadState().getStateFile( )); } public int getCacheMode() { return( CacheFileOwner.CACHE_MODE_NORMAL ); } }, getFile( true ), convertDMStorageTypeToCache( type )); }catch( Throwable e ){ Debug.printStackTrace(e); throw( new IOException( e.getMessage())); } } temp = read_cache_file; }finally{ cache_read_mon.exit(); } DirectByteBuffer buffer = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_DM_READ, length ); try{ temp.read( buffer, offset, CacheFile.CP_READ_CACHE ); }catch( Throwable e ){ buffer.returnToPool(); Debug.printStackTrace(e); throw( new IOException( e.getMessage())); } return( buffer ); } public int getReadBytesPerSecond() { CacheFile temp = read_cache_file; if ( temp == null ){ return( 0 ); } // could do something here one day I guess return( 0 ); } public int getWriteBytesPerSecond() { return( 0 ); } public long getETA() { return( -1 ); } public void close() { CacheFile temp; try{ cache_read_mon.enter(); temp = read_cache_file; read_cache_file = null; }finally{ cache_read_mon.exit(); } if ( temp != null ){ try{ temp.close(); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public void addListener( DiskManagerFileInfoListener listener ) { if ( getDownloaded() == getLength()){ try{ listener.dataWritten( 0, getLength()); listener.dataChecked( 0, getLength()); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public void removeListener( DiskManagerFileInfoListener listener ) { } }; res[i] = info; } loadFilePriorities( download_manager, fileSetSkeleton); loadFileDownloaded( download_manager, res ); return( fileSetSkeleton ); }catch( Throwable e ){ Debug.printStackTrace(e); return( new DiskManagerFileInfoSetImpl(new DiskManagerFileInfoImpl[0],null) ); } } public static int convertDMStorageTypeFromString( String str ) { char c = str.charAt(0); switch( c ){ case 'L':{ return( DiskManagerFileInfo.ST_LINEAR ); } case 'C':{ return( DiskManagerFileInfo.ST_COMPACT ); } case 'R':{ return( DiskManagerFileInfo.ST_REORDER ); } case 'X':{ return( DiskManagerFileInfo.ST_REORDER_COMPACT ); } } Debug.out( "eh?" ); return( DiskManagerFileInfo.ST_LINEAR ); } public static String convertDMStorageTypeToString( int dm_type ) { switch( dm_type ){ case DiskManagerFileInfo.ST_LINEAR:{ return( "L" ); } case DiskManagerFileInfo.ST_COMPACT:{ return( "C" ); } case DiskManagerFileInfo.ST_REORDER:{ return( "R" ); } case DiskManagerFileInfo.ST_REORDER_COMPACT:{ return( "X" ); } } Debug.out( "eh?" ); return( "?" ); } public static String convertCacheStorageTypeToString( int cache_type ) { switch( cache_type ){ case CacheFile.CT_LINEAR:{ return( "L" ); } case CacheFile.CT_COMPACT:{ return( "C" ); } case CacheFile.CT_PIECE_REORDER:{ return( "R" ); } case CacheFile.CT_PIECE_REORDER_COMPACT:{ return( "X" ); } } Debug.out( "eh?" ); return( "?" ); } public static int convertDMStorageTypeToCache( int dm_type ) { switch( dm_type ){ case DiskManagerFileInfo.ST_LINEAR:{ return( CacheFile.CT_LINEAR ); } case DiskManagerFileInfo.ST_COMPACT:{ return( CacheFile.CT_COMPACT ); } case DiskManagerFileInfo.ST_REORDER:{ return( CacheFile.CT_PIECE_REORDER ); } case DiskManagerFileInfo.ST_REORDER_COMPACT:{ return( CacheFile.CT_PIECE_REORDER_COMPACT ); } } Debug.out( "eh?" ); return( CacheFile.CT_LINEAR ); } protected static void storeFilePriorities( DownloadManager download_manager, DiskManagerFileInfo[] files ) { if ( files == null ) return; // bit confusing this: priorities are stored as // >= 1 : priority, 1 = high, 2 = higher etc // 0 : skipped // -1 : normal priority // <= -2 : negative priority , so -2 -> priority -1, -3 -> priority -2 etc List file_priorities = new ArrayList(files.length); for (int i=0; i < files.length; i++) { DiskManagerFileInfo file = files[i]; if (file == null) return; boolean skipped = file.isSkipped(); int priority = file.getPriority(); int value; if ( skipped ){ value = 0; }else if ( priority > 0 ){ value = priority; }else{ value = priority - 1; if ( value > 0 ){ value = Integer.MIN_VALUE; } } file_priorities.add( i, Long.valueOf(value)); } download_manager.setData( "file_priorities", file_priorities ); if (files.length > 0 && !(files[0] instanceof DiskManagerFileInfoImpl)) { long skipped_file_set_size = 0; long skipped_but_downloaded = 0; for (int i=0;i<files.length;i++){ DiskManagerFileInfo file = files[i]; if ( file.isSkipped()){ skipped_file_set_size += file.getLength(); skipped_but_downloaded += file.getDownloaded(); } } DownloadManagerStats stats = download_manager.getStats(); if (stats instanceof DownloadManagerStatsImpl) { ((DownloadManagerStatsImpl) stats).setSkippedFileStats(skipped_file_set_size, skipped_but_downloaded); } } } static void loadFilePriorities( DownloadManager download_manager, DiskManagerFileInfoSet fileSet ) { // TODO: remove this try/catch. should only be needed for those upgrading from previous snapshot try { DiskManagerFileInfo[] files = fileSet.getFiles(); if ( files == null ) return; List file_priorities = (List)download_manager.getData( "file_priorities" ); if ( file_priorities == null ) return; boolean[] toSkip = new boolean[files.length]; int[] prio = new int[files.length]; for (int i=0; i < files.length; i++) { DiskManagerFileInfo file = files[i]; if (file == null) return; int priority = ((Long)file_priorities.get( i )).intValue(); if ( priority == 0 ){ toSkip[i] = true; }else{ if ( priority < 0 ){ priority++; } prio[i] = priority; } } fileSet.setPriority(prio); fileSet.setSkipped(toSkip, true); } catch (Throwable t) {Debug.printStackTrace( t );} } protected static void loadFileDownloaded( DownloadManager download_manager, DiskManagerFileInfoHelper[] files ) { DownloadManagerState state = download_manager.getDownloadState(); Map details = state.getMapAttribute( DownloadManagerState.AT_FILE_DOWNLOADED ); if ( details == null ){ return; } List downloaded = (List)details.get( "downloaded" ); if ( downloaded == null ){ return; } try{ for (int i=0;i<files.length;i++){ files[i].setDownloaded(((Long)downloaded.get(i)).longValue()); } }catch( Throwable e ){ Debug.printStackTrace(e); } } }