diff --git a/desmume/src/addons/compactFlash.cpp b/desmume/src/addons/compactFlash.cpp index 476424171..40b7a9ade 100644 --- a/desmume/src/addons/compactFlash.cpp +++ b/desmume/src/addons/compactFlash.cpp @@ -327,6 +327,9 @@ void build_ListCallback(FsEntry* fs, EListCallbackArg arg) { 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) { currFatFile = fatStack.top(); @@ -384,15 +387,22 @@ static BOOL cflash_build_fat() currPath = sFlashPath; 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. //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; - file = new EMUFILE_MEMORY(dataSectors*512+1); + file = new EMUFILE_MEMORY(dataSectors*512); + //file = new EMUFILE_FILE("c:\\temp.ima","rb+"); EmuFat fat(file); EmuFatVolume vol; u8 ok = vol.init(&fat); - vol.format(dataSectors); + vol.formatNew(dataSectors); reconstruct(&currFatFile); currFatFile.openRoot(&vol); @@ -772,11 +782,6 @@ static unsigned int cflash_read(unsigned int address) { unsigned int ret_value = 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) { diff --git a/desmume/src/emufat.cpp b/desmume/src/emufat.cpp index 7af09d68c..78fdeba1c 100644 --- a/desmume/src/emufat.cpp +++ b/desmume/src/emufat.cpp @@ -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 . +*/ + //based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ ) -/* - * Copyright (C) 2009 by William Greiman - * - * 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 Arduino SdFat Library. If not, see - * . - */ +//Copyright (C) 2009 by William Greiman + +//based on mkdosfs - utility to create FAT/MS-DOS filesystems +//Copyright (C) 1991 Linus Torvalds +//Copyright (C) 1992-1993 Remy Card +//Copyright (C) 1993-1994 David Hudson +//Copyright (C) 1998 H. Peter Anvin +//Copyright (C) 1998-2005 Roman Hodek #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, - 0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x20, 0x20, 0x50, - 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, - 0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6C, 0x6F, 0x70, 0x70, 0x79, 0x20, 0x61, - 0x6E, 0x64, 0x0D, 0x0A, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, - 0x79, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6E, 0x20, 0x2E, - 0x2E, 0x2E, 0x20, 0x0D, 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, + 0x0E, 0x1F, 0xBE, 0x77, 0x7C, 0xAC, 0x22, 0xC0, 0x74, 0x0B, 0x56, 0xB4, 0x0E, 0xBB, 0x07, 0x00, + 0xCD, 0x10, 0x5E, 0xEB, 0xF0, 0x32, 0xE4, 0xCD, 0x16, 0xCD, 0x19, 0xEB, 0xFE, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x61, + 0x62, 0x6C, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x20, 0x20, 0x50, 0x6C, 0x65, 0x61, 0x73, + 0x65, 0x20, 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x61, + 0x62, 0x6C, 0x65, 0x20, 0x66, 0x6C, 0x6F, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6E, 0x64, 0x0D, 0x0A, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x74, 0x6F, + 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6E, 0x20, 0x2E, 0x2E, 0x2E, 0x20, 0x0D, + 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, @@ -80,7 +118,6 @@ EmuFat::~EmuFat() delete m_pFile; } - u8 EmuFat::cacheRawBlock(u32 blockNumber, u8 action) { if (cache_.cacheBlockNumber_ != blockNumber) { @@ -105,6 +142,11 @@ u8 EmuFat::cacheZeroBlock(u32 blockNumber) return true; } +void EmuFat::cacheReset() +{ + reconstruct(&cache_); +} + u8 EmuFat::cacheFlush() { if (cache_.cacheDirty_) { 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;ireservedSectorCount;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;ifatCount;i++) + for(int j=0;jwriteBlock(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;iwriteBlock(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. //it would be nice if someone who understood fat better could modify the root //directory setup to use reasonable code instead of magic arrays bool EmuFatVolume::format(u32 sectors) { - u32 volumeStartBlock = 0; - dev_->truncate(0); - dev_->truncate(sectors*512); - if (!dev_->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_WRITE)) return false; - memset(&dev_->cache_.cacheBuffer_,0,sizeof(dev_->cache_.cacheBuffer_)); - TFat32BootSector* bs = &dev_->cache_.cacheBuffer_.fbs; - TBiosParmBlock* bpb = &bs->bpb; + //u32 volumeStartBlock = 0; + //dev_->truncate(0); + //dev_->truncate(sectors*512); + //if (!dev_->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_WRITE)) return false; + //memset(&dev_->cache_.cacheBuffer_,0,sizeof(dev_->cache_.cacheBuffer_)); + //TFat32BootSector* bs = &dev_->cache_.cacheBuffer_.fbs; + //TBiosParmBlock* bpb = &bs->bpb; - bs->jmpToBootCode[0] = 0xEB; - bs->jmpToBootCode[1] = 0x3C; - bs->jmpToBootCode[2] = 0x90; - memcpy(bs->oemName,"mkdosfs",8); - bs->driveNumber = 0; - bs->reserved1 = 0; - bs->bootSignature = 0x29; - bs->volumeSerialNumber = 0; - memcpy(bs->volumeLabel," ",11); - memcpy(bs->fileSystemType,"FAT16 ",8); - memcpy(bs->bootCode,mkdosfs_bootcode,420); - bs->bootSectorSig0 = 0x55; - bs->bootSectorSig1 = 0xAA; + //bs->jmpToBootCode[0] = 0xEB; + //bs->jmpToBootCode[1] = 0x3C; + //bs->jmpToBootCode[2] = 0x90; + //memcpy(bs->oemName,"mkdosfs",8); + //bs->driveNumber = 0; + //bs->reserved1 = 0; + //bs->bootSignature = 0x29; + //bs->volumeSerialNumber = 0; + //memcpy(bs->volumeLabel," ",11); + //memcpy(bs->fileSystemType,"FAT16 ",8); + //memcpy(bs->bootCode,mkdosfs_bootcode,420); + //bs->bootSectorSig0 = 0x55; + //bs->bootSectorSig1 = 0xAA; - bpb->bytesPerSector = 512; - bpb->sectorsPerCluster = 4; - bpb->reservedSectorCount = 1; - bpb->fatCount = 2; - bpb->rootDirEntryCount = 512; - bpb->totalSectors16 = 0; - bpb->mediaType = 0xF8; - bpb->sectorsPerFat16 = 32; - bpb->sectorsPerTrack = 32; - bpb->headCount = 64; - bpb->hiddenSectors = 0; - bpb->totalSectors32 = sectors; - bpb->fat32Flags = 0xbe0d; - bpb->fat32Version = 0x20Fd; - bpb->fat32RootCluster = 0x20202020; - bpb->fat32FSInfo = 0x2020; - bpb->fat32BackBootBlock = 0x2020; + //bpb->bytesPerSector = 512; + //bpb->sectorsPerCluster = 4; + //bpb->reservedSectorCount = 1; + //bpb->fatCount = 2; + //bpb->rootDirEntryCount = 512; + //bpb->totalSectors16 = 0; + //bpb->mediaType = 0xF8; + //bpb->sectorsPerFat16 = 32; + //bpb->sectorsPerTrack = 32; + //bpb->headCount = 64; + //bpb->hiddenSectors = 0; + //bpb->totalSectors32 = sectors; + //bpb->fat32Flags = 0xbe0d; + //bpb->fat32Version = 0x20Fd; + //bpb->fat32RootCluster = 0x20202020; + //bpb->fat32FSInfo = 0x2020; + //bpb->fat32BackBootBlock = 0x2020; - if(!dev_->cacheFlush()) - return false; + //if(!dev_->cacheFlush()) + // 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] = - { - 0xF8, 0xFF, 0xFF, 0xFF, - } ; + //static const u8 rootEntry[8] = + //{ + // 0xF8, 0xFF, 0xFF, 0xFF, + //} ; - memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4); + //memcpy(dev_->cache_.cacheBuffer_.data,rootEntry,4); - if(!dev_->cacheFlush()) - return false; + //if(!dev_->cacheFlush()) + // 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()) - return false; + //if(!dev_->cacheFlush()) + // return false; - return init(dev_,0); + //return init(dev_,0); + + return false; } bool EmuFatVolume::init(EmuFat* dev, u8 part) { @@ -253,16 +658,16 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) { volumeStartBlock = p->firstSector; } if (!dev->cacheRawBlock(volumeStartBlock, EmuFat::CACHE_FOR_READ)) return false; - TBiosParmBlock* bpb = &dev->cache_.cacheBuffer_.fbs.bpb; - if (bpb->bytesPerSector != 512 || - bpb->fatCount == 0 || - bpb->reservedSectorCount == 0 || - bpb->sectorsPerCluster == 0) { + TFat32BootSector* bs = &dev->cache_.cacheBuffer_.fbs; + if (bs->bytesPerSector != 512 || + bs->fatCount == 0 || + bs->reservedSectorCount == 0 || + bs->sectorsPerCluster == 0) { // not valid FAT volume return false; } - fatCount_ = bpb->fatCount; - blocksPerCluster_ = bpb->sectorsPerCluster; + fatCount_ = bs->fatCount; + blocksPerCluster_ = bs->sectorsPerCluster; // determine shift that is same as multiply by blocksPerCluster_ clusterSizeShift_ = 0; @@ -270,23 +675,23 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) { // error if not power of 2 if (clusterSizeShift_++ > 7) return false; } - blocksPerFat_ = bpb->sectorsPerFat16 ? - bpb->sectorsPerFat16 : bpb->sectorsPerFat32; + blocksPerFat_ = bs->sectorsPerFat16 ? + bs->sectorsPerFat16 : bs->fat32.sectorsPerFat32; - fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; + fatStartBlock_ = volumeStartBlock + bs->reservedSectorCount; // count for FAT16 zero for FAT32 - rootDirEntryCount_ = bpb->rootDirEntryCount; + rootDirEntryCount_ = bs->rootDirEntryCount; // directory start for FAT16 dataStart for FAT32 - rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; + rootDirStart_ = fatStartBlock_ + bs->fatCount * blocksPerFat_; // 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 - u32 totalBlocks = bpb->totalSectors16 ? - bpb->totalSectors16 : bpb->totalSectors32; + u32 totalBlocks = bs->totalSectors16 ? + bs->totalSectors16 : bs->totalSectors32; // total data blocks clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); @@ -299,7 +704,7 @@ bool EmuFatVolume::init(EmuFat* dev, u8 part) { } else if (clusterCount_ < 65525) { fatType_ = 16; } else { - rootDirStart_ = bpb->fat32RootCluster; + rootDirStart_ = bs->fat32.fat32RootCluster; fatType_ = 32; } return true; diff --git a/desmume/src/emufat.h b/desmume/src/emufat.h index 2745d6ef7..6ecdc8b61 100644 --- a/desmume/src/emufat.h +++ b/desmume/src/emufat.h @@ -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 . +*/ + //based on Arduino SdFat Library ( http://code.google.com/p/sdfatlib/ ) -/* - * Copyright (C) 2009 by William Greiman - * - * 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 Arduino SdFat Library. If not, see - * . - */ +//Copyright (C) 2009 by William Greiman + +//based on mkdosfs - utility to create FAT/MS-DOS filesystems +//Copyright (C) 1991 Linus Torvalds +//Copyright (C) 1992-1993 Remy Card +//Copyright (C) 1993-1994 David Hudson +//Copyright (C) 1998 H. Peter Anvin +//Copyright (C) 1998-2005 Roman Hodek + #ifndef EMUFAT_H #define EMUFAT_H @@ -25,6 +32,10 @@ #include "emufile.h" #include +#define BOOTCODE_SIZE 448 +#define BOOTCODE_FAT32_SIZE 420 + + // use the gnu style oflag in open() /** open() oflag for reading */ static const u8 EO_READ = 0X01; @@ -129,16 +140,29 @@ struct __PACKED TMasterBootRecord { u8 mbrSig1; }; -//BIOS parameter block -//The BIOS parameter block describes the physical layout of a FAT volume. -struct __PACKED TBiosParmBlock { +struct __PACKED msdos_volume_info { + u8 drive_number; /* BIOS drive number */ + 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 //following values: 512, 1024, 2048 or 4096 u16 bytesPerSector; //Number of sectors per allocation unit. This value must be a //power of 2 that is greater than 0. The legal values are //1, 2, 4, 8, 16, 32, 64, and 128. - u8 sectorsPerCluster; + u8 sectorsPerCluster; //cluster_size //Number of sectors before the first FAT. //This value must not be zero. u16 reservedSectorCount; @@ -151,7 +175,7 @@ struct __PACKED TBiosParmBlock { //value should always specify a count that when multiplied by 32 //results in a multiple of bytesPerSector. FAT16 volumes should //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 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 @@ -182,61 +206,56 @@ struct __PACKED TBiosParmBlock { //of the volume. This field can be 0; if it is 0, then //totalSectors16 must be non-zero. 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. -struct __PACKED TFat32BootSector { - //X86 jmp to boot program - u8 jmpToBootCode[3]; - //informational only - don't depend on it - u8 oemName[8]; - //BIOS Parameter Block - TBiosParmBlock bpb; - //for int0x13 use value 0X80 for hard drive - u8 driveNumber; - //used by Windows NT - should be zero for FAT - u8 reserved1; - //0X29 if next three fields are valid - u8 bootSignature; - //usually generated by combining date and time - u32 volumeSerialNumber; - //should match volume label in root dir - u8 volumeLabel[11]; - //informational only - don't depend on it - u8 fileSystemType[8]; - //X86 boot code - u8 bootCode[420]; - //must be 0X55 - u8 bootSectorSig0; - //must be 0XAA - u8 bootSectorSig1; + union { + struct __PACKED { + msdos_volume_info vi; + u8 boot_code[BOOTCODE_SIZE]; + } oldfat; + + struct __PACKED + { + //Count of sectors occupied by one FAT on FAT32 volumes. + u32 sectorsPerFat32; //fat32_length; /* sectors/FAT */ + + //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;// flags; /* bit 8: fat mirroring, low 4: active fat */ + + //FAT32 version. High byte is major revision number. + //Low byte is minor revision number. Only 0.0 define. + u16 fat32Version;//version[2]; /* major, minor filesystem version */ + + //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" @@ -586,6 +605,7 @@ public: bool init(EmuFat* dev, u8 part); bool format(u32 sectors); + bool formatNew(u32 sectors); // inline functions that return volume info //The volume's cluster size in blocks. @@ -687,6 +707,7 @@ private: void cacheSetDirty() {cache_.cacheDirty_ |= CACHE_FOR_WRITE;} u8 cacheZeroBlock(u32 blockNumber); u8 cacheFlush(); + void cacheReset(); //IO functions: u8 readBlock(u32 block, u8* dst); diff --git a/desmume/src/emufile.cpp b/desmume/src/emufile.cpp index 7a05ba999..1b2206895 100644 --- a/desmume/src/emufile.cpp +++ b/desmume/src/emufile.cpp @@ -24,4 +24,42 @@ EMUFILE* EMUFILE::memwrap(EMUFILE* fp) file->fread(mem->buf(),file->size()); delete file; return mem; +} + +size_t EMUFILE_MEMORY::_fread(const void *ptr, size_t bytes){ + u32 remain = len-pos; + u32 todo = std::min(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(remain,(u32)bytes); - memcpy((void*)ptr,buf()+pos,todo); - pos += todo; - if(todofname = fname; + strcpy(this->mode,mode); } public: @@ -255,14 +251,7 @@ public: bool is_open() { return fp != NULL; } - virtual void truncate(s32 length) - { - #ifdef _MSC_VER - _chsize(_fileno(fp),length); - #else - ftruncate(fileno(fp),length); - #endif - } + virtual void truncate(s32 length); virtual int fprintf(const char *format, ...) { va_list argptr;