libfat improvements: now actually works, and uses fat32.

This commit is contained in:
zeromus 2010-09-04 02:57:52 +00:00
parent cf5ba963c0
commit b1362ccb40
5 changed files with 656 additions and 198 deletions

View File

@ -327,6 +327,9 @@ void build_ListCallback(FsEntry* fs, EListCallbackArg arg)
{ {
char* fname = (strlen(fs->cAlternateFileName)>0) ? fs->cAlternateFileName : fs->cFileName; char* fname = (strlen(fs->cAlternateFileName)>0) ? fs->cAlternateFileName : fs->cFileName;
//we use cFileName always because it is a LFN and we are making sure that we always make a fat32 image
fname = fs->cFileName;
if(arg == EListCallbackArg_Pop) if(arg == EListCallbackArg_Pop)
{ {
currFatFile = fatStack.top(); currFatFile = fatStack.top();
@ -384,15 +387,22 @@ static BOOL cflash_build_fat()
currPath = sFlashPath; currPath = sFlashPath;
list_files(sFlashPath.c_str(), count_ListCallback); list_files(sFlashPath.c_str(), count_ListCallback);
dataSectors += 8; //a few for reserved sectors, etc.
dataSectors += 16*1024*1024/512; //add 16MB worth of write space. this is probably enough for anyone, but maybe it should be configurable. dataSectors += 16*1024*1024/512; //add 16MB worth of write space. this is probably enough for anyone, but maybe it should be configurable.
//we could always suggest to users to add a big file to their directory to overwrite (that would cause the image to get padded) //we could always suggest to users to add a big file to their directory to overwrite (that would cause the image to get padded)
//this seems to be the minimum size that will turn into a solid fat32
if(dataSectors<36*1024*1024/512)
dataSectors = 36*1024*1024/512;
delete file; delete file;
file = new EMUFILE_MEMORY(dataSectors*512+1); file = new EMUFILE_MEMORY(dataSectors*512);
//file = new EMUFILE_FILE("c:\\temp.ima","rb+");
EmuFat fat(file); EmuFat fat(file);
EmuFatVolume vol; EmuFatVolume vol;
u8 ok = vol.init(&fat); u8 ok = vol.init(&fat);
vol.format(dataSectors); vol.formatNew(dataSectors);
reconstruct(&currFatFile); reconstruct(&currFatFile);
currFatFile.openRoot(&vol); currFatFile.openRoot(&vol);
@ -772,11 +782,6 @@ static unsigned int cflash_read(unsigned int address)
{ {
unsigned int ret_value = 0; unsigned int ret_value = 0;
size_t elems_read = 0; size_t elems_read = 0;
#if 0 /* used by next if 0 block */
#define BUFFERED_BLOCK_SIZE 512
static u8 block_buffer[BUFFERED_BLOCK_SIZE];
static s32 buffered_start_index = -1;
#endif
switch (address) switch (address)
{ {

View File

@ -1,36 +1,74 @@
//Copyright (C) 2009-2010 DeSmuME team /* Copyright 2009-2010 DeSmuME team
This file 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 file 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 the this software. If not, see <http://www.gnu.org/licenses/>.
*/
//based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ ) //based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ )
/* //Copyright (C) 2009 by William Greiman
* Copyright (C) 2009 by William Greiman
* //based on mkdosfs - utility to create FAT/MS-DOS filesystems
* This file is free software: you can redistribute it and/or modify //Copyright (C) 1991 Linus Torvalds <torvalds@klaava.helsinki.fi>
* it under the terms of the GNU General Public License as published by //Copyright (C) 1992-1993 Remy Card <card@masi.ibp.fr>
* the Free Software Foundation, either version 3 of the License, or //Copyright (C) 1993-1994 David Hudson <dave@humbug.demon.co.uk>
* (at your option) any later version. //Copyright (C) 1998 H. Peter Anvin <hpa@zytor.com>
* //Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
* This file 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 the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "emufat.h" #include "emufat.h"
static const u8 mkdosfs_bootcode[420] = #define LE16(x) (x)
#define LE32(x) (x)
#define MAX_CLUST_12 ((1 << 12) - 16)
#define MAX_CLUST_16 ((1 << 16) - 16)
#define MIN_CLUST_32 65529
/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong
* to the cluster number. So the max. cluster# is based on 2^28 */
#define MAX_CLUST_32 ((1 << 28) - 16)
#define FAT12_THRESHOLD 4085
#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */
#define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */
#define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */
#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */
static const int sector_size = 512;
#define BLOCK_SIZE 512
#define HARD_SECTOR_SIZE 512
#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE )
#define FAT_EOF (0x0ffffff8)
#define BOOT_SIGN 0xAA55 /* Boot sector magic number */
struct __PACKED fat32_fsinfo {
u32 reserved1; /* Nothing as far as I can tell */
u32 signature; /* 0x61417272L */
u32 free_clusters; /* Free cluster count. -1 if unknown */
u32 next_cluster; /* Most recently allocated cluster.
* Unused under Linux. */
u32 reserved2[4];
};
//see mkdosfs for the disassembly
static const u8 mkdosfs_bootcode_fat32[420] =
{ {
0xFE, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x61, 0x20, 0x62, 0x0E, 0x1F, 0xBE, 0x77, 0x7C, 0xAC, 0x22, 0xC0, 0x74, 0x0B, 0x56, 0xB4, 0x0E, 0xBB, 0x07, 0x00,
0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x20, 0x20, 0x50, 0xCD, 0x10, 0x5E, 0xEB, 0xF0, 0x32, 0xE4, 0xCD, 0x16, 0xCD, 0x19, 0xEB, 0xFE, 0x54, 0x68, 0x69,
0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x61,
0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6C, 0x6F, 0x70, 0x70, 0x79, 0x20, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x20, 0x20, 0x50, 0x6C, 0x65, 0x61, 0x73,
0x6E, 0x64, 0x0D, 0x0A, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x65, 0x20, 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x61,
0x79, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6E, 0x20, 0x2E, 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6C, 0x6F, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6E, 0x64, 0x0D, 0x0A,
0x2E, 0x2E, 0x20, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x74, 0x6F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6E, 0x20, 0x2E, 0x2E, 0x2E, 0x20, 0x0D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -80,7 +118,6 @@ EmuFat::~EmuFat()
delete m_pFile; delete m_pFile;
} }
u8 EmuFat::cacheRawBlock(u32 blockNumber, u8 action) u8 EmuFat::cacheRawBlock(u32 blockNumber, u8 action)
{ {
if (cache_.cacheBlockNumber_ != blockNumber) { if (cache_.cacheBlockNumber_ != blockNumber) {
@ -105,6 +142,11 @@ u8 EmuFat::cacheZeroBlock(u32 blockNumber)
return true; return true;
} }
void EmuFat::cacheReset()
{
reconstruct(&cache_);
}
u8 EmuFat::cacheFlush() { u8 EmuFat::cacheFlush() {
if (cache_.cacheDirty_) { if (cache_.cacheDirty_) {
if (!writeBlock(cache_.cacheBlockNumber_, cache_.cacheBuffer_.data)) { if (!writeBlock(cache_.cacheBlockNumber_, cache_.cacheBuffer_.data)) {
@ -165,74 +207,437 @@ void EmuFat::truncate(u32 size)
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
inline int cdiv (int a, int b)
{
return (a + b - 1) / b;
}
bool calculateClusterSize(TFat32BootSector* bsp, u32 avail_sectors, u32& cluster_count, u32& fat_length, int size_fat_by_user, int &size_fat)
{
TFat32BootSector &bs = *bsp;
const u32 fatdata = avail_sectors;
int maxclustsize = bsp->sectorsPerCluster;
u32 fatlength12, fatlength16, fatlength32;
u32 maxclust12, maxclust16, maxclust32;
u32 clust12, clust16, clust32;
do {
printf( "Trying with %d sectors/cluster:\n", bs.sectorsPerCluster );
/* The factor 2 below avoids cut-off errors for nr_fats == 1.
* The "nr_fats*3" is for the reserved first two FAT entries */
clust12 = 2*((u64) fatdata *sector_size + bs.fatCount*3) /
(2*(int) bs.sectorsPerCluster * sector_size + bs.fatCount*3);
fatlength12 = cdiv (((clust12+2) * 3 + 1) >> 1, sector_size);
/* Need to recalculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust12 = (fatdata - bs.fatCount*fatlength12)/bs.sectorsPerCluster;
maxclust12 = (fatlength12 * 2 * sector_size) / 3;
if (maxclust12 > MAX_CLUST_12)
maxclust12 = MAX_CLUST_12;
printf( "FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
clust12, fatlength12, maxclust12, MAX_CLUST_12 );
if (clust12 > maxclust12-2) {
clust12 = 0;
printf( "FAT12: too much clusters\n" );
}
clust16 = ((u64) fatdata *sector_size + bs.fatCount*4) /
((int) bs.sectorsPerCluster * sector_size + bs.fatCount*2);
fatlength16 = cdiv ((clust16+2) * 2, sector_size);
/* Need to recalculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust16 = (fatdata - bs.fatCount*fatlength16)/bs.sectorsPerCluster;
maxclust16 = (fatlength16 * sector_size) / 2;
if (maxclust16 > MAX_CLUST_16)
maxclust16 = MAX_CLUST_16;
printf( "FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
clust16, fatlength16, maxclust16, MAX_CLUST_16 );
if (clust16 > maxclust16-2) {
printf( "FAT16: too much clusters\n" );
clust16 = 0;
}
/* The < 4078 avoids that the filesystem will be misdetected as having a
* 12 bit FAT. */
if (clust16 < FAT12_THRESHOLD && !(size_fat_by_user && size_fat == 16)) {
printf( clust16 < FAT12_THRESHOLD ?
"FAT16: would be misdetected as FAT12\n" :
"FAT16: too much clusters\n" );
clust16 = 0;
}
clust32 = ((u64) fatdata *sector_size + bs.fatCount*8) /
((int) bs.sectorsPerCluster * sector_size + bs.fatCount*4);
fatlength32 = cdiv ((clust32+2) * 4, sector_size);
/* Need to recalculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust32 = (fatdata - bs.fatCount*fatlength32)/bs.sectorsPerCluster;
maxclust32 = (fatlength32 * sector_size) / 4;
if (maxclust32 > MAX_CLUST_32)
maxclust32 = MAX_CLUST_32;
if (clust32 && clust32 < MIN_CLUST_32 && !(size_fat_by_user && size_fat == 32)) {
clust32 = 0;
printf( "FAT32: not enough clusters (%d)\n", MIN_CLUST_32);
}
printf( "FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
clust32, fatlength32, maxclust32, MAX_CLUST_32 );
if (clust32 > maxclust32) {
clust32 = 0;
printf( "FAT32: too much clusters\n" );
}
if ((clust12 && (size_fat == 0 || size_fat == 12)) ||
(clust16 && (size_fat == 0 || size_fat == 16)) ||
(clust32 && size_fat == 32))
break;
bs.sectorsPerCluster <<= 1;
} while (bs.sectorsPerCluster && bs.sectorsPerCluster <= maxclustsize);
/* Use the optimal FAT size if not specified;
* FAT32 is (not yet) choosen automatically */
if (!size_fat) {
size_fat = (clust16 > clust12) ? 16 : 12;
printf( "Choosing %d bits for FAT\n", size_fat );
}
switch (size_fat) {
case 12:
cluster_count = clust12;
fat_length = fatlength12;
bs.sectorsPerFat16 = LE16(fatlength12);
break;
case 16:
if (clust16 < FAT12_THRESHOLD) {
if (size_fat_by_user) {
printf("WARNING: Not enough clusters for a "
"16 bit FAT! The filesystem will be\n"
"misinterpreted as having a 12 bit FAT without "
"mount option \"fat=16\".\n" );
return false;
}
else {
printf("This filesystem has an unfortunate size. "
"A 12 bit FAT cannot provide\n"
"enough clusters, but a 16 bit FAT takes up a little "
"bit more space so that\n"
"the total number of clusters becomes less than the "
"threshold value for\n"
"distinction between 12 and 16 bit FATs.\n" );
return false;
}
}
cluster_count = clust16;
fat_length = fatlength16;
bs.sectorsPerFat16 = LE16(fatlength16);
break;
case 32:
if (clust32 < MIN_CLUST_32)
printf("WARNING: Not enough clusters for a 32 bit FAT!\n");
cluster_count = clust32;
fat_length = fatlength32;
bs.sectorsPerFat16 = LE16(0);
bs.fat32.sectorsPerFat32 = LE32(fatlength32);
break;
}
return true;
}
static void mark_FAT_cluster (int size_fat, u8* fat, int cluster, unsigned int value)
{
switch( size_fat ) {
case 12:
value &= 0x0fff;
if (((cluster * 3) & 0x1) == 0)
{
fat[3 * cluster / 2] = (unsigned char) (value & 0x00ff);
fat[(3 * cluster / 2) + 1] = (unsigned char) ((fat[(3 * cluster / 2) + 1] & 0x00f0)
| ((value & 0x0f00) >> 8));
}
else
{
fat[3 * cluster / 2] = (unsigned char) ((fat[3 * cluster / 2] & 0x000f) | ((value & 0x000f) << 4));
fat[(3 * cluster / 2) + 1] = (unsigned char) ((value & 0x0ff0) >> 4);
}
break;
case 16:
value &= 0xffff;
fat[2 * cluster] = (unsigned char) (value & 0x00ff);
fat[(2 * cluster) + 1] = (unsigned char) (value >> 8);
break;
case 32:
value &= 0xfffffff;
fat[4 * cluster] = (unsigned char) (value & 0x000000ff);
fat[(4 * cluster) + 1] = (unsigned char) ((value & 0x0000ff00) >> 8);
fat[(4 * cluster) + 2] = (unsigned char) ((value & 0x00ff0000) >> 16);
fat[(4 * cluster) + 3] = (unsigned char) ((value & 0xff000000) >> 24);
break;
}
}
//use 36M as minimum fat32 size (or else mkdosfs complains)
//this function assumes fat32. it could be redone to be intelligent by making another pass through mkdosfs and analyzing it again
//but we onnly targeted fat32 our first time through
bool EmuFatVolume::formatNew(u32 sectors)
{
u32 volumeStartBlock = 0;
TFat32BootSector bsrec;
memset(&bsrec,0,sizeof(TFat32BootSector));
TFat32BootSector *bs = &bsrec;
//perform same analysis (we guess) as mkdosfs
//"fake values"
bs->sectorsPerTrack = 32;
bs->headCount = 64;
//def_hd_params:
bs->mediaType = 0xF8;
bs->rootDirEntryCount = LE16(512); //Default to 512 entries - N.B. this is overwritten later
static const u32 BLOCK_SIZE_BITS = 9;
const u32 sz_mb = (sectors+(1<<(20-BLOCK_SIZE_BITS))-1) >> (20-BLOCK_SIZE_BITS);
bs->sectorsPerCluster =
sz_mb > 16*1024 ? 32 :
sz_mb > 8*1024 ? 16 :
sz_mb > 260 ? 8 :
1;
//(fat16 and fat12 would start at 4 sectors per cluster)
memcpy (bs->oemName, "mkdosfs", 8);
bs->rootDirEntryCount = 0; //Under FAT32, the root dir is in a cluster chain, and this is signalled by bs.dir_entries being 0
bs->fat32.vi.volume_id = 0; //not generating a volume id.. just use 0 for determinism's sake
memcpy(bs->fat32.vi.volume_label," ",11);
bs->jmpToBootCode[0] = 0xEB;
bs->jmpToBootCode[1] = 0x58; //this value is only for fat32 //Patch in the correct offset to the boot code
bs->jmpToBootCode[2] = 0x90;
memcpy(bs->fat32.boot_code,mkdosfs_bootcode_fat32,420);
bs->boot_sign[0] = 0x55;
bs->boot_sign[1] = 0xAA;
bs->reservedSectorCount = LE16(32);
bs->fatCount = 2;
bs->hiddenSectors = LE32(0);
u32 fatdata = sectors - cdiv (bs->rootDirEntryCount * 32, 512) - bs->reservedSectorCount;
u32 cluster_count;
u32 fat_length;
int size_fat = 32;
if(!calculateClusterSize(bs, fatdata, cluster_count, fat_length, 1, size_fat))
return false;
//TODO - this function whacks values we set earlier. gross. either mkdosfs is sloppy or i dont understand it.
//anyway, whack that dup code
switch(size_fat)
{
case 12: memcpy(bs->oldfat.vi.fs_type, MSDOS_FAT12_SIGN, 8); break;
case 16: memcpy(bs->oldfat.vi.fs_type, MSDOS_FAT16_SIGN, 8); break;
case 32: memcpy(bs->fat32.vi.fs_type, MSDOS_FAT32_SIGN, 8); break;
}
bs->bytesPerSector = 512;
//set up additional FAT32 fields
bs->fat32.fat32Flags = LE16(0);
bs->fat32.fat32Version = LE16(0);
bs->fat32.fat32RootCluster = LE32(2);
bs->fat32.fat32FSInfo = LE16(1);
u32 backup_boot = (bs->reservedSectorCount>= 7) ? 6 : (bs->reservedSectorCount >= 2) ? bs->reservedSectorCount-1 : 0;
printf( "Using sector %d as backup boot sector (0 = none)\n",backup_boot );
bs->fat32.fat32BackBootBlock = LE16(backup_boot);
memset(bs->fat32.fat32Reserved,0,sizeof(bs->fat32.fat32Reserved));
if(sectors>= 65536) {
bs->totalSectors16 = LE16(0);
bs->totalSectors32 = LE32(sectors);
} else {
bs->totalSectors16 = LE16(sectors);
bs->totalSectors32 = LE32(0);
}
if (!cluster_count)
{
//if (sectors_per_cluster) /* If yes, die if we'd spec'd sectors per cluster */
// die ("Too many clusters for file system - try more sectors per cluster");
//else
printf("Attempting to create a too large file system");
return false;
}
u32 start_data_sector = (bs->reservedSectorCount + bs->fatCount * fat_length) * (sector_size/512);
u32 start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
if (sectors < start_data_block + 32) /* Arbitrary undersize file system! */
{
printf("Too few blocks for viable file system");
return false;
}
bs->fat32.vi.ext_boot_sign = MSDOS_EXT_SIGN;
//Make the file allocation tables!
u8* fat = new u8[fat_length * sector_size];
memset( fat, 0, fat_length * sector_size );
mark_FAT_cluster (size_fat, fat, 0, 0xffffffff); /* Initial fat entries */
mark_FAT_cluster (size_fat, fat, 1, 0xffffffff);
fat[0] = bs->mediaType; /* Put media type in first byte! */
if (size_fat == 32) {
/* Mark cluster 2 as EOF (used for root dir) */
mark_FAT_cluster (size_fat, fat, 2, FAT_EOF);
}
u32 size_root_dir = (size_fat == 32) ? bs->sectorsPerCluster*sector_size :
bs->rootDirEntryCount * sizeof (TDirectoryEntry);
//u8* root_dir = new u8[size_root_dir];
//memset(root_dir, 0, size_root_dir);
u32 size_root_dir_in_sectors = size_root_dir/512;
u8* info_sector = NULL;
if (size_fat == 32) {
/* For FAT32, create an info sector */
fat32_fsinfo *info;
info_sector = new u8[sector_size];
memset(info_sector, 0, sector_size);
/* fsinfo structure is at offset 0x1e0 in info sector by observation */
info = (fat32_fsinfo *)(info_sector + 0x1e0);
/* Info sector magic */
info_sector[0] = 'R';
info_sector[1] = 'R';
info_sector[2] = 'a';
info_sector[3] = 'A';
/* Magic for fsinfo structure */
info->signature = LE32(0x61417272);
/* We've allocated cluster 2 for the root dir. */
info->free_clusters = LE32(cluster_count - 1);
info->next_cluster = LE32(2);
/* Info sector also must have boot sign */
*(u16 *)(info_sector + 0x1fe) = LE16(BOOT_SIGN);
}
//-------------
//write_tables()
u8* blank_sector = new u8[512];
memset(blank_sector,0,512);
dev_->cacheReset();
dev_->truncate(0);
dev_->truncate(sectors*512);
/* clear all reserved sectors */
for(int i=0;i<bs->reservedSectorCount;i++)
dev_->writeBlock(0,blank_sector);
/* seek back to sector 0 and write the boot sector */
dev_->writeBlock(0,(const u8*)bs);
/* on FAT32, write the info sector and backup boot sector */
if (size_fat == 32)
{
dev_->writeBlock(bs->fat32.fat32FSInfo,info_sector);
if(bs->fat32.fat32BackBootBlock)
dev_->writeBlock(bs->fat32.fat32BackBootBlock,(const u8*)bs);
}
/* seek to start of FATS and write them all */
int ctr=bs->reservedSectorCount;
for (int i=0;i<bs->fatCount;i++)
for(int j=0;j<fat_length;j++,ctr++)
dev_->writeBlock(ctr,fat+j*sector_size);
/* Write the root directory directly after the last FAT. This is the root
* dir area on FAT12/16, and the first cluster on FAT32. */
for(int i=0;i<size_root_dir_in_sectors;i++)
dev_->writeBlock(ctr,blank_sector);
delete[] blank_sector;
delete[] info_sector;
delete[] fat;
return init(dev_,0);
//return true;
}
//well, there are a lot of ways to format a disk. this is just a simple one. //well, there are a lot of ways to format a disk. this is just a simple one.
//it would be nice if someone who understood fat better could modify the root //it would be nice if someone who understood fat better could modify the root
//directory setup to use reasonable code instead of magic arrays //directory setup to use reasonable code instead of magic arrays
bool EmuFatVolume::format(u32 sectors) bool EmuFatVolume::format(u32 sectors)
{ {
u32 volumeStartBlock = 0; //u32 volumeStartBlock = 0;
dev_->truncate(0); //dev_->truncate(0);
dev_->truncate(sectors*512); //dev_->truncate(sectors*512);
if (!dev_->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_WRITE)) return false; //if (!dev_->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_WRITE)) return false;
memset(&dev_->cache_.cacheBuffer_,0,sizeof(dev_->cache_.cacheBuffer_)); //memset(&dev_->cache_.cacheBuffer_,0,sizeof(dev_->cache_.cacheBuffer_));
TFat32BootSector* bs = &dev_->cache_.cacheBuffer_.fbs; //TFat32BootSector* bs = &dev_->cache_.cacheBuffer_.fbs;
TBiosParmBlock* bpb = &bs->bpb; //TBiosParmBlock* bpb = &bs->bpb;
bs->jmpToBootCode[0] = 0xEB; //bs->jmpToBootCode[0] = 0xEB;
bs->jmpToBootCode[1] = 0x3C; //bs->jmpToBootCode[1] = 0x3C;
bs->jmpToBootCode[2] = 0x90; //bs->jmpToBootCode[2] = 0x90;
memcpy(bs->oemName,"mkdosfs",8); //memcpy(bs->oemName,"mkdosfs",8);
bs->driveNumber = 0; //bs->driveNumber = 0;
bs->reserved1 = 0; //bs->reserved1 = 0;
bs->bootSignature = 0x29; //bs->bootSignature = 0x29;
bs->volumeSerialNumber = 0; //bs->volumeSerialNumber = 0;
memcpy(bs->volumeLabel," ",11); //memcpy(bs->volumeLabel," ",11);
memcpy(bs->fileSystemType,"FAT16 ",8); //memcpy(bs->fileSystemType,"FAT16 ",8);
memcpy(bs->bootCode,mkdosfs_bootcode,420); //memcpy(bs->bootCode,mkdosfs_bootcode,420);
bs->bootSectorSig0 = 0x55; //bs->bootSectorSig0 = 0x55;
bs->bootSectorSig1 = 0xAA; //bs->bootSectorSig1 = 0xAA;
bpb->bytesPerSector = 512; //bpb->bytesPerSector = 512;
bpb->sectorsPerCluster = 4; //bpb->sectorsPerCluster = 4;
bpb->reservedSectorCount = 1; //bpb->reservedSectorCount = 1;
bpb->fatCount = 2; //bpb->fatCount = 2;
bpb->rootDirEntryCount = 512; //bpb->rootDirEntryCount = 512;
bpb->totalSectors16 = 0; //bpb->totalSectors16 = 0;
bpb->mediaType = 0xF8; //bpb->mediaType = 0xF8;
bpb->sectorsPerFat16 = 32; //bpb->sectorsPerFat16 = 32;
bpb->sectorsPerTrack = 32; //bpb->sectorsPerTrack = 32;
bpb->headCount = 64; //bpb->headCount = 64;
bpb->hiddenSectors = 0; //bpb->hiddenSectors = 0;
bpb->totalSectors32 = sectors; //bpb->totalSectors32 = sectors;
bpb->fat32Flags = 0xbe0d; //bpb->fat32Flags = 0xbe0d;
bpb->fat32Version = 0x20Fd; //bpb->fat32Version = 0x20Fd;
bpb->fat32RootCluster = 0x20202020; //bpb->fat32RootCluster = 0x20202020;
bpb->fat32FSInfo = 0x2020; //bpb->fat32FSInfo = 0x2020;
bpb->fat32BackBootBlock = 0x2020; //bpb->fat32BackBootBlock = 0x2020;
if(!dev_->cacheFlush()) //if(!dev_->cacheFlush())
return false; // return false;
if (!dev_->cacheRawBlock(1, EmuFat::CACHE_FOR_WRITE)) return false; //if (!dev_->cacheRawBlock(1, EmuFat::CACHE_FOR_WRITE)) return false;
static const u8 rootEntry[8] = //static const u8 rootEntry[8] =
{ //{
0xF8, 0xFF, 0xFF, 0xFF, // 0xF8, 0xFF, 0xFF, 0xFF,
} ; //} ;
memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4); //memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4);
if(!dev_->cacheFlush()) //if(!dev_->cacheFlush())
return false; // return false;
if (!dev_->cacheRawBlock(33, EmuFat::CACHE_FOR_WRITE)) return false; //if (!dev_->cacheRawBlock(33, EmuFat::CACHE_FOR_WRITE)) return false;
memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4); //memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4);
if(!dev_->cacheFlush()) //if(!dev_->cacheFlush())
return false; // return false;
return init(dev_,0); //return init(dev_,0);
return false;
} }
bool EmuFatVolume::init(EmuFat* dev, u8 part) { bool EmuFatVolume::init(EmuFat* dev, u8 part) {
@ -253,16 +658,16 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) {
volumeStartBlock = p->firstSector; volumeStartBlock = p->firstSector;
} }
if (!dev->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_READ)) return false; if (!dev->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_READ)) return false;
TBiosParmBlock* bpb = &dev->cache_.cacheBuffer_.fbs.bpb; TFat32BootSector* bs = &dev->cache_.cacheBuffer_.fbs;
if (bpb->bytesPerSector != 512 || if (bs->bytesPerSector != 512 ||
bpb->fatCount == 0 || bs->fatCount == 0 ||
bpb->reservedSectorCount == 0 || bs->reservedSectorCount == 0 ||
bpb->sectorsPerCluster == 0) { bs->sectorsPerCluster == 0) {
// not valid FAT volume // not valid FAT volume
return false; return false;
} }
fatCount_ = bpb->fatCount; fatCount_ = bs->fatCount;
blocksPerCluster_ = bpb->sectorsPerCluster; blocksPerCluster_ = bs->sectorsPerCluster;
// determine shift that is same as multiply by blocksPerCluster_ // determine shift that is same as multiply by blocksPerCluster_
clusterSizeShift_ = 0; clusterSizeShift_ = 0;
@ -270,23 +675,23 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) {
// error if not power of 2 // error if not power of 2
if (clusterSizeShift_++ > 7) return false; if (clusterSizeShift_++ > 7) return false;
} }
blocksPerFat_ = bpb->sectorsPerFat16 ? blocksPerFat_ = bs->sectorsPerFat16 ?
bpb->sectorsPerFat16 : bpb->sectorsPerFat32; bs->sectorsPerFat16 : bs->fat32.sectorsPerFat32;
fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; fatStartBlock_ = volumeStartBlock + bs->reservedSectorCount;
// count for FAT16 zero for FAT32 // count for FAT16 zero for FAT32
rootDirEntryCount_ = bpb->rootDirEntryCount; rootDirEntryCount_ = bs->rootDirEntryCount;
// directory start for FAT16 dataStart for FAT32 // directory start for FAT16 dataStart for FAT32
rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; rootDirStart_ = fatStartBlock_ + bs->fatCount * blocksPerFat_;
// data start for FAT16 and FAT32 // data start for FAT16 and FAT32
dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511)/512); dataStartBlock_ = rootDirStart_ + ((32 * bs->rootDirEntryCount + 511)/512);
// total blocks for FAT16 or FAT32 // total blocks for FAT16 or FAT32
u32 totalBlocks = bpb->totalSectors16 ? u32 totalBlocks = bs->totalSectors16 ?
bpb->totalSectors16 : bpb->totalSectors32; bs->totalSectors16 : bs->totalSectors32;
// total data blocks // total data blocks
clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
@ -299,7 +704,7 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) {
} else if (clusterCount_ < 65525) { } else if (clusterCount_ < 65525) {
fatType_ = 16; fatType_ = 16;
} else { } else {
rootDirStart_ = bpb->fat32RootCluster; rootDirStart_ = bs->fat32.fat32RootCluster;
fatType_ = 32; fatType_ = 32;
} }
return true; return true;

View File

@ -1,22 +1,29 @@
//Copyright (C) 2009-2010 DeSmuME team /* Copyright 2009-2010 DeSmuME team
This file 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 file 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 the this software. If not, see <http://www.gnu.org/licenses/>.
*/
//based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ ) //based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ )
/* //Copyright (C) 2009 by William Greiman
* Copyright (C) 2009 by William Greiman
* //based on mkdosfs - utility to create FAT/MS-DOS filesystems
* This file is free software: you can redistribute it and/or modify //Copyright (C) 1991 Linus Torvalds <torvalds@klaava.helsinki.fi>
* it under the terms of the GNU General Public License as published by //Copyright (C) 1992-1993 Remy Card <card@masi.ibp.fr>
* the Free Software Foundation, either version 3 of the License, or //Copyright (C) 1993-1994 David Hudson <dave@humbug.demon.co.uk>
* (at your option) any later version. //Copyright (C) 1998 H. Peter Anvin <hpa@zytor.com>
* //Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
* This file 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 the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef EMUFAT_H #ifndef EMUFAT_H
#define EMUFAT_H #define EMUFAT_H
@ -25,6 +32,10 @@
#include "emufile.h" #include "emufile.h"
#include <stdio.h> #include <stdio.h>
#define BOOTCODE_SIZE 448
#define BOOTCODE_FAT32_SIZE 420
// use the gnu style oflag in open() // use the gnu style oflag in open()
/** open() oflag for reading */ /** open() oflag for reading */
static const u8 EO_READ = 0X01; static const u8 EO_READ = 0X01;
@ -129,16 +140,29 @@ struct __PACKED TMasterBootRecord {
u8 mbrSig1; u8 mbrSig1;
}; };
//BIOS parameter block struct __PACKED msdos_volume_info {
//The BIOS parameter block describes the physical layout of a FAT volume. u8 drive_number; /* BIOS drive number */
struct __PACKED TBiosParmBlock { u8 RESERVED; /* Unused */
u8 ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */
u32 volume_id; /* Volume ID number */
u8 volume_label[11];/* Volume label */
u8 fs_type[8]; /* Typically FAT12 or FAT16 */
};
//Boot sector for a FAT16 or FAT32 volume.
struct __PACKED TFat32BootSector {
//X86 jmp to boot program
u8 jmpToBootCode[3];
//informational only - don't depend on it
u8 oemName[8];
//Count of bytes per sector. This value may take on only the //Count of bytes per sector. This value may take on only the
//following values: 512, 1024, 2048 or 4096 //following values: 512, 1024, 2048 or 4096
u16 bytesPerSector; u16 bytesPerSector;
//Number of sectors per allocation unit. This value must be a //Number of sectors per allocation unit. This value must be a
//power of 2 that is greater than 0. The legal values are //power of 2 that is greater than 0. The legal values are
//1, 2, 4, 8, 16, 32, 64, and 128. //1, 2, 4, 8, 16, 32, 64, and 128.
u8 sectorsPerCluster; u8 sectorsPerCluster; //cluster_size
//Number of sectors before the first FAT. //Number of sectors before the first FAT.
//This value must not be zero. //This value must not be zero.
u16 reservedSectorCount; u16 reservedSectorCount;
@ -151,7 +175,7 @@ struct __PACKED TBiosParmBlock {
//value should always specify a count that when multiplied by 32 //value should always specify a count that when multiplied by 32
//results in a multiple of bytesPerSector. FAT16 volumes should //results in a multiple of bytesPerSector. FAT16 volumes should
//use the value 512. //use the value 512.
u16 rootDirEntryCount; u16 rootDirEntryCount; //dir_entries
//This field is the old 16-bit total count of sectors on the volume. //This field is the old 16-bit total count of sectors on the volume.
//This count includes the count of all sectors in all four regions //This count includes the count of all sectors in all four regions
//of the volume. This field can be 0; if it is 0, then totalSectors32 //of the volume. This field can be 0; if it is 0, then totalSectors32
@ -182,61 +206,56 @@ struct __PACKED TBiosParmBlock {
//of the volume. This field can be 0; if it is 0, then //of the volume. This field can be 0; if it is 0, then
//totalSectors16 must be non-zero. //totalSectors16 must be non-zero.
u32 totalSectors32; u32 totalSectors32;
//Count of sectors occupied by one FAT on FAT32 volumes.
u32 sectorsPerFat32;
//This field is only defined for FAT32 media and does not exist on
//FAT12 and FAT16 media.
//Bits 0-3 -- Zero-based number of active FAT.
// Only valid if mirroring is disabled.
//Bits 4-6 -- Reserved.
//Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
// -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
//Bits 8-15 -- Reserved.
u16 fat32Flags;
//FAT32 version. High byte is major revision number.
//Low byte is minor revision number. Only 0.0 define.
u16 fat32Version;
//Cluster number of the first cluster of the root directory for FAT32.
//This usually 2 but not required to be 2.
u32 fat32RootCluster;
//Sector number of FSINFO structure in the reserved area of the
//FAT32 volume. Usually 1.
u16 fat32FSInfo;
//If non-zero, indicates the sector number in the reserved area
//of the volume of a copy of the boot record. Usually 6.
//No value other than 6 is recommended.
u16 fat32BackBootBlock;
//Reserved for future expansion. Code that formats FAT32 volumes
//should always set all of the bytes of this field to 0.
u8 fat32Reserved[12];
};
//Boot sector for a FAT16 or FAT32 volume. union {
struct __PACKED TFat32BootSector { struct __PACKED {
//X86 jmp to boot program msdos_volume_info vi;
u8 jmpToBootCode[3]; u8 boot_code[BOOTCODE_SIZE];
//informational only - don't depend on it } oldfat;
u8 oemName[8];
//BIOS Parameter Block struct __PACKED
TBiosParmBlock bpb; {
//for int0x13 use value 0X80 for hard drive //Count of sectors occupied by one FAT on FAT32 volumes.
u8 driveNumber; u32 sectorsPerFat32; //fat32_length; /* sectors/FAT */
//used by Windows NT - should be zero for FAT
u8 reserved1; //This field is only defined for FAT32 media and does not exist on
//0X29 if next three fields are valid //FAT12 and FAT16 media.
u8 bootSignature; //Bits 0-3 -- Zero-based number of active FAT.
//usually generated by combining date and time // Only valid if mirroring is disabled.
u32 volumeSerialNumber; //Bits 4-6 -- Reserved.
//should match volume label in root dir //Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
u8 volumeLabel[11]; // -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
//informational only - don't depend on it //Bits 8-15 -- Reserved.
u8 fileSystemType[8]; u16 fat32Flags;// flags; /* bit 8: fat mirroring, low 4: active fat */
//X86 boot code
u8 bootCode[420]; //FAT32 version. High byte is major revision number.
//must be 0X55 //Low byte is minor revision number. Only 0.0 define.
u8 bootSectorSig0; u16 fat32Version;//version[2]; /* major, minor filesystem version */
//must be 0XAA
u8 bootSectorSig1; //Cluster number of the first cluster of the root directory for FAT32.
//This usually 2 but not required to be 2.
u32 fat32RootCluster; //root_cluster; /* first cluster in root directory */
//Sector number of FSINFO structure in the reserved area of the
//FAT32 volume. Usually 1.
u16 fat32FSInfo;// info_sector; /* filesystem info sector */
//If non-zero, indicates the sector number in the reserved area
//of the volume of a copy of the boot record. Usually 6.
//No value other than 6 is recommended.
u16 fat32BackBootBlock; //backup_boot; /* backup boot sector */
//Reserved for future expansion. Code that formats FAT32 volumes
//should always set all of the bytes of this field to 0.
u8 fat32Reserved[12]; //reserved2[6]; /* Unused */
msdos_volume_info vi;
u8 boot_code[BOOTCODE_FAT32_SIZE];
} fat32;
};
u8 boot_sign[2];
}; };
#include "PACKED_END.h" #include "PACKED_END.h"
@ -586,6 +605,7 @@ public:
bool init(EmuFat* dev, u8 part); bool init(EmuFat* dev, u8 part);
bool format(u32 sectors); bool format(u32 sectors);
bool formatNew(u32 sectors);
// inline functions that return volume info // inline functions that return volume info
//The volume's cluster size in blocks. //The volume's cluster size in blocks.
@ -687,6 +707,7 @@ private:
void cacheSetDirty() {cache_.cacheDirty_ |= CACHE_FOR_WRITE;} void cacheSetDirty() {cache_.cacheDirty_ |= CACHE_FOR_WRITE;}
u8 cacheZeroBlock(u32 blockNumber); u8 cacheZeroBlock(u32 blockNumber);
u8 cacheFlush(); u8 cacheFlush();
void cacheReset();
//IO functions: //IO functions:
u8 readBlock(u32 block, u8* dst); u8 readBlock(u32 block, u8* dst);

View File

@ -24,4 +24,42 @@ EMUFILE* EMUFILE::memwrap(EMUFILE* fp)
file->fread(mem->buf(),file->size()); file->fread(mem->buf(),file->size());
delete file; delete file;
return mem; return mem;
}
size_t EMUFILE_MEMORY::_fread(const void *ptr, size_t bytes){
u32 remain = len-pos;
u32 todo = std::min<u32>(remain,(u32)bytes);
if(len==0)
{
failbit = true;
return 0;
}
if(todo<=4)
{
u8* src = buf()+pos;
u8* dst = (u8*)ptr;
for(int i=0;i<todo;i++)
*dst++ = *src++;
}
else
{
memcpy((void*)ptr,buf()+pos,todo);
}
pos += todo;
if(todo<bytes)
failbit = true;
return todo;
}
void EMUFILE_FILE::truncate(s32 length)
{
fflush(fp);
#ifdef _MSC_VER
_chsize(_fileno(fp),length);
#else
ftruncate(fileno(fp),length);
#endif
fclose(fp);
fp = NULL;
open(fname.c_str(),mode);
} }

View File

@ -176,15 +176,7 @@ public:
return 0; return 0;
} }
virtual size_t _fread(const void *ptr, size_t bytes){ virtual size_t _fread(const void *ptr, size_t bytes);
u32 remain = len-pos;
u32 todo = std::min<u32>(remain,(u32)bytes);
memcpy((void*)ptr,buf()+pos,todo);
pos += todo;
if(todo<bytes)
failbit = true;
return todo;
}
//removing these return values for now so we can find any code that might be using them and make sure //removing these return values for now so we can find any code that might be using them and make sure
//they handle the return values correctly //they handle the return values correctly
@ -230,6 +222,8 @@ public:
class EMUFILE_FILE : public EMUFILE { class EMUFILE_FILE : public EMUFILE {
protected: protected:
FILE* fp; FILE* fp;
std::string fname;
char mode[16];
private: private:
void open(const char* fname, const char* mode) void open(const char* fname, const char* mode)
@ -237,6 +231,8 @@ private:
fp = fopen(fname,mode); fp = fopen(fname,mode);
if(!fp) if(!fp)
failbit = true; failbit = true;
this->fname = fname;
strcpy(this->mode,mode);
} }
public: public:
@ -255,14 +251,7 @@ public:
bool is_open() { return fp != NULL; } bool is_open() { return fp != NULL; }
virtual void truncate(s32 length) virtual void truncate(s32 length);
{
#ifdef _MSC_VER
_chsize(_fileno(fp),length);
#else
ftruncate(fileno(fp),length);
#endif
}
virtual int fprintf(const char *format, ...) { virtual int fprintf(const char *format, ...) {
va_list argptr; va_list argptr;