/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.fs.fat;
import org.apache.log4j.Logger;
import org.jnode.util.LittleEndian;
/**
* <description>
*
* @author epr
* @author Fabien DUMINY
*/
public class FatUtils {
public static final int FIRST_CLUSTER = 2;
/**
* Gets the offset (in bytes) of the fat with the given index
*
* @param bs
* @param fatNr (0..)
* @return long
*/
public static long getFatOffset(BootSector bs, int fatNr) {
long sectSize = bs.getBytesPerSector();
long sectsPerFat = bs.getSectorsPerFat();
long resSects = bs.getNrReservedSectors();
long offset = resSects * sectSize;
long fatSize = sectsPerFat * sectSize;
offset += fatNr * fatSize;
return offset;
}
/**
* Gets the offset (in bytes) of the root directory with the given index
*
* @param bs
* @return long
*/
public static long getRootDirOffset(BootSector bs) {
long sectSize = bs.getBytesPerSector();
long sectsPerFat = bs.getSectorsPerFat();
int fats = bs.getNrFats();
long offset = getFatOffset(bs, 0);
offset += fats * sectsPerFat * sectSize;
return offset;
}
/**
* Gets the offset of the data (file) area
*
* @param bs
* @return long
*/
public static long getFilesOffset(BootSector bs) {
long offset = getRootDirOffset(bs);
offset += bs.getNrRootDirEntries() * 32;
return offset;
}
/**
* Return the name (without extension) of a full file name
*
* @param nameExt
* @return the name part
*/
public static String splitName(String nameExt) {
int i = nameExt.indexOf('.');
if (i < 0) {
return nameExt;
} else {
return nameExt.substring(0, i);
}
}
/**
* Return the extension (without name) of a full file name
*
* @param nameExt
* @return the extension part
*/
public static String splitExt(String nameExt) {
int i = nameExt.indexOf('.');
if (i < 0) {
return "";
} else {
return nameExt.substring(i + 1);
}
}
/**
* Normalize full file name in DOS 8.3 format from the name and the ext
*
* @param name a DOS 8 name
* @param ext a DOS 3 extension
* @return the normalized DOS 8.3 name
*/
public static String normalizeName(String name, String ext) {
if (ext.length() > 0) {
return (name + "." + ext).toUpperCase();
} else {
return name.toUpperCase();
}
}
/**
* Normalize full file name in DOS 8.3 format from the given full name
*
* @param nameExt a DOS 8.3 name + extension
* @return the normalized DOS 8.3 name
*/
public static String normalizeName(String nameExt) {
if (nameExt.equals("."))
return nameExt;
if (nameExt.equals(".."))
return nameExt;
return normalizeName(splitName(nameExt), splitExt(nameExt));
}
public static void checkValidName(String name) {
checkString(name, "name", 1, 8);
}
public static void checkValidExt(String ext) {
checkString(ext, "extension", 0, 3);
}
private static void checkString(String str, String strType, int minLength, int maxLength) {
if (str == null)
throw new IllegalArgumentException(strType + " is null");
if (str.length() < minLength)
throw new IllegalArgumentException(strType + " must have at least " + maxLength +
" characters: " + str);
if (str.length() > maxLength)
throw new IllegalArgumentException(strType + " has more than " + maxLength +
" characters: " + str);
}
public static final int SUBNAME_SIZE = 13;
/**
* Write the part of a long file name to the given byte array
*
* @param src
* @param srcOffset
* @param ordinal
* @param checkSum
* @param isLast
* @param dest
* @param destOffset
*/
public static void writeSubString(char[] src, int srcOffset, int ordinal, byte checkSum,
boolean isLast, byte[] dest, int destOffset) {
if (isLast) {
LittleEndian.setInt8(dest, destOffset, ordinal + (1 << 6)); // set
// the
// 6th
// security ending
// bit
} else {
LittleEndian.setInt8(dest, destOffset, ordinal);
}
LittleEndian.setInt16(dest, destOffset + 1, src[srcOffset + 0]);
LittleEndian.setInt16(dest, destOffset + 3, src[srcOffset + 1]);
LittleEndian.setInt16(dest, destOffset + 5, src[srcOffset + 2]);
LittleEndian.setInt16(dest, destOffset + 7, src[srcOffset + 3]);
LittleEndian.setInt16(dest, destOffset + 9, src[srcOffset + 4]);
LittleEndian.setInt8(dest, destOffset + 11, 0x0f); // this is the
// hidden attribute
// tag for
// lfn
LittleEndian.setInt8(dest, destOffset + 12, 0); // reserved
LittleEndian.setInt8(dest, destOffset + 13, checkSum); // checksum
LittleEndian.setInt16(dest, destOffset + 14, src[srcOffset + 5]);
LittleEndian.setInt16(dest, destOffset + 16, src[srcOffset + 6]);
LittleEndian.setInt16(dest, destOffset + 18, src[srcOffset + 7]);
LittleEndian.setInt16(dest, destOffset + 20, src[srcOffset + 8]);
LittleEndian.setInt16(dest, destOffset + 22, src[srcOffset + 9]);
LittleEndian.setInt16(dest, destOffset + 24, src[srcOffset + 10]);
LittleEndian.setInt16(dest, destOffset + 26, 0); // sector... unused
LittleEndian.setInt16(dest, destOffset + 28, src[srcOffset + 11]);
LittleEndian.setInt16(dest, destOffset + 30, src[srcOffset + 12]);
if (log.isDebugEnabled()) {
log.debug("<<< END writeSubString dest=\n" /* +FSUtils.toString(dest) */ + ">>>");
}
}
public static byte getOrdinal(byte[] rawData, int offset) {
return (byte) LittleEndian.getUInt8(rawData, offset);
}
public static byte getCheckSum(byte[] rawData, int offset) {
return (byte) LittleEndian.getUInt8(rawData, offset + 13);
}
/**
* Read a part of the long filename from the given byte array and append the
* result to the given StringBuffer
*
* @param sb
* @param rawData
* @param offset
*/
public static void appendSubstring(StringBuffer sb, byte[] rawData, int offset) {
if (log.isDebugEnabled()) {
log.debug("<<< BEGIN appendSubstring buffer=" + sb.toString() + ">>>");
}
int index = 12;
char[] unicodechar = getUnicodeChars(rawData, offset);
if (log.isDebugEnabled()) {
log.debug("appendSubstring: unicodechar=" + new String(unicodechar));
}
while (unicodechar[index] == 0)
index--;
sb.append(unicodechar, 0, index + 1);
if (log.isDebugEnabled()) {
log.debug("<<< END appendSubstring buffer=" + sb.toString() + ">>>");
}
}
/**
* Return a part of a long file name read from the given byte array
*/
public static String getSubstring(byte[] rawData, int offset) {
if (log.isDebugEnabled()) {
log.debug("<<< BEGIN getSubString: rawData=" /*
* +FSUtils.toString(rawData,
* offset, 12)
*/
+ " >>>");
}
// log.debug("getSubString: rawData as
// chars="+FSUtils.toStringAsChars(rawData, offset, 12));
int index = 12;
char[] unicodechar = getUnicodeChars(rawData, offset);
while (unicodechar[index] == 0)
index--;
// log.debug("getSubString: rawData.length="+rawData.length+"
// offset="+offset+" nbChars(index)="+index);
String str = new String(unicodechar, 0, index);
if (log.isDebugEnabled()) {
log.debug("<<< END getSubString: return=" + str + " >>>");
}
return str;
}
/**
* convert an array of bytes to an array of chars (unicode)
*/
static char[] getUnicodeChars(byte[] rawData, int offset) {
char[] unicodechar = new char[SUBNAME_SIZE];
unicodechar[0] = (char) LittleEndian.getUInt16(rawData, offset + 1);
unicodechar[1] = (char) LittleEndian.getUInt16(rawData, offset + 3);
unicodechar[2] = (char) LittleEndian.getUInt16(rawData, offset + 5);
unicodechar[3] = (char) LittleEndian.getUInt16(rawData, offset + 7);
unicodechar[4] = (char) LittleEndian.getUInt16(rawData, offset + 9);
unicodechar[5] = (char) LittleEndian.getUInt16(rawData, offset + 14);
unicodechar[6] = (char) LittleEndian.getUInt16(rawData, offset + 16);
unicodechar[7] = (char) LittleEndian.getUInt16(rawData, offset + 18);
unicodechar[8] = (char) LittleEndian.getUInt16(rawData, offset + 20);
unicodechar[9] = (char) LittleEndian.getUInt16(rawData, offset + 22);
unicodechar[10] = (char) LittleEndian.getUInt16(rawData, offset + 24);
unicodechar[11] = (char) LittleEndian.getUInt16(rawData, offset + 28);
unicodechar[12] = (char) LittleEndian.getUInt16(rawData, offset + 30);
return unicodechar;
}
private static final Logger log = Logger.getLogger(FatUtils.class);
}