/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2009, Jens Kager, Fritz Praus
Copyright (C) 2008-2009, Rainhard Raschbauer
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 3 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, see <http://www.gnu.org/licenses/>.
*/
package fat;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FatItS {
// Block Size in Bytes
public static final int BlockSize = 512;
// maximum number of entries in a directory (arbitrary)
public static final int MAX_ENTRIES = 1024;
// Master Boot Record
public static final int MASTER_BOOT_RECORD = 0;
// Volume Boot Record location in Master Boot Record
public static final int VBR_ADDR = 0x1C6;
// define ASCII
public static final int SPACE = 0x20;
public static final int DIR_ENTRY_IS_FREE = 0xE5;
public static final int FIRST_LONG_ENTRY = 0x01;
public static final int SECOND_LONG_ENTRY = 0x42;
// define DIR_Attr
public static final int ATTR_LONG_NAME = 0x0F;
public static final int ATTR_READ_ONLY = 0x01;
public static final int ATTR_HIDDEN = 0x02;
public static final int ATTR_SYSTEM = 0x04;
public static final int ATTR_VOLUME_ID = 0x08;
public static final int ATTR_DIRECTORY = 0x10;
public static final int ATTR_ARCHIVE = 0x20;
// FAT12 and FAT16 Structure Starting at Offset 36
public static final int BS_DRVNUM = 36;
public static final int BS_RESERVED1 = 37;
public static final int BS_BOOTSIG = 38;
public static final int BS_VOLID = 39;
public static final int BS_VOLLAB = 43;
public static final int BS_FILSYSTYPE = 54;
// FAT32 Structure Starting at Offset 36
public static final int BPB_FATSZ32 = 36;
public static final int BPB_EXTFLAGS = 40;
public static final int BPB_FSVER = 42;
public static final int BPB_ROOTCLUS = 44;
public static final int BPB_FSINFO = 48;
public static final int BPB_BKBOOTSEC = 50;
public static final int BPB_RESERVED = 52;
public static final int FAT32_BS_DRVNUM = 64;
public static final int FAT32_BS_RESERVED1 = 65;
public static final int FAT32_BS_BOOTSIG = 66;
public static final int FAT32_BS_VOLID = 67;
public static final int FAT32_BS_VOLLAB = 71;
public static final int FAT32_BS_FILSYSTYPE = 82;
// End of Boot Sctor and BPB Structure
private static int volume_boot_record_addr;
private static int fat_offset;
private static int fat_size;
private static int cluster_offset;
private static int cluster_size;
private static int[] tmp_buffer = new int[BlockSize];
// preallocate exceptions
private static IOException IOExc = new IOException();
private static FileNotFoundException FNFExc = new FileNotFoundException();
public static void dump_sector(int[] sector) {
int j = 0;
for (int i = 0; i < 512; i++) {
j++;
System.out.print(Integer.toHexString(sector[i]) + " ");
if (j == 32) {
System.out.println();
j = 0;
}
}
}
private static int fat_addr(FatLowLevel fat_access) {
//System.out.println("fat_addr()");
// auslesen des Master Boot Record von der MMC/SD Karte (addr = 0)
fat_access.ReadSector(MASTER_BOOT_RECORD, tmp_buffer); // Read Master Boot Record
// dump_sector(tmp_buffer);
int volume_boot_record_addr = tmp_buffer[VBR_ADDR] + (tmp_buffer[VBR_ADDR + 1] << 8);
return volume_boot_record_addr;
}
private static int fat_root_dir_addr(FatLowLevel fat_access) {
//System.out.println("fat_root_dir_addr");
// auslesen des Volume Boot Record von der MMC/SD Karte
fat_access.ReadSector(volume_boot_record_addr, tmp_buffer);
BootSec.set(tmp_buffer);
// berechnet den ersten Sector des Root Directory
int firstRootDirSecNum = BootSec.BPB_RsvdSecCnt + (BootSec.BPB_NumFATs * BootSec.BPB_FATSz16);
firstRootDirSecNum += volume_boot_record_addr;
fat_size = BootSec.BPB_FATSz16;
return firstRootDirSecNum;
}
private static int fat_load(FatLowLevel fat_access, int cluster, int block) {
//System.out.println("fat_load(): cluster=" + cluster + ", block=" + block);
// Zum berprfen ob der FAT Block schon geladen wurde
int blockStore = 0;
// Byte Adresse innerhalb des Fat Blocks
int byteAddress;
// FAT Block Adresse
int blockAddress;
// Berechnung fr den ersten FAT Block (FAT Start Addresse)
for (int a = 0;; a++) {
if (a == block) {
block = (0x0000FFFF & cluster);
//System.out.println("fat_load() finished: block=" + block);
return block;
}
if (cluster == 0xFFFF) {
block = 0xFFFF;
break; // Ist das Ende des Files erreicht Schleife beenden
}
// Berechnung des Bytes innerhalb des FAT Blocks
byteAddress = (cluster * 2) % BlockSize;
// Berechnung des Blocks der gelesen werden mu
blockAddress = ((cluster * 2) / BlockSize) + volume_boot_record_addr + fat_offset;
// Lesen des FAT Blocks
// berprfung ob dieser Block schon gelesen wurde
if (blockAddress != blockStore) {
blockStore = blockAddress;
// Lesen des FAT Blocks
fat_access.ReadSector(blockAddress, tmp_buffer);
}
// Lesen der nchsten Clusternummer
cluster = (tmp_buffer[byteAddress + 1] << 8) + tmp_buffer[byteAddress];
}
//System.out.println("fat_load() finished: block=" + block);
return block;
}
public static void fat_init(FatLowLevel fat_access) throws IOException {
//System.out.println("fat_init");
// Init Fat system and underlying hardware
if (fat_access.Init() != 0) {
throw new IOException("FatLowLevel.Init failed or no Card connected");
}
// Retrieve number of sector where volume boot record starts
volume_boot_record_addr = fat_addr(fat_access);
System.out.println("Volume Boot Record: " + volume_boot_record_addr);
fat_access.ReadSector(volume_boot_record_addr, tmp_buffer);
BootSec.set(tmp_buffer);
System.out.println("Sectors per Cluster: " +BootSec.BPB_SecPerClus);
System.out.println("Number of FATs" + BootSec.BPB_NumFATs);
fat_offset = BootSec.BPB_RsvdSecCnt;
cluster_size = BootSec.BPB_SecPerClus;
if (cluster_size == 0) { // something went terribly wrong
throw IOExc;
}
cluster_offset = ((BootSec.BPB_BytesPerSec * 32) / BlockSize);
cluster_offset += fat_root_dir_addr(fat_access);
System.out.println("cluster offset" + cluster_offset);
}
public static void fat_read_file(FatLowLevel fat_access, int cluster,// Angabe des Startclusters vom File
int[] buffer, // Workingbuffer
int blockCount) // Angabe welcher Bock vom File geladen/ werden soll a 512 Bytes
{
//System.out.println("fat_read_file()");
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu laden ist
int block = (blockCount / cluster_size);
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, cluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
// Berechnung des Blocks innerhalb des Cluster
block += (blockCount % cluster_size);
// Read Data Block from Device
fat_access.ReadSector(block, buffer);
return;
}
public static void fat_write_file(FatLowLevel fat_access, int cluster,// Angabe des Startclusters vom File
int[] buffer, // Workingbuffer
int blockCount) // Angabe welcher Bock vom File gespeichert werden soll a 512 Bytes
{
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu speichern ist
int block = (blockCount / cluster_size);
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, cluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
// Berechnung des Blocks innerhalb des Cluster
block += (blockCount % cluster_size);
// Write Data Block to Device
fat_access.WriteSector(block, buffer);
return;
}
public static DirEntry fat_search_file(FatLowLevel fat_access, int dirCluster, char[] filename, DirEntry entry) throws IOException
{
for (int a = 0; a < MAX_ENTRIES; a++) {
entry = fat_read_dir_ent(fat_access, dirCluster, a, entry);
if (entry.matchName(filename)) {
return entry;
}
}
throw IOExc; // some strange error occurred
}
public static DirEntry fat_mod_file(FatLowLevel fat_access, int dirCluster, char[] filename, long size, int attr, DirEntry entry) throws IOException
{
for (int a = 0; a < MAX_ENTRIES; a++) {
entry = FatItS.fat_read_dir_ent(fat_access, dirCluster, a, entry);
if (entry.matchName(filename)) {
entry = FatItS.fat_set_dir_ent(fat_access, dirCluster, a, size, attr, entry);
return entry;
}
}
throw IOExc;
}
// Filname fehler das nur volle 8.3 char gehn
private static DirEntry fat_read_dir_ent(FatLowLevel fat_access, int cluster, int index, DirEntry entry) throws IOException
{
//System.out.println("fat_read_dir_ent()");
int currentIndex = 0;
int block = 0;
if (cluster == 0) {
block = fat_root_dir_addr(fat_access);
} else {
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu laden ist
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, cluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
}
// auslesen des gesamten Root Directory
for (int blk = block;; blk++) {
fat_access.ReadSector(blk, tmp_buffer); // Lesen eines Blocks des
// Root Directory
for (int a = 0; a < BlockSize; a = a + 32) {
entry.initialize(tmp_buffer, a); // Zeiger auf aktuellen Verzeichniseintrag holen
// Kein weiterer Eintrag wenn erstes Zeichen des Namens 0 ist
if (entry.DIR_Name[0] == 0)
{
throw FNFExc;
}
// Prfen ob es ein 8.3 Eintrag ist
// Das ist der Fall wenn es sich nicht um einen Eintrag fr lange
// Dateinamen oder um einen als gelscht markierten Eintrag handelt.
if ((entry.DIR_Attr != ATTR_LONG_NAME) && (entry.DIR_Name[0] != DIR_ENTRY_IS_FREE)) {
// Ist es der gewnschte Verzeichniseintrag
if (currentIndex == index) {
return entry;
}
currentIndex++;
}
}
}
}
private static int fat_get_free_cluster(FatLowLevel fat_access, int block)
{
//System.out.println("fat_get_free_cluster");
// FAT Block Adresse
int blockAddress;
// Berechnung fr den ersten FAT Block (FAT Start Addresse)
blockAddress = volume_boot_record_addr + fat_offset;
for (int a = 0;; a++) {
if (!(blockAddress < fat_size)) {
block = 0xFFFFFFFF;
return block;
}
// Lesen des FAT Blocks
fat_access.ReadSector(blockAddress, tmp_buffer);
for (int b = 0; b < BlockSize; b = b + 2) {
if (((tmp_buffer[b] & 0x000000FF) == 0x00)
&& ((tmp_buffer[b + 1] & 0x000000FF) == 0x00)) {
block = ((((blockAddress - volume_boot_record_addr - fat_offset) & 0x000000FF) << 8) | ((b / 2) & 0x000000FF));
tmp_buffer[b + 0] = 0xFF;
tmp_buffer[b + 1] = 0xFF;
fat_access.WriteSector(blockAddress, tmp_buffer);
fat_access.WriteSector((blockAddress + fat_size), tmp_buffer);
return block;
}
}
blockAddress++;
}
}
public static void fat_grow_file(FatLowLevel fat_access, int cluster) {
int last_cluster = 0xFFFFFFFF;
int i = 0;
int block = i;
block = fat_load(fat_access, cluster, block);
last_cluster = (block & 0x0000FFFF);
// Loop until end of chain
while ((block & 0x0000FFFF) < 0x0000FFF8) {
i++;
last_cluster = (block & 0x0000FFFF);
block = i;
block = fat_load(fat_access, cluster, block);
}
// Find free Cluster & Mark new cluster as end of chain
block = fat_get_free_cluster(fat_access, block);
// Mark new cluster as end of chain
fat_set_cluster_value(fat_access, last_cluster, block);
}
private static void fat_set_cluster_value(FatLowLevel fat_access, int cluster, int nextCluster) {
//System.out.println("fat_set_cluster_value");
// Byte Adresse innerhalb des Fat Blocks
int byteAddress;
// FAT Block Adresse
int blockAddress;
byteAddress = (cluster * 2) % BlockSize;
// Berechnung des Blocks der gelesen werden mu
blockAddress = ((cluster * 2) / BlockSize) + volume_boot_record_addr + fat_offset;
// FAT_Block_Store = FAT_Block_Addresse;
// Lesen des FAT Blocks
fat_access.ReadSector(blockAddress, tmp_buffer);
tmp_buffer[byteAddress + 0] = (0x000000FF & nextCluster);
tmp_buffer[byteAddress + 1] = (0x000000FF & (nextCluster >> 8));
fat_access.WriteSector(blockAddress, tmp_buffer);
fat_access.WriteSector((blockAddress + fat_size), tmp_buffer);
}
private static DirEntry fat_set_dir_ent(FatLowLevel fat_access, int dir_cluster, int index, long size, int dirAttrib, DirEntry entry) throws IOException
{
//System.out.println("fat_set_dir_ent");
int currentIndex = 0;
int block = 0;
if (dir_cluster == 0) {
block = fat_root_dir_addr(fat_access);
} else {
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu laden ist
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, dir_cluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
}
// auslesen des gesamten Directory
for (int blk = block;; blk++) {
fat_access.ReadSector(blk, tmp_buffer); // Lesen eines Blocks des
// Root Directory
for (int a = 0; a < BlockSize; a = a + 32) {
entry.initialize(tmp_buffer, a); // Zeiger auf aktuellen Verzeichniseintrag holen
if (entry.DIR_Name[0] == 0) // Kein weiterer Eintrag wenn erstes
// Zeichen des Namens 0 ist
{
throw IOExc;
}
// Prfen ob es ein 8.3 Eintrag ist
// Das ist der Fall wenn es sich nicht um einen Eintrag fr lange
// Dateinamen
// oder um einen als gelscht markierten Eintrag handelt.
if ((entry.DIR_Attr != ATTR_LONG_NAME) && (entry.DIR_Name[0] != DIR_ENTRY_IS_FREE)) {
// System.out.println("a not long "+a);
// Ist es der gewnschte Verzeichniseintrag
if (currentIndex == index) {
entry.DIR_Attr = dirAttrib;
entry.DIR_FileSize = (int)size;
entry.dump(tmp_buffer, a);
// Eintrag gefunden ndern und speichern
fat_access.WriteSector(blk, tmp_buffer);
return entry;
}
currentIndex++;
}
}
}
}
// -----------------------------------------------------------------------------
// FAT32_FindFreeOffset: Find a free space in the directory for a new entry
// which takes up 'entryCount' blocks (or allocate some more)
// -----------------------------------------------------------------------------
private static int fat_find_free_offset(FatLowLevel fat_access, int dir_cluster, DirEntry entry) {
//System.out.println("fat_find_free_offset");
int currentIndex = 0;
int block = 0;
if (dir_cluster == 0) {
block = fat_root_dir_addr(fat_access);
} else {
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu laden ist
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, dir_cluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
}
// auslesen des gesamten Directory
for (int blk = block;; blk++) {
fat_access.ReadSector(blk, tmp_buffer); // Lesen eines Blocks des Root Directory
for (int a = 0; a < BlockSize; a = a + 32) {
entry.initialize(tmp_buffer, a); // Zeiger auf aktuellen Verzeichniseintrag holen
if (entry.DIR_Name[0] == 0) // Kein weiterer Eintrag wenn erstes
// Zeichen des Namens 0 ist
{
if ((a + 32) < BlockSize) {
tmp_buffer[a + 32] = 0;
fat_access.WriteSector(blk, tmp_buffer);
return currentIndex;
} else {
return -1;
}
}
// Prfen ob es ein 8.3 Eintrag ist
// Das ist der Fall wenn es sich nicht um einen Eintrag fr lange
// Dateinamen
// oder um einen als gelscht markierten Eintrag handelt.
if ((entry.DIR_Attr != ATTR_LONG_NAME) && (entry.DIR_Name[0] == DIR_ENTRY_IS_FREE)) {
// Ist es der gewnschte Verzeichniseintrag
return currentIndex;
}
currentIndex++;
}
}
}
// -----------------------------------------------------------------------------
// FAT32_AddFileEntry: Add a directory entry to a location found by
// FindFreeOffset
// -----------------------------------------------------------------------------
public static DirEntry fat_add_file(FatLowLevel fat_access, int dirCluster, char[] filename, DirEntry entry) throws IOException {
//System.out.println("fat_add_file");
int block = 0;
int startcluster = 0;
int index = 0;
// BLOCK 2[0] = next free cluster = start cluster for new file with size
// =1
block = fat_get_free_cluster(fat_access, block);
startcluster = block;
// Find space in the directory for this filename (or allocate some more)
if ((index = fat_find_free_offset(fat_access, dirCluster, entry)) < 0) {
throw IOExc;
}
// Main cluster following loop
int currentIndex = 0;
// System.out.println("dir_cluster[0] ="+dir_cluster[0]);
if (dirCluster == 0) {
block = fat_root_dir_addr(fat_access);
} else {
// Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
// Berechnung welcher Cluster zu laden ist
// Auslesen der FAT - Tabelle
block = fat_load(fat_access, dirCluster, block);
block = ((block - 2) * cluster_size) + cluster_offset;
}
// auslesen des gesamten Directory
for (int blk = block;; blk++) {
fat_access.ReadSector(blk, tmp_buffer); // Lesen eines Blocks des Root Directory
for (int a = 0; a < BlockSize; a = a + 32) {
if (currentIndex == index) {
entry.setName(filename);
entry.DIR_Attr = ATTR_ARCHIVE;
entry.DIR_FstClusLO = startcluster;
entry.DIR_FileSize = 0;
entry.dump(tmp_buffer, a);
// Eintrag gefunden ndern und speichern
fat_access.WriteSector(blk, tmp_buffer);
return entry;
}
currentIndex++;
}
}
}
public static int fat_unlink() {
// TODO implement delete file operation
return 0;
}
// create filesystem with mbr and one (minimal) partition
public static int fat_mkfs(FatLowLevel fat_access) {
int mbr[] = new int[512];
int bootsector[] = new int[512];
int retvalue=0;
// Clear medium in preparation for formatting
System.out.println("Clearing medium");
fat_access.ClearMedium();
System.out.println("Constructing MBR");
// Create makeshift MBR
// Address of first partition
mbr[FatItS.VBR_ADDR] = 0x01;
mbr[FatItS.VBR_ADDR+1] = 0x00;
// MBR signature
mbr[0x1FE] = 0x55;
mbr[0x1FF] = 0xAA;
// Construct boot sector
System.out.println("Constructing partition header");
bootsector = BootSec.construct(fat_access.GetTotalBytes()-mbr.length);
// Write MBR
System.out.println("Writing MBR");
retvalue = fat_access.WriteSector(MASTER_BOOT_RECORD, mbr);
if (retvalue!=0) {
System.out.println("Error writing MBR Sector");
}
// Write Partition and Bootsector directly after the MBR
if (retvalue==0) {
System.out.println("Writing partition header");
retvalue = fat_access.WriteSector(1, bootsector);
}
// Flush buffers
fat_access.Flush();
return retvalue;
}
}